如何在PHP中构建大型MySQL INSERT查询而不浪费内存

时间:2008-10-17 06:48:24

标签: php mysql

我的代码看起来像这样:

$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);

我知道每个''都不是很有效率。运算符将重新分配更大的内存块,并且将多次复制30MB字符串。

有没有比以下解决方案更有效的方法?

$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 个答案:

答案 0 :(得分:7)

这里有两个问题:

#1,有几种不同的方法可以计算MD5哈希值:

  • 按照您的方式执行操作,并将其作为字符串加载到PHP中并使用PHP的md5()
  • 使用PHP的md5_file()
  • 从PHP 5.1+起,您可以将PHP的流API与md5md5_file一起使用,以避免完全加载到内存中
  • 使用exec()来调用系统的md5sum命令
  • 使用MySQL的MD5()函数计算哈希值

由于这些都很容易实现,因此您可以轻松地实现所有内存使用和速度并对其进行基准测试。以下some benchmarks显示系统md5通过exec比PHP的md5_file快得多,因为文件大小增加。就内存使用而言,按照自己的方式行事绝对是最糟糕的方式。

#2,mysql_real_escape_string执行数据库查询,因此您实际上将blob数据传输到数据库,将其作为字符串返回,然后使用INSERT查询再次传输(!)。所以它往返于DB服务器3x而不是1x,并且在PHP中使用2x内存。

使用PHP5 prepared statements会更高效,并且只将此数据发送到数据库一次。阅读链接的文章部分,您将看到它提到在绑定参数时,您可以使用blob类型将blob数据以块的形式传输到数据库。 PHP docs for mysqli_stmt::send_long_data有一个非常简单的例子,就像你一样将文件插入到blob列中。

通过这样做,并使用系统md5命令使用流API,md5_fileexec,您可以完成整个INSERT,而无需将整个文件加载到内存中,这意味着内存您的一系列操作的用法可以尽可能低!

答案 1 :(得分:3)

如果您正在使用PDO和准备好的语句,则可以使用PDO :: PARAM_LOB类型。请参阅LOB页面上的示例#2,其中显示了如何使用文件指针将图像插入数据库。

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

答案 2 :(得分:0)

您是否对输出缓冲技巧进行了基准测试?

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调用字符串连接。

答案 3 :(得分:0)

如果您没有将查询放入另一个变量,而是将其直接传递给MySQL命令,它会有什么不同吗?我从来没有试过这个,但它可能会有所不同,因为整个字符串没有存储在另一个变量中。

答案 4 :(得分:-1)

如果内存使用是您的问题,您可以读取块中的大文件,并将这些块与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);
    }

这种方法会慢一些,但你只需要4Kb的内存。