Как построить большой запрос MySQL INSERT на PHP без потери памяти - PullRequest
5 голосов
/ 17 октября 2008

У меня есть код, похожий на этот:

$data = file_get_contents($tempFile); // perhaps 30MB of file data, now in PHP's memory
$hash = md5($data);
$query = "INSERT INTO some_table
          SET BlobData = '" . mysql_real_escape_string($data) . "',
          BlobHash = '$hash'
          ";
mysql_query($query);

Я знаю, что это не очень эффективно, как каждый из '.' операторы перераспределят больший блок памяти, и строка размером 30 МБ будет скопирована несколько раз.

Есть ли что-нибудь более эффективное, чем следующее решение?

$data = file_get_contents($tempFile); // perhaps 30MB of file data, now in PHP's memory
$hash = md5($data);
$query = "INSERT INTO some_table SET BlobData = '%s', BlobHash = '$hash'";
mysql_query(sprintf($query, mysql_real_escape_string($data)));

Ответы [ 5 ]

7 голосов
/ 18 октября 2008

У вас есть две проблемы здесь:

# 1, есть несколько различных способов вычисления хеша MD5:

  • Делайте как вы, загружайте в PHP как строку и используйте PHP md5()
  • Использовать PHP md5_file()
  • Начиная с PHP 5.1+, вы можете использовать API потоков PHP с md5 или md5_file, чтобы избежать полной загрузки в память
  • Используйте exec() для вызова системной md5sum команды
  • Используйте функцию MySQL MD5() для вычисления хеша

Поскольку реализовать их все тривиально, вам будет легко внедрить и сравнить их все по использованию памяти и скорости. Вот некоторые тесты , показывающие, что система md5 через exec намного быстрее, чем PHP md5_file, с увеличением размера файла. Делать это по-своему - определенно худший способ использования памяти.

# 2, mysql_real_escape_string выполняет запрос к базе данных, так что вы фактически передаете данные BLOB-объектов в базу данных, возвращаете их в виде строки и передаете их снова (!) С помощью запроса INSERT. Таким образом, он перемещается на / с сервера БД 3x вместо 1x и использует 2x памяти в PHP.

Будет эффективнее использовать подготовленные PHP5 операторы и отправлять эти данные в базу данных только один раз. Прочтите раздел связанной статьи, и вы увидите, что когда вы связываете параметры, вы можете использовать тип BLOB-объектов для потоковой передачи данных BLOB-блоков в БД. Документы PHP для mysqli_stmt::send_long_data имеют отличный простой пример того, как ВСТАВИТЬ файл в столбец BLOB-объектов, как вы.

Делая это, и используя API потоков, md5_file или exec с системной командой md5, вы можете выполнить весь INSERT, не загружая весь файл в память, что означает использование памяти для вашей серии операций может быть так низко, как вы хотите!

3 голосов
/ 17 октября 2008

Если вы используете PDO и подготовленные отчеты, вы можете использовать тип PDO :: PARAM_LOB. См. Пример № 2 на странице большого объекта, где показано, как вставить изображение в базу данных с помощью указателя файла.

http://us2.php.net/manual/en/pdo.lobs.php

0 голосов
/ 18 октября 2008

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

0 голосов
/ 17 октября 2008

Хорошо, если использование памяти является вашей проблемой, вы можете прочитать большой файл в виде фрагментов и вставить эти фрагменты с помощью CONCAT в поле базы данных.

Пример кода (не тестировался):

    $id = 1337;
$h = fopen("path/to/file.ext", "r");
while (!feof($h)) 
    {
    $buffer = fread($h, 4096);
    $sql = "UPDATE table SET my_field = CONCAT(my_field, '" . mysql_real_escape_string($buffer) . "') WHERE Id = " . $id;
    mysql_query($sql);
    }

Этот метод будет медленнее, но вам потребуется только 4 КБ памяти.

0 голосов
/ 17 октября 2008

Тестировали ли вы выходной буферный трюк?

ob_start();
echo 'INSERT INTO some_table SET BlobData = \'', mysql_real_escape_string( $data ), '\', BlobHash = \'', $hash, '\'';
mysql_query( ob_get_clean() );

Еще одна вещь, которую вы можете сделать, - это конвертировать в mysqli или MDB2, которые поддерживают связанные параметры. Это позволит вам пропустить вызов mysql_real_escape_string и конкатенации строк.

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