【问题标题】:Whether to use "SET NAMES"是否使用“SET NAMES”
【发布时间】:2010-12-11 15:47:02
【问题描述】:

在阅读 O'Reilly 的“高性能 MySQL”时,我偶然发现了以下内容

另一个常见的垃圾查询是 SET NAMES UTF8,这是错误的方式 无论如何做事(它不会改变 客户端库的字符集;它 只影响服务器)。

我有点困惑,因为我曾经将“SET NAMES utf8”放在每个脚本的顶部,让数据库知道我的查询是 utf8 编码的。

任何人都可以评论上述引用,或者更正式地说,您有什么建议/最佳实践来确保我的数据库工作流程是 unicode 感知的。

如果相关的话,我的目标语言是 php 和 python。

【问题讨论】:

  • 你最终实施了什么技术?

标签: php python mysql internationalization


【解决方案1】:

TLDR

// The key is the "charset=utf8" part.
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8';
$dbh = new PDO($dsn, 'user', 'pass');

这个答案强调 php 的 pdo 库,因为它无处不在。

一个简短的提醒 - mysql 是一个客户端-服务器架构。这很重要,因为不仅有实际数据库所在的 mysql 服务器,而且还有单独的 mysql 客户端驱动程序,这是与 mysql 服务器通信的东西(它们是单独的实体)。你可以说 mysql 客户端和 pdo 混合在一起。

当您使用set names utf8 时,您向mysql 发出标准sql 查询。虽然 sql 查询确实通过 pdo,然后通过 mysql 客户端库,最后到达 mysql 服务器,但只有 mysql 服务器解析和解释该 sql 查询。这很重要,因为 mysql 服务器不会将任何消息发送回 pdo 或 mysql 客户端让它知道字符集和编码已更改,因此 mysql 客户端和 pdo 都完全不知道它发生的事实。

不要这样做很重要,因为如果客户端库不知道当前字符集,它就无法正确处理字符串。大多数常见操作都可以在客户端不知道正确字符集的情况下正常工作,但字符串转义(例如PDO::quote)则不会。您可能认为您不需要担心这种手动原始字符串转义,因为您使用准备好的语句,但事实是绝大多数 pdo:mysql 用户在不知不觉中使用emulated prepared statements,因为它是 pdo:mysql 的默认设置司机很长时间了。模拟的预处理语句不使用 mysql api 提供的真正的原生 mysql 预处理语句;相反,php 相当于在您的所有值上调用 PDO::quote(),并为您用引用的值替换所有占位符。

除非您知道所使用的字符集,否则您无法正确转义字符串,因此如果您通过set names 更改为某些字符集,这些模拟的预处理语句很容易受到 sql 注入的影响。不管 sql 注入的可能性如何,如果您使用针对不同字符集的转义方案,您仍然可以破坏您的字符串。

对于pdo mysql驱动,可以在连接时指定字符集,通过specifying it in the DSN。如果你这样做,客户端库和服务器都会知道字符集,所以事情会像他们应该的那样工作。

// The key is the "charset=utf8" part.
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8';
$dbh = new PDO($dsn, 'user', 'pass');

但不正确的字符串转义并不是唯一的问题。例如,您在使用PDO::bindColumn 时也会遇到问题,因为列名被指定为字符串,因此编码也很重要。一个示例可能是名为ütube(注意变音符号)的列名,您通过集合名称从latin 切换到utf8,然后您尝试使用$stmt->bindColumn('ütube', $var); 转换为$stmt->bindColumn('ütube', $var);,其中ütube 是一个utf8 编码字符串,因为你的 php 文件是 utf8 编码的。它行不通,你需要将字符串编码为 latin1 变体......现在你有各种疯狂的事情发生。

【讨论】:

  • 如今(2014 年 9 月)PDO 是将 PHP 与数据库连接的最新和最强大的方法,我认为这个答案应该被接受。
【解决方案2】:

mysql_set_charset() 将是一个选项 - 但选项仅限于 ext/mysql。对于ext/mysqli,它是mysqli_set_charset,对于PDO::mysql,您需要指定一个连接参数。

由于使用此函数会导致 MySQL API 调用,因此应该认为它比发出查询要快得多。

在性能方面,确保脚本和 MySQL 服务器之间基于 UTF-8 的通信的最快方法是正确设置 MySQL 服务器。因为SET NAMES xequivalent

SET character_set_client = x;
SET character_set_results = x;
SET character_set_connection = x;

SET character_set_connection = x 在内部也执行SET collation_connection = <<default_collation_of_character_set_x>>,您也可以在my.ini/cnf 中静态设置these server variables

请注意在同一 MySQL 服务器实例上运行的其他应用程序可能会出现问题,并且需要一些其他字符集。

【讨论】:

  • 我提到了mysql_set_charset() - 这是旧ext/mysql 中包含的功能。如上所述,PDOext/mysqli 都没有直接为这个操作提供任何支持。
  • 看来我发布的链接不可靠。这里有一个更好的:php.net/manual/en/mysqli.set-charset.php 不知道你的意思是 mysqli 不支持这个操作。
  • 啊,好的-抱歉...没有意识到有mysqli_set_charset 函数可用。感谢您的澄清。
【解决方案3】:

不确定 py,但 php 现在有 mysql_set_charset,它指出这是“不推荐使用 mysql_query() 来更改字符集 [和] 执行 SET NAMES 的首选方法。”请注意,此功能是为 MySQL 5.0.7 引入的,因此不适用于早期版本。

mysql_set_charset('utf8', $link);

其中 $link 是使用mysql_connect 创建的连接

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-06
    • 1970-01-01
    • 2014-12-23
    • 1970-01-01
    • 1970-01-01
    • 2011-02-09
    • 2012-12-12
    相关资源
    最近更新 更多