2017-02-14 2 views
0

Мне нужна помощь в улучшении моего текущего кода. У меня огромный массив (около 20 000 объектов внутри него). Массив выглядит следующим образом:PDO - Вставка большого массива в базу данных MySQL

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, такие как «имя», «уровень» и «призвание» каждого отдельного объекта, не зацикливая их?

Это то, что мой код выглядит следующим образом:

// 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 
    ]); 
} 

Потому что сейчас на этом Еогеасп на дне, он зацикливается через 20000 объектов игроков в моем массиве, и получать их имена/уровень/призвание/мир и так далее.

Есть ли лучший способ сделать это? Мой способ сделать это не может быть лучшим решением. Я слышу, что мой компьютер работает с перегрузкой, и кажется, что он вот-вот рухнет.

+0

без зацикливания вы ничего не можете сделать здесь. –

+0

Итак, нет быстрого способа захватить имя $ player-> и т. Д., Не пропуская через них? Не могу ли я как-то сказать, что «: name» должно совпадать с «именем» внутри каждого объекта Player? Зацикливание вызывает много проблем с задержкой и производительностью. –

+0

Вы можете получить элементы с помощью синтаксиса '[]', но это вам не очень помогает. –

ответ

2

Хотя я до сих пор сомневаюсь, что сделки и/или рулонированные вставки являются жизнеспособным решением вашей проблемы использования ресурсов, они по-прежнему является лучшим решением, чем подготовка массовых заявлений, как Дэйв предложил.

Дайте им снимок и посмотрите, помогут ли они.

Следующее предполагает, что режим обработки ошибок 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(); 
} 
+0

Эй! Извините за задержку с ответом. Я только что протестировал его и HOLY SH * T !!!! Вы бог. Вы буквально Бог. Почему существует такая огромная разница в производительности при использовании «beginTransaction»? Он завершил все это за считанные секунды. 20 000 заняли всего 2 или 3 секунды. По сравнению с моим оригинальным кодом, который займет более 6 или 7 минут. Это безумно быстро и только доходило до 18% использования ресурсов. Мой код увеличился бы до 90-95%. Черт побери! Я должен взглянуть на руководство по транзакциям в PHP. Это взорвало мой разум!СПАСИБО ВАМ! –

+0

Я перешел к каналу mysql в IRC и спросил, помогут ли транзакции в таком случае, и, как я помню, с одной большой транзакцией mysql может потенциально отложить запись в файлы Tlog, index и data до конца сделки. Было упомянуто одно предупреждение: если база данных будет реплицирована, крупные транзакции могут ввести отставание в репликации так же велико, как время, затраченное на завершение транзакции. Я бы не стал классифицировать его как нечто, чтобы потерять сон, но что-то иметь в виду, тем не менее. – Sammitch

+0

Также учтите, что любая ошибка во время транзакции, которая запускает функцию rollback() 'при открытой транзакции, отменяет все изменения. Кроме того, изменения DDL [CREATE/ALTER/etc] вызовут неявное коммитирование в mysql. – Sammitch

-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); 
+0

Это только вставил последний объект Player массива. Он проигнорировал остальные. –

+0

Извините, не было '[]' после '$ values' в цикле, просто зафиксировано это – dave

+0

' Примечание: преобразование строки в строку D: \ xampp \ htdocs \ index.php в строке 75 'строка 75 - это' $ query = $ db-> prepare ($ sql) -> execute ($ values); ' –

Смежные вопросы