【问题标题】:Why use bin2hex when inserting binary data from PHP into MySQL?为什么在将二进制数据从 PHP 插入 MySQL 时使用 bin2hex?
【发布时间】:2011-02-03 05:55:18
【问题描述】:

我听说在将二进制数据(文件等)插入 MySQL 时,您应该使用 bin2hex() 函数并将其作为 HEX 编码值发送,而不是仅在二进制字符串上使用 mysql_real_escape_string 和使用它。

// That you should do
$hex = bin2hex($raw_bin);
$sql = "INSERT INTO `table`(`file`) VALUES (X'{$hex}')";

// Rather than
$bin = mysql_real_escape_string($raw_bin);
$sql = "INSERT INTO `table`(`file`) VALUES ('{$bin}')";

据说是出于性能原因。与 MySQL 如何处理大字符串与它如何处理 HEX 编码值有关

但是,我很难确认这一点。我所有的测试都表明完全相反。 bin2hex 方法的速度慢了约 85%,并使用了约 24% 的内存。
(我正在 PHP 5.3、MySQL 5.1、Win7 x64 上测试这个 - 使用非常简单的插入循环。) em>

例如,此图显示了测试代码运行时 mysqld 进程的私有内存使用情况:


(来源:advefir.com

有没有人有任何解释或资源可以澄清这一点?

谢谢。

【问题讨论】:

  • 使用"INSERT INTO `table`(`file`) VALUES (X{$hex})"; 时可能会有不同的性能(从十六进制值中删除引号)? (+1 顺便说一句)
  • @Jacco 感谢您的建议。我做了几个测试,这两种方法的执行似乎几乎相同。 X'...' 方法似乎在内存和 CPU 使用方面都有一点优势。 - 我将结果一起编辑并上传,如果您有兴趣:atli.advefir.com/images/myisam_joined.png, atli.advefir.com/images/innodb_joined.png
  • 有趣,我真的很想请一位 DBA 来解释一下这里的“原因”。

标签: php mysql binary insert


【解决方案1】:

例如,如果您遇到此处描述的类似问题:http://www.php.net/manual/en/function.mysql-real-escape-string.php#82015

例如即使 mysql_real_escape_string 似乎是“二进制安全的”,您也不能将它(作为示例)与 igbinary_serialize 结合使用 - 反序列化只会失败。

在这种情况下,您需要在将数据插入 mysql 之前进行 bin2hex。

另外,通常你从 mysql 读取数据比插入数据更频繁:)

【讨论】:

    【解决方案2】:

    对我来说,这听起来像是一个都市传说。

    bin2hex() 将输入中的每个字节映射到输出中的 两个 字节 ('a' -> '61'),因此您应该注意到执行查询的脚本显着增加了内存- 它应该使用至少与要插入的二进制数据的字节长度一样多的内存。

    此外,这意味着在长字符串上运行 bin2hex() 比运行 mysql_real_escape string() 花费的时间要长得多,正如 MySQL's documentation 中所解释的那样 - 只需转义 6 个字符:NULL\r\n\, 和“Control-Z”。

    那是 PHP 部分,现在是 MySQL:服务器需要执行反向操作才能正确存储数据。反转任何一个函数所花费的时间几乎与原始操作一样长 - mysql_real_escape_string() 的反转函数需要将转义值 (\\) 替换为未转义值 (\),而 bin2hex() 的反转需要用一个新字节替换每个字节元组

    由于在二进制数据上调用 mysql_real_escape_string() 是安全的(根据 MySQL 和 PHP's documentation,或者即使只是考虑到该操作除了上面列出的转换之外不进行任何其他转换),执行它绝对没有意义如此昂贵的操作。

    【讨论】:

    • 这是有道理的。仅在 PHP 中存储查询字符串所需的额外内存似乎足以避免使用bin2hex 函数,而且我自己的测试表明 MySQL 也受到影响。然后是转换的额外 CPU 成本。 ——这听起来确实越来越像都市传说了。尽管如此,我还是想知道是什么开始的。为什么人们认为这是个好主意。
    • 我认为有些人认为使用名为 ..._escape_string 的函数对二进制数据进行编码或在人类可读的 SQL 语句中发送二进制数据可能是不明智的,但实际上并没有错(尽管该函数可能有别名 - 例如 mysql_escape_data() 或类似的)
    • 好点。我可以看到人们会如何看待它,尤其是那些来自强类型语言的人。 - 不过,我从来没有真正考虑过它们有什么不同。我的意思是,PHP 字符串基本上只是字节数组,就像二进制数据一样。 (至少在 PHP 6 之前。)
    • 我读到这个的原因是:MySQL 可以对十六进制数据使用流运算符,因此它不必将整个字符串加载到内存中。显然,这不是真的。
    【解决方案3】:

    我自己一直在对此进行测试,并且得出了相当一致的结果。 (尽管我的测试有点粗糙。)

    我测试了三台电脑

    1. Windows 7 (x64)、PHP 5.3、MySQL 5.1
    2. Ubuntu 9.10 (x64) PHP 5.2, MySQL 5.1
    3. Ubuntu 10.04 (x32) PHP 5.3, MySQL 5.1

    到目前为止,所有三个平台上的测试都表明了相同的情况:

    • 在 MyISAM 上插入 BLOB 比在 InnoDB 上快 2 到 8 倍。二进制字符串的差异似乎高于十六进制编码的字符串。 (见下面的数据)
    • 平均而言,使用 HEX 编码字符串 bin2hex 转换为 X'...' 比使用转义二进制字符串 mysql_real_escape_string 在原始数据上)使用更多内存。 - 这似乎适用于 MyISAM 和 InnoDB。
    • 二进制字符串在 MyISAM 上更快,但 HEX 编码的数据在 InnoDB 上更快。

    测试基本上只是一个简单的循环,对原始数据进行转义或十六进制编码(在脚本顶部检索一次的 2.4 MiB 图像),构建查询字符串并执行它通过mysql_querymysqli::query 函数。 - 我测试了两个扩展。好像没什么区别。

    我将 Ubuntu 10.04 (#3) 的结果放在电子表格中。来自 Ubuntu 9.10 (#2) 机器的结果几乎相同,所以我没有费心设置它们:
    (终于有理由正确测试 Google Docs 了!xD)

    这些图表显示了 Win7 (#1) 机器上 mysqld 进程的私有内存使用情况。

    【讨论】:

      【解决方案4】:

      十六进制字符串明显长于相应的二进制字符串。只需在 PHP 和 MySQL 的内存中传输时间和复制它就可以解决问题。

      老实说,我不是底层实现方面的专家,但最好不要在 SQL 中传递数据,而是使用例如PDOStatement的参数绑定?也许这里有更多知识的人可以确认这是否确实会导致数据作为二进制字符串发送,完全在任何 SQL 语句之外,或者 PDO 是否只是在后台进行转义和查询字符串操作。

      无论哪种方式,您都可以获得安全(和简单)的好处。

      【讨论】:

      • 感谢您的回复。那也是我的第一个;转换过程和增加的字符串长度会导致性能下降。看来我们是对的。 - 但是,我现在发现有几个页面显示了bin2hex 函数的使用(或者更令人不安的是,Base64 函数),我看不出原因。这没有任何意义...... - 顺便说一句,我个人确实使用准备好的语句(通常是mysqli)。这个问题比实际更假设:)
      • bin2hex/base64 将避免任何字符集问题,如果表是(错误地)使用 TEXT 字段而不是 BLOB 创建的。但代价是数据大小最多增加 3 倍(假设数据完全是非 ascii 并完全转换为 %xx%yy%zz...)
      • hex 和 base64 都会增加发送数据的大小。使用十六进制,数据以二进制形式存储。后来,数据以 base64 编码格式存储,因此增大了 33%。 (但这并不能回答 OP 的问题)
      猜你喜欢
      • 1970-01-01
      • 2010-11-27
      • 2010-10-15
      • 2012-04-25
      • 2010-12-17
      • 2012-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多