Мне нужна помощь для улучшения моего текущего кода. У меня огромный массив (около 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 )
И так далее … всего около 20000 объектов.
Теперь я хочу вставить их все в мою базу данных. Я хотел бы найти способ не перебирать всех игроков. Это вызывает множество проблем с производительностью, и это почти убивает мой компьютер. Я хотел бы сделать это в одном запросе сразу.
Но как я могу получить атрибуты Object Player, такие как «имя», «уровень» и «призвание» каждого отдельного объекта, не зацикливая их?
Вот как выглядит мой код:
// 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 игровых объектов в моем массиве и получает их имена / уровень / призвание / мир и так далее.
Есть лучший способ сделать это? Мой способ сделать это не может быть лучшим решением. Я слышу, что мой компьютер работает с перегрузкой, и кажется, что он вот-вот рухнет.
Хотя я все еще сомневаюсь, что транзакции и / или пакетные вставки являются жизнеспособным решением проблемы использования ресурсов, они по-прежнему являются лучшим решением, чем подготовка массивных заявлений, как предположил Дейв.
Дайте им выстрел и посмотрите, помогут ли они.
Следующее предполагает, что режим обработки ошибок PDO установлен для исключения исключений. Например: $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Если по какой-то причине вы не можете использовать режим 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(); }
Я думаю, что самый большой прирост производительности, который вы получите, – это не выполнение одного запроса для каждой вставки, а выполнение одного запроса для всех вставок. Что-то вроде:
$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);