我需要一些帮助才能改进我目前的代码。我有一个巨大的阵列(里面有大约20,000个对象)。该数组如下所示:
Array
(
[0] => Player Object
(
[name] => Aaron Flash
[level] => 16
[vocation] => Knight
[world] => Amera
[time] => 900000
[online] => 1
)
[1] => Player Object
(
[name] => Abdala da Celulose
[level] => 135
[vocation] => Master Sorcerer
[world] => Amera
[time] => 900000
[online] => 1
)
[2] => Player Object
(
[name] => Ahmudi Segarant
[level] => 87
[vocation] => Elite Knight
[world] => Amera
[time] => 900000
[online] => 1
)
[3] => Player Object
(
[name] => Alaskyano
[level] => 200
[vocation] => Royal Paladin
[world] => Amera
[time] => 900000
[online] => 1
)
[4] => Player Object
(
[name] => Aleechoito
[level] => 22
[vocation] => Knight
[world] => Amera
[time] => 900000
[online] => 1
)
等等......总共约有20,000个玩家对象。
现在我想将它们全部插入到我的数据库中。我想找到一种不循环所有玩家的方法。它导致了很多性能问题,而且几乎导致我的计算机死机。我想一次性在一个查询中完成它。
但是如何获得Player Object属性,比如每个单独对象的“name”,“level”和“vocation”而不循环它们?
这就是我的代码:
// Insert player list to database
$sql = $db->prepare("INSERT INTO players (name, level, vocation, world, month, today, online) VALUES (:name, :level, :vocation, :world, :time, :time, :online) ON DUPLICATE KEY UPDATE level = :level, vocation = :vocation, world = :world, month = month + :time, today = today + :time, online = :online");
foreach ($players as $player) {
$query = $sql->execute([
":name" => $player->name,
":level" => $player->level,
":vocation" => $player->vocation,
":world" => $player->world,
":time" => $player->time,
":online" => $player->online
]);
}
因为现在在底部的那个foreach上,它循环遍历我的数组中的20,000个玩家对象,并获得他们的名字/等级/职业/世界等等。
有更好的方法吗? 我这样做的方式不是最好的解决方案。我可以听到我的电脑工作超载,感觉好像要崩溃了。
答案 0 :(得分:5)
虽然我仍然怀疑交易和/或批量插入是解决资源使用问题的可行方案,但它们仍然比准备Dave建议的大量语句更好。
给他们一个镜头,看看他们是否有帮助。
以下假设PDO的错误处理模式设置为抛出异常。例如:$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
如果出于某种原因,您无法使用异常模式,那么您每次都需要检查execute()
的返回并抛出您自己的异常。
单笔交易:
$sql = $db->prepare("INSERT INTO players (name, level, vocation, world, month, today, online) VALUES (:name, :level, :vocation, :world, :time, :time, :online) ON DUPLICATE KEY UPDATE level = :level, vocation = :vocation, world = :world, month = month + :time, today = today + :time, online = :online");
$db->beginTransaction();
try {
foreach ($players as $player) {
$sql->execute([
":name" => $player->name,
":level" => $player->level,
":vocation" => $player->vocation,
":world" => $player->world,
":time" => $player->time,
":online" => $player->online
]);
}
$db->commit();
} catch( PDOException $e ) {
$db->rollBack();
// at this point you would want to implement some sort of error handling
// or potentially re-throw the exception to be handled at a higher layer
}
批量交易:
$batch_size = 1000;
for( $i=0,$c=count($players); $i<$c; $i+=$batch_size ) {
$db->beginTransaction();
try {
for( $k=$i; $k<$c && $k<$i+$batch_size; $k++ ) {
$player = $players[$k];
$sql->execute([
":name" => $player->name,
":level" => $player->level,
":vocation" => $player->vocation,
":world" => $player->world,
":time" => $player->time,
":online" => $player->online
]);
}
} catch( PDOException $e ) {
$db->rollBack();
// at this point you would want to implement some sort of error handling
// or potentially re-throw the exception to be handled at a higher layer
break;
}
$db->commit();
}
答案 1 :(得分:-1)
我认为您将获得的最大性能提升是不对每个插入执行一次查询,而是对所有插入执行单个查询。类似的东西:
$sql = "INSERT INTO players (name, level, vocation, world, month, today, online) VALUES ";
$inserts = [];
$values = [];
$idx = 0;
foreach ($players as $player) {
$idx++;
$inserts[] = "(:name{$idx}, :level{$idx}, :vocation{$idx}, :world{$idx}, :month{$idx}, :today{$idx}, :online{$idx})";
$values[":name{$idx}"] = $player->name;
$values[":level{$idx}"] = $player->level;
$values[":vocation{$idx}"] = $player->vocation;
$values[":world{$idx}"] = $player->world;
$values[":month{$idx}"] = $player->time;
$values[":today{$idx}"] = $player->time;
$values[":online{$idx}"] = $player->online;
}
$sql .= implode(",", $inserts);
$sql .= " ON DUPLICATE KEY UPDATE level = VALUES(level), vocation = VALUES(vocation), world = VALUES(world), month = month + VALUES(time), today = today + VALUES(time), online = VALUES(online)";
$query = $db->prepare($sql)->execute($values);