Проблемы с производительностью PHP mySQL для большого количества операторов вставки, стоит ли использовать что-то еще? - PullRequest
1 голос
/ 21 марта 2011

У меня есть скрипт, который составляет список элементов, который может быть очень длинным - просто составление этого списка может занять 10-15 минут, но это приемлемо. Когда я включаю функцию, которая перебирает список и вставляет их все в таблицу mySQL, это время увеличивается примерно на 50%. Мне было интересно, есть ли более быстрый способ сериализации этих данных? Должен ли я изучить CSV или что-то еще? Или я могу оптимизировать свой код, чтобы сделать это быстрее:

    private function toDB(){
    $sql[] = "DROP TABLE IF EXISTS checklisttest";$sql[] = "CREATE TABLE checklisttest (
  Incident varchar(12) NOT NULL,
  TestID mediumint(9) NOT NULL AUTO_INCREMENT,
  Element varchar(12) NOT NULL,
  Name varchar(128) NOT NULL,
  Code varchar(512) NOT NULL,
  Expected varchar(512) NOT NULL,
  Actual varchar(512) NOT NULL,
  AutoVerifyResult varchar(32) NOT NULL,
  QAResult varchar(32) DEFAULT NULL,
  Comments text,
  PRIMARY KEY (TestID)
)";

    //iterate through the records $this->records[10001] -- There can be anywhere from 100 - 300 records
    foreach($this->records as $inc => $record){
        //iterate through the element ids $this->records[10001][E02_04]
        foreach($this->records[$inc]["Elements"] as $elementID => $element){
            //iterate through the element ids $this->records[10001][E02_04][1] --There can be anywhere from 150 - 350 elements per record.
            foreach($element as $key => $val){
                $sql[] = "
INSERT INTO `checklistTest` VALUES (\"$inc\",NULL,\"$elementID\",\"$val[name]\",\"$val[code]\",\"$val[expected]\",\"$val[actual]\",\"$val[match]\",\"$val[QAResult]\",NULL)";
            }
        }
    }
    foreach($sql as $key => $val){
        mysql_select_db("new",$GLOBALS['local']);
        mysql_query($val,$GLOBALS['local']) or die(mysql_error());
    }
    //echo "<textarea style='width:100%;height:400px'>$sql</textarea>";
    //mysql_select_db("new",$GLOBALS['local']);
    //mysql_query($sql,$GLOBALS['local']) or die(mysql_error());
}

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

спасибо за ответы, я отправил свое решение в комментарии к принятому ответу.

Ответы [ 2 ]

3 голосов
/ 21 марта 2011

Существует целый ряд факторов, влияющих на производительность, включая серверное оборудование, среднюю нагрузку, настройки MySQL, использование памяти и т. Д. Я собираюсь слепо предположить, что у вас узкое место ввода-вывода и что MySQL правильно настроен для нагрузки, которую вы на него ставите.

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

$pdo->beginTransaction();
$statement = $pdo->prepare('
    INSERT INTO checklistTest
           VALUES(?, NULL, ?, ?, ?, ?, ?, ?, ?, NULL)
');
foreach($this->records as $inc => $record){
    foreach($this->records[$inc]["Elements"] as $elementID => $element){
        foreach($element as $key => $val) {
            $statement->execute(array(
                $inc,
                $elementID,
                $val['name'],
                $val['code'],
                $val['expected'],
                $val['actual'],
                $val['match'],
                $val['QAResult']
            ));
        }
    }
}
$pdo->commit();

Итак, что здесь происходит?Сначала мы начинаем транзакцию .Мы сообщаем базе данных, что собираемся выполнить кучу работы, и мы либо хотим, чтобы все было сделано, либо ничего не сделали.

Во-вторых, мы готовим оператор SQL.Видите эти знаки вопроса?Они называются заполнителями.Позже мы сообщим базе данных заполнить конкретные данные для каждого заполнителя.Также обратите внимание, что нет кавычек.Они в основном добавляются автоматически при заполнении заполнителей.

Внутри цикла мы говорим о выполнении инструкции, и мы используем PDO execute метод для передачимассив значений для заполнителей.Некоторые люди предпочитают делать эту одну переменную за раз, используя bindParam, но я предпочитаю метод массива.

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

Наконец, когда цикл закончен, мы сообщаем базе данных:совершить работу, которую мы только что сделали.Как я уже упоминал в комментариях, именно здесь возможно значительное повышение производительности.База данных будет фактически постоянно записывать изменения на диск при выполнении фиксации.Это означает, что обычные задачи бухгалтерского учета могут ждать, пока не произойдет фиксация, вместо того, чтобы выполнять их при каждой вставке.Таким образом, большая часть ввода-вывода, которая вам нужна, не должна происходить вживую, когда вы запускаете вставку.

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

CREATE TABLE checklistTest (
   ... // No changes inside
) ENGINE=InnoDB

Единственное отличие - в конце, после закрытой пары.Мы просим MySQL использовать механизм хранения InnoDB вместо того, чтобы использовать сервер по умолчанию.Это гарантирует, что мы получим таблицу, которая поддерживает транзакции.

Теперь я понимаю, что просить вас изменить адаптеры базы данных может быть немного глупо, но это не без причины.Хотя вы можете выполнять транзакции с использованием интерфейса oldschool mysql (самостоятельно вводя команды START TRANSACTION и COMMIT / ROLLBACK), вы не можете использовать с ним подготовленные операторы.Хотя это само по себе не является нарушителем правил, процесс prepare-bind-execute - это процесс, за которым следует каждый современный адаптер базы данных PHP .Старый интерфейс mysql - это , а не современный адаптер базы данных PHP, и вам действительно следует серьезно подумать о переходе на PDO или mysqli.


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

1 голос
/ 21 марта 2011

Вставка нескольких операторов вставки, вы можете объединить их в один запрос, как показано ниже -

//iterate through the records $this->records[10001] -- There can be anywhere from 100 - 300 records

$ sql = "INSERT INTO checklistTest VALUES";

    foreach($this->records as $inc => $record){
        //iterate through the element ids $this->records[10001][E02_04]

        foreach($this->records[$inc]["Elements"] as $elementID => $element){

            //iterate through the element ids $this->records[10001][E02_04][1]--There can be anywhere from 150 - 350 elements per record.
            foreach($element as $key => $val){
                $sql.= "(\"$inc\",NULL,\"$elementID\",\"$val[name]\",\"$val[code]\",\"$val[expected]\",\"$val[actual]\",\"$val[match]\",\"$val[QAResult]\",NULL),";
            }
        }
    }

Примечание. Теперь удалите последнюю запятую. т.е. к последнему значению в массиве в конце будет добавлена ​​запятая, удалите, иначе вы получите ошибку базы данных.

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

mysql_select_db ( "новый", $ GLOBALS [ 'местный']);

mysql_query ($ sql, $ GLOBALS ['local']) или die (mysql_error ());

//echo "<textarea style='width:100%;height:400px'>$sql</textarea>";
//mysql_select_db("new",$GLOBALS['local']);
//mysql_query($sql,$GLOBALS['local']) or die(mysql_error());

}

Вы можете обратиться по ссылкам ниже для подробной документации этого метода -

http://www.brainbell.com/tutorials/MySQL/Inserting_Multiple_Rows.htm

http://dev.mysql.com/doc/refman/5.5/en/insert.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...