当前位置:首页 > 问答 > 正文

MySQL里字符集那些事儿,乱码问题到底咋回事简单聊聊

行,那咱就直接开聊 MySQL 的字符集和乱码这点事儿,这事儿说复杂能整得非常复杂,但咱今天就用大白话把它捋清楚,你肯定遇到过这种情况:在网页上输入几个字,存到数据库里,再拿出来一看,哎哟,怎么变成一堆问号或者奇怪的符号了?这就是典型的乱码。

要弄懂乱码,首先得明白三个最基本的概念,我把它们比作“三次转码”,这三次转码就像一场接力赛,数据从你的电脑出发,跑到 MySQL 数据库里存起来,然后再跑回你的电脑上显示,这个过程中有三位“接力棒选手”,任何一位跑偏了,接力棒(也就是你的文字)就可能传错地方,导致乱码。

第一位选手:客户端字符集(就是你告诉 MySQL 的话) 当你用某个软件(比如一个网页、一个程序,或者 MySQL 自带的命令行)连接上 MySQL 时,你这个“客户端”得先跟 MySQL 打个招呼:“嗨,我这边发过来的文字,都是用 XXX 编码的哦!”这个“XXX”就是客户端字符集,比如最常用的 utf8 或者 gbk。 这个过程是通过执行一条命令来设定的,SET NAMES 'utf8';,这条命令一下,就等于告诉 MySQL 服务器:“喂,接下来我发给你的所有数据,你都当成 UTF-8 编码来理解,别搞错了!” 如果这里没说对,或者根本没说过,MySQL 就可能用它自己默认的字符集(latin1)去解读你发过来的 UTF-8 文字,那肯定就驴唇不对马嘴了,这就好比你对一个只懂英语的人说中文,他肯定听不懂。

第二位选手:MySQL 数据库内部的存储设置 数据被 MySQL 正确“听明白”之后,它得找个地方存起来,存到哪里呢?存到数据表里,而数据表的每个字段(用户名”、“标题”这种),在创建的时候就可以指定一个字符集。 这就好比仓库里有很多货架,每个货架上可以贴个标签,写明这个货架是专门放“中文书”的,还是放“英文书”的,MySQL 常见的存储字符集有 utf8utf8mb4(这个更重要,后面说)、latin1 等。 理想情况下,第二位选手应该和第一位选手保持一致,如果客户端用 UTF-8 发数据,存储也用 UTF-8,那就无缝衔接了,但如果存储的字符集是 latin1,而它试图去存储一个 UTF-8 编码的中文字,就好比一个小抽屉硬要塞进一个大箱子,要么塞不进去(报错),要么就被截断、损坏了,存进去的已经是个“残次品”。

第三位选手:返回结果的字符集 当你从数据库里查询数据时,过程正好反过来,MySQL 会把存在仓库(数据表)里的数据拿出来,然后要发回给你的客户端,在发回去之前,MySQL 也得想一想:“我该用哪种编码把数据打包发出去,对方才能看懂呢?” 这个“打包方式”就是结果的字符集,MySQL 会很智能地沿用你一开始用 SET NAMES 设置的字符集,也就是说,如果你之前告诉它“用 UTF-8 接收”,它默认也会“用 UTF-8 发送”。 这样,只要保证“收”和“发”用的是同一种编码,数据在返回的路上就不会出问题。

乱码是怎么发生的? 现在我们把三位选手串起来,乱码的根源就清晰了:

  1. 最常见的情况: 第一位和第三位选手没问题(比如你的程序用 UTF-8 收发),但第二位选手(数据库存储)用的是 latin1,你的 UTF-8 中文数据被 MySQL 错误地当成 latin1 存了进去,本身已经损坏了,之后即使你用 UTF-8 去读,读出来的也是损坏的数据,自然是乱码。这种情况,数据在存入的那一刻就已经坏了。
  2. 另一种情况: 存储本身没问题(比如是 UTF-8),但“接送”环节出了错,你的数据是 UTF-8 存的,但查询的时候,MySQL 误以为你的客户端只懂 gbk,于是用 GBK 编码把数据打包发给你,你的客户端用 UTF-8 去解读这份 GBK 编码的数据,显示出来就是乱码。这种情况,数据在库里是好的,只是在传输回显示的最后一刻被误解了。

特别重要的一个坑:MySQL 的 utf8 不是真 UTF-8 这是个大坑,很多人在这里栽跟头,标准的 UTF-8 编码是支持存储所有 Unicode 字符的,包括一些不常用的汉字或者 emoji 表情(????),但 MySQL 历史上定义的 utf8 字符集,最多只支持 3 个字节的字符,而很多 emoji 表情需要 4 个字节,这就导致你存一个笑脸进去,可能会报错或者变成问号。 那怎么办?好在 MySQL 后来推出了一个真正的、完整的 UTF-8 支持,叫做 utf8mb4,这个 mb4 最多4个字节”的意思。现在的最佳实践是,永远使用 utf8mb4 作为你的数据库、数据表和字段的字符集,而不是老的 utf8

怎么尽量避免乱码? 记住一个核心原则:保证整个链条上,所有环节的字符集设置完全一致。

  1. 建库建表时: 明确指定字符集为 utf8mb4CREATE DATABASE mydb CHARACTER SET utf8mb4;
  2. 程序连接时: 在连接上数据库之后,立刻执行类似 SET NAMES 'utf8mb4'; 的语句(具体语法取决于你的编程语言和数据库驱动),确保收发口径一致。
  3. 检查你的编辑器和终端: 确保你用来查看结果的工具(比如网页浏览器、命令行终端)也支持并设置为 UTF-8 编码。

只要这“三次转码”的接力棒每一次都稳稳地传对了,乱码问题基本上就跟你无缘了,如果已经出现了乱码,解决起来会比较麻烦,因为你要判断数据是在哪个环节坏的,但理解了这个原理,至少你有了排查的方向。

MySQL里字符集那些事儿,乱码问题到底咋回事简单聊聊