2013-05-27 3 views
0
function generateRandomData(){ 
@ $db = new mysqli('localhost','XXX','XXX','scores'); 
if(mysqli_connect_errno()) { 
    echo 'Failed to connect to database. Please try again later.'; 
    exit; 
} 
$query = "insert into scoretable values(?,?,?)"; 
for($a = 0; $a < 1000000; $a++) 
{ 
$stmt = $db->prepare($query); 

$id = rand(1,75000); 

$score = rand(1,100000); 

$time = rand(1367038800 ,1369630800); 
$stmt->bind_param("iii",$id,$score,$time); 
$stmt->execute(); 
} 

} 

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

+0

Я предпочел бы, чтобы подготовить запрос полностью, а затем выполнить его один раз. Это эффективно сократит время. –

+0

см. Здесь: http://stackoverflow.com/a/3711277/1755720 –

+0

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

ответ

1

Как указано в комментариях, вам необходимо уменьшить количество запросов, собирая как можно большее количество вставок.В PHP, легко добиться того, что:

$query = "insert into scoretable values"; 
for($a = 0; $a < 1000000; $a++) { 
    $id = rand(1,75000); 
    $score = rand(1,100000); 
    $time = rand(1367038800 ,1369630800); 
    $query .= "($id, $score, $time),"; 
} 
$query[strlen($query)-1]= ' '; 

Существует ограничение на максимальный размер запросов вы можете выполнять, которая непосредственно связана с настройкой на max_allowed_packet сервера (This page документации тузд описывает, как настроить это настройка в ваших интересах).

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

Другая практика, чтобы отключить проверочные ограничения таблицы, которую вы хотите сделать массовой вставки:

ALTER TABLE yourtablename DISABLE KEYS; 
SET FOREIGN_KEY_CHECKS=0; 
-- bulk insert comes here 
SET FOREIGN_KEY_CHECKS=1; 
ALTER TABLE yourtablename ENABLE KEYS; 

Эта практика, однако, необходимо тщательно, особенно в вашем случае, так как вы генерировать значения случайным образом. Если у вас есть уникальный ключ в столбцах, которые вы генерируете, вы не можете использовать эту технику с вашим запросом в том виде, в каком он есть, поскольку он может генерировать дублируемую вставку ключа. Вы, вероятно, хотите, чтобы добавить пункт IGNORE к нему:

$query = "insert INGORE into scoretable values"; 

Это заставит сервер молча игнорировать дубликаты записей об уникальных ключей. Чтобы достичь общего количества требуемых вставок, просто зациклируйте столько времени, сколько необходимо, чтобы заполнить оставшиеся недостающие строки.

Я полагаю, что единственное место, где у вас может быть уникальное ограничение ключа, находится на столбце id. В этом случае вы никогда не сможете достичь количества строк, которое вы хотите иметь, поскольку оно находится выше диапазона случайных значений, которые вы генерируете для этого поля. Подумайте о том, чтобы повысить этот лимит или, еще лучше, генерировать ваши идентификаторы по-разному (возможно, просто используя счетчик, который гарантирует, что каждая запись использует другой ключ).

1

Вы делаете несколько вещей неправильно. Первое, что вы должны учитывать, - это то, что вы используете MySQL.

По умолчанию используется InnoDB, ранее по умолчанию использовался MyISAM.

Я напишу этот ответ в предположении, что вы используете InnoDB, который вы должны использовать по множеству причин. InnoDB работает во встроенном режиме автосохранения. Это означает, что каждый сделанный вами запрос заключен в транзакцию. Чтобы перевести это на язык, который мы можем понять простым смертным - каждый запрос, который вы делаете без с указанием BEGIN WORK; блок - транзакция - ergo, MySQL будет ждать, пока жесткий диск не подтвердит, что данные были написаны.

Зная, что жесткие диски медленные (механические все еще наиболее широко используются), это означает, что ваши вставки будут такими же быстрыми, как на жестком диске. Обычно механические жесткие диски могут выполнять около 300 операций ввода вывода в секунду, считая, что вы можете делать 300 вставок в секунду - да, вы будете ждать совсем немного, чтобы вставить 1 миллион записей.

Итак, зная, как все работает - вы можете использовать их в своих интересах.

Объем данных, которые HDD записывает за транзакцию, будет в целом очень маленьким (4 КБ или даже меньше), и знание современных жестких дисков может записывать более 100 МБ/с - это означает, что мы должны обернуть несколько запросов в одну транзакцию ,

Таким образом, MySQL отправит довольно много данных и дождитесь, пока HDD подтвердит, что он написал все и что весь мир в порядке и денди.

Итак, если у вас есть 1M строк, которые вы хотите заполнить, вы выполните 1M запросов. Если ваши транзакции совершают 1000 запросов за раз, вы должны выполнить только около 1000 операций записи.

Таким образом, ваш код становится что-то вроде этого:

(я не знаком с Mysqli интерфейсом так имена функций могут быть ошибочными, и, видя, что я печатаю без фактического выполнения кода - пример, возможно, не работают, поэтому используйте его на свой страх и риск)

function generateRandomData() 
{ 
    $db = new mysqli('localhost','XXX','XXX','scores'); 

    if(mysqli_connect_errno()) { 
     echo 'Failed to connect to database. Please try again later.'; 
     exit; 
    } 

    $query = "insert into scoretable values(?,?,?)"; 

    // We prepare ONCE, that's the point of prepared statements 
    $stmt = $db->prepare($query); 

    $start = 0; 
    $top = 1000000; 

    for($a = $start; $a < $top; $a++) 
    { 
     // If this is the very first iteration, start the transaction 
     if($a == 0) 
     { 
      $db->begin_transaction(); 
     } 
     $id = rand(1,75000); 

     $score = rand(1,100000); 

     $time = rand(1367038800 ,1369630800); 

     $stmt->bind_param("iii",$id,$score,$time); 
     $stmt->execute(); 


     // Commit on every thousandth query 
     if(($a % 1000) == 0 && $a != ($top - 1)) 
     { 
      $db->commit(); 

      $db->begin_transaction(); 
     } 

     // If this is the very last query, then we just need to commit and end 
     if($a == ($top - 1)) 
     { 
      $db->commit(); 
     } 
    } 
} 
1

Запрос БД включает в себя множество взаимосвязанных задач. В результате это «дорогой» процесс. Это еще более «дорого», когда речь идет о вставке/обновлении.

Выполнение запроса один раз - лучший способ повысить производительность.

Вы можете подготовить операторы в цикле и запустить его один раз.

например.

$query = "insert into scoretable values "; 
for($a = 0; $a < 1000000; $a++) 
{ 
$values = " ('".$?."','".$?."','".$?."'), "; 
$query.=$values; 
... 


} 
... 
//remove the last comma 
... 
$stmt = $db->prepare($query); 
... 
$stmt->execute(); 
0

Посмотрите на this gist Я создал. Вставить миллион строк на моем ноутбуке занимает около 5 минут.

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