如何使MySQL正确处理UTF-8

125

在我昨天提出的一个问题的回答之一建议我应该确保我的数据库能够正确处理UTF-8字符。我如何在MySQL中做到这点?


4
希望我们能得到一份全面的答案,涵盖各种MySQL版本、不兼容性等内容。 - Edward Z. Yang
请参见https://dev59.com/anE95IYBdhLWcg3wXcrd。 - tripleee
1
@EdwardZ.Yang -- MySQL 4.1引入了“字符集”; 5.1.24混淆了德语尖音s(ß)的排序,这在5.1.62中通过添加另一个排序得到了纠正(可以说是使情况变得更糟); 5.5.3用新的字符集utf8mb4填充了utf8。 - Rick James
1
这个问题与这一个非常相似。请看这个链接:https://dev59.com/v3A75IYBdhLWcg3wBkO1 - Nyein Aung
1
值得指出的是,这些答案大多数都是完全错误的。不要使用 utf8。它只支持最多3字节的字符。在MySQL中应该使用的正确字符集是 utf8mb4 - SineSwiper
15个回答

117

更新:

简短回答 - 几乎总是应该使用 utf8mb4 字符集和 utf8mb4_unicode_ci 排序规则。

修改数据库:

ALTER DATABASE dbname CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

参见:

原始回答:

MySQL 4.1及以上版本的默认字符集为UTF-8。您可以在my.cnf文件中验证此信息,请记得设置客户端和服务器(default-character-setcharacter-set-server)。

如果您希望将现有数据转换为UTF-8,请备份数据库,并将其作为UTF-8导入,确保:

  • 在查询/插入数据库之前使用SET NAMES utf8
  • 在创建新表时使用DEFAULT CHARSET=utf8
  • 此时您的MySQL客户端和服务器应该是UTF-8 (见my.cnf)。请记住,您所使用的任何语言(如PHP)也必须是UTF-8格式的。某些版本的PHP将使用自己的MySQL客户端库,可能不支持UTF-8。

如果您确实想要迁移现有数据,请务必先备份!当计划不如预期时,会出现很多奇怪的数据截断情况!

一些资源:


34
我的理解是,在MySQL中,utf8仅涵盖Unicode的一个小子集。你应该使用utf8mb4来确保完全支持Unicode。请参考http://mathiasbynens.be/notes/mysql-utf8mb4。长时间以来,我一直在使用MySQL的`utf8`字符集来创建数据库、表和列,并*假设*它映射到上述UTF-8编码。 - Aaron McDaid
9
MySQL从未使用UTF-8作为默认字符集,4.1和5.x版本一直使用latin1latin1_swedish_ci作为默认字符集和排序规则。请参考MySQL手册中的“服务器字符集和排序规则”页面进行确认:https://dev.mysql.com/doc/refman/5.1/en/charset-server.html。 - Animism
2
@TimTisdall 不必担心 utf8mb4 在大多数文本为 ASCII 时会占用额外的存储空间。虽然 char 字符串是预分配的,但 varchar 字符串不是--请参见此文档页面上的最后几行。例如,char(10) 在 utf8mb4 下会悲观地保留 40 个字节,但 varchar(10) 将根据可变长度编码分配字节。 - Kevin A. Naudé
1
@Kevin 我认为你误读了。我认为最大行长度是64k。你只能将utf8mb4字段的大小设置为其中的1/4,因为它需要保留那么多的空间。所以,即使是ASCII字符,你也只能插入16k个字符。 - Tim Tisdall
显示剩余9条评论

48

要使此更改“永久生效”,请在 my.cnf 中进行以下修改:

[client]
default-character-set=utf8
[mysqld]
character-set-server = utf8

要检查,请转到客户端并显示一些变量:

SHOW VARIABLES LIKE 'character_set%';

验证它们全部都是utf8编码,除了..._filesystem应该是binary编码和指向MySQL安装位置的..._dir


在我的情况下它没有起作用,但是我仍然在/etc中创建了名为my.cf的文件,并使用给定的内容。我使用了create table my_name(field_name varchar(25) character set utf8); - Marek Bar
"SHOW VARIABLES LIKE 'character_set%';" 命令向我展示了我的连接问题。谢谢! - javsmo
4
这不正确。MySQL所称的“utf8”并非完整的UTF-8。 - T.W.R. Cole
设置字符集为 utf8; 以设置一个新值。 - Almett

33

MySQL 4.1及以上版本默认采用称为utf8的字符集,但实际上它只是UTF-8的子集(仅支持三个字节或更小的字符)。

如果需要"完整"的UTF-8,请使用utf8mb4字符集。


5
完全同意,这是唯一正确的答案。 utf8 不包括表情符等字符,而 utf8mb4 则包括。查看此链接以获取有关如何更新的更多信息:https://mathiasbynens.be/notes/mysql-utf8mb4 - jibai31
@Basti -- 大部分正确(直到最近,Latin1是默认设置),但不完整(未讨论如何正确插入/选择UTF8编码的数据,以及在HTML中显示)。 - Rick James
尊敬的@RickJames,Basti说“到目前为止” - 我记不得在我发布这个问题时看到你的回答了。 - T.W.R. Cole
唉,UTF8 问题有大约 5 种明显不同的症状,程序员会做出大约 4 种错误导致麻烦。大多数答案只指出可能需要修复的 一个 问题。原始问题很广泛,因此答案需要全部 4 个。也许 Basti 熟悉其中 一种 症状,你提供的解决方案是针对这种症状的。 - Rick James
或者换个角度看,“正确处理UTF-8字符”可以有两种理解方式...你认为需要使用utf8mb4。我认为是在进出数据库时不要破坏文本。按照你和巴斯蒂的解释,你的答案是正确而完整的。 - Rick James
12
顺便说一句,我想暂停一下,仔细看看MySQL团队。 o_o 你们当时在想什么?你们知道你们通过在程序中创建一个名为“utf8”的代码页而实际上并不是UTF-8,造成了多少混乱吗?该死的混蛋们。 </愤怒> - T.W.R. Cole

23
简短回答:在以下4个地方使用utf8mb4
  • 客户端中的字节为utf8,而不是latin1/cp1251等。
  • 建立客户端连接到MySQL时使用SET NAMES utf8mb4或类似方法。
  • 对于所有表/列使用CHARACTER SET utf8mb4,但排除仅限于ascii/hex/country_code/zip_code等的列。
  • 如果你要输出到HTML,则需要使用<meta charset charset=UTF-8>。(是的,这里的拼写不同)

更多信息
一路使用UTF-8

上述链接提供了“需要处理所有问题的详细规范答案”。-- 本论坛有空间限制。

编辑

除了CHARACTER SET utf8mb4包含“所有”世界字符之外,COLLATION utf8mb4_unicode_520_ci可谓是使用最好的通用排序。 (对于那些想在语言中使用微妙差别的人,还有土耳其语,西班牙语等排序)。


我的新链接,向您展示如何通过输出来调试UTF8问题。 - Rick James
为什么unicode_520_ci不是最好的全能选择:https://dev59.com/EmMk5IYBdhLWcg3w3xhY#49982378 - lsl
@Louis - 正如我所暗示的,西班牙语、土耳其语(以及波兰语)用户可能不会很高兴。 “最佳全能”倾向于伤害每个人。 MySQL 8.0有一个更新的“最佳”排序规则:utf8mb4_0900_ai_ci。 不幸的是,L=Ł。 - Rick James

5

字符集是数据库(默认)和表的属性。 您可以查看(MySQL命令):

show create database foo; 
> CREATE DATABASE  `foo`.`foo` /*!40100 DEFAULT CHARACTER SET latin1 */

show create table foo.bar;
> lots of stuff ending with
> ) ENGINE=InnoDB AUTO_INCREMENT=252 DEFAULT CHARSET=latin1

换句话说,检查数据库字符集或更改它非常容易。
ALTER TABLE `foo`.`bar` CHARACTER SET utf8mb4; /* was: utf8 */

2
这是不正确的。MySQL所称的“utf8”并不是“完整”的UTF-8。 - T.W.R. Cole

3

3

我按照Javier的解决方案进行了操作,但在my.cnf文件中添加了一些不同的行:

[myslqd]
skip-character-set-client-handshake
collation_server=utf8_unicode_ci
character_set_server=utf8 

我在这里找到了这个想法:http://dev.mysql.com/doc/refman/5.0/en/charset-server.html,它出现在页面底部的第一个/唯一的用户评论中。他提到skip-character-set-client-handshake具有重要意义。

2
这个没有人喜欢、零票的答案是唯一帮助我的东西!所以它得到了我的投票,那是肯定的。skip-character-set-client-handshake 是关键。 - Marcus

0

使用在mysql上排序utf8mb4,在DBI连接中添加属性mysql_enable_utf8mb4并在连接到mysql后执行sql命令"SET NAMES utf8mb4"将使perl正确处理UTF-8。

#!/usr/bin/perl
print "Content-type: text/html; charset=UTF-8\n\n";

#use utf8;
#use open ':utf8';
#binmode STDOUT, ":utf8";
#binmode STDIN , ":utf8";
#use encoding 'utf8';

our $dbh = DBI->connect("DBI:mysql:database=$database;host=$servername;port=$port",$username,$password, {PrintWarn => 0, PrintError => 0, mysql_enable_utf8mb4 => 1}) || die;
$dbh->do("SET NAMES utf8mb4");

0

将您的数据库排序规则设置为UTF-8,然后将表排序规则应用于数据库默认值。


-1

你的答案是可以通过 MySql 设置进行配置。我的回答可能有些脱离上下文,但这也是对你有帮助的。
如何配置 字符集校对规则

对于使用默认 MySQL 字符集和校对规则 (latin1, latin1_swedish_ci) 存储数据的应用程序,不需要特殊配置。如果应用程序需要使用不同的字符集或校对规则存储数据,则可以通过以下几种方式配置字符集信息:

  • 针对每个数据库指定字符设置。例如,使用一个数据库的应用程序可能需要 utf8,而使用另一个数据库的应用程序可能需要 sjis。
  • 在服务器启动时指定字符设置。这会使服务器对所有未做其他安排的应用程序使用给定的设置。
  • 在配置时间指定字符设置,如果您从源代码构建 MySQL。这会使服务器对所有应用程序使用给定的设置,而无需在服务器启动时指定它们。

这里展示了有关设置utf8字符集的问题的示例,同时还设置了更有帮助的排序规则(utf8_general_ci排序规则)。

为每个数据库指定字符设置

  CREATE DATABASE new_db
  DEFAULT CHARACTER SET utf8
  DEFAULT COLLATE utf8_general_ci;

在服务器启动时指定字符设置

[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci

在MySQL配置时间指定字符设置
shell> cmake . -DDEFAULT_CHARSET=utf8 \
           -DDEFAULT_COLLATION=utf8_general_ci

要查看适用于您的连接的字符集和排序系统变量的值,请使用以下语句:

SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';

这可能是一个很长的答案,但有很多方法可以使用。希望我的回答对你有所帮助。若需要更多信息,请参考http://dev.mysql.com/doc/refman/5.7/en/charset-applications.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接