是否使用“SET NAMES”

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

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

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

任何人都可以评论上面的报价,或者说,更正式地说,你的build议/最佳实践,以确保我的数据库工作stream是unicode意识。

我的目标语言是PHP和Python,如果这是相关的。

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

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

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

 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静态设置这些服务器variables 。

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

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 ,你发出一个标准的sql查询到mysql。 虽然sql查询确实通过pdo,然后通过mysql客户端库,然后到达mysql服务器,只有mysql服务器parsing并解释该sql查询。 这很重要,因为mysql服务器不会向pdo发送任何消息,或者mysql客户端让它知道字符集和编码已经改变,所以pdo完全不知道它发生的事实。

重要的是不要这样做,因为客户端库不能正确处理string,如果它不知道当前的字符集。 如果客户端不知道正确的字符集,那么大多数常见的操作都可以正常工作,但是不会出现string转义的情况,比如PDO :: quote 。 你可能会认为你不需要担心这样的手工原语string转义,因为你使用了准备语句,但事实是绝大多数pdo:mysql用户在不知不觉中使用模拟的预处理语句,因为它是pdo的默认设置:mysql司机很长一段时间了。 模拟的准备语句不使用由mysql api提供的真正的本地mysql准备语句; 相反,PHP的所有值都调用PDO::quote() ,str_replacing你所有的占位符与你引用的值。

既然你不能正确地转义一个string,除非你知道你正在使用的字符集,如果你已经通过集名改变了某些字符集,这些模拟的准备好的语句就容易受到sql注入的攻击。 无论使用sql注入的可能性,如果使用针对不同字符集的转义scheme,仍然可能会破坏string。

对于pdo mysql驱动程序,可以在连接时指定字符集,方法是在DSN中指定它 。 如果你这样做,客户端库和服务器都会知道字符集。

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

但不适当的string转义并不是唯一的问题。 例如,你也可以在使用PDO :: bindColumn时遇到问题,因为列名被指定为string,所以编码很重要。 一个例子可能是名为ütube的列名(注意变音符号),然后通过设置名称从latinutf8 ,然后尝试$stmt->bindColumn('ütube', $var); ütube是一个utf8编码的string,因为你的php文件是utf8编码的。 它不会工作,你需要将string编码为latin1变体…现在你有各种疯狂的事情发生。

不知道关于Py,但PHP现在有mysql_set_charset ,其中指出,这是“不推荐使用mysql_query()执行SET NAMES更改字符集[和]的首选方法。 请注意,这个函数是为MySQL 5.0.7引入的,所以它不适用于早期版本。

 mysql_set_charset('utf8', $link); 

$ link是使用mysql_connect创build的mysql_connect