оптимизация для массовой вставки sql - PullRequest
2 голосов
/ 22 июня 2011

Я должен вставить запись 100k + в БД, и у меня есть некоторые проблемы с памятью.$ _data - это массив, содержащий массивы данных.Я даже увеличил объем памяти, но все равно столкнулся с проблемами

// VERSION 1
protected function save() {

    $memory_limit = ini_get('memory_limit');
    ini_set('memory_limit', '512M');

    $sql = "
        INSERT INTO table (
            c1,
            c2,
            c3,
            c4,
            c5,
            c6,
            c7,
            c7,
            c9,
            c10,
            c11             
        ) VALUES (?,?,?,?,?,?,?,?,?)
        ON DUPLICATE KEY UPDATE 
            c10 = VALUES(c10),
            c11 = VALUES(c10),
            c12 = VALUES(c12)
    ";
    $db = Zend_Registry::get('db');
    $stmt = new Zend_Db_Statement_Pdo($db, $sql);
    foreach($this->_data as $entry){
        $stmt->execute($entry);
    }
    unset($this->_data, $stmt, $sql);
    ini_set('memory_limit', $memory_limit);

Второй пытается вставить все записи в мульти-вставку, но не лучше.

// VERSION 2
protected function save2(){
    $question_marks = str_repeat('?,', count($this->_data[0]));
    $question_marks = trim($question_marks, ',');
    $question_marks = str_repeat("($question_marks),", count($this->_data));
    $question_marks = trim($question_marks, ',');
    $sql = "
        INSERT INTO table (
            c1,
            c2,
            c3,
            c4,
            c5,
            c6,
            c7,
            c7,
            c9,
            c10,
            c11             
        ) VALUES $question_marks
        ON DUPLICATE KEY UPDATE 
            c10 = VALUES(c10),
            c11 = VALUES(c11),
            c12 = VALUES(c12)
        ;";
    $db = Zend_Registry::get('db');
    $stmt = new Zend_Db_Statement_Pdo($db, $sql);
    $insert_values = call_user_func_array('array_merge', $this->_data);
    $stmt->execute($insert_values);
    $affected_rows = $stmt->rowCount();
    if ($affected_rows){
        // @todo log    
    } 
    unset($this->_data);
    unset($stmt, $sql, $insert_values, $affected_rows, $question_marks);

Имена столбцов неоригинал.Любые предложения?


Я постараюсь разбить массив данных на 5 тыс. Записей и сделать вставки в пакетах.Также пытаюсь понять, как помогает изменение max_allowed_packet в mysql cnf.Между тем я был бы признателен за любые предложения.Спасибо

ОБНОВЛЕНИЕ

в моем случае изменение max_allowed_packet с 16M до 1024M помогло, и я сделал вставку без разделения массива.

Ответы [ 3 ]

2 голосов
/ 22 июня 2011

Откуда вы знаете, что у вас проблемы с памятью, а не тайм-ауты выполнения скрипта?

В любом случае, я бы попробовал с простым mysql_query

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

1 голос
/ 22 июня 2011

В дополнение к ответу @Tudor Constantin и другим предложениям:

Один из способов справиться с этим - работать с кусками данных, а не с большим массивом _data из 100k + строк (который, даже не говоря о запросе, является массивом, который потребляет много вашей доступной памяти).

Вам следует проверить Zend_Memory для хранения вашей структуры данных, содержащей все данные по 100К строк.Это позволит вам управлять виртуальными объектами, содержащими все строки, не имея их всех в реальной памяти PHP.И это, возможно, позволит вам избежать асинхронных пакетов.

Затем каждый раз выполняйте запрос вставки с ограниченным количеством строк (чтобы строка запроса не становилась слишком большой).Могут быть использованы ваши 2 примера кода, и, как сказано в другом ответе, прямой доступ к mysql_query может предотвратить утечки памяти из Zend_Db или PDO.

Если вы не используете mysql_query, убедитесь, что вы не используетеактивировать Zend_Db_Profiler для объекта Db.Если вы все еще видите утечки памяти после каждого запроса, вы можете попробовать Zend_Debug объекта Db и попытаться выяснить, не хранятся ли где-нибудь некоторые исторические данные (например, список прошлых запросов).Затем проверьте объект оператор на то же самое. Вызовы функций memory_get_usage также могут помочь в устранении утечек.

Я бы попытался unset($entry); в конце цикла foreach.а также.

0 голосов
/ 22 июня 2011

Я думаю, что ваша проблема в размере массива, а не в том, что связано с БД. Вы должны разделить массив данных $ this -> _ IMO.

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