突发状况周末,当我在峡谷马上拿下五杀时,突然手机弹出消息。 "线上系统的评论功能不能用了"。 还没等我反应过来,一个视频会议电话已经打进来了。作为社畜只能默默放下手机,打开电脑接入会议。 刚进入会议,产品疯狂吐槽。 "刚给用户开通灰度,用户怎么就用不了"; "重试好几次都不行"; "每次发表情都不行"; 听到这,有经验的研发兄弟应该大概猜出来了,跟数据库的字符集有关系。 于是迅速登录mysql,查询了评论表的字符集,发现是utf8。于是马上提单修改字符集为utf8mb4。问题马上解决了。
❝ 复盘维护的这个评论系统非常的老,前端支持的编辑器也很老,原本只支持纯文本格式,所以之前一直相安无事。但是上周老大提了个需求,让前端支持富文本编辑器。但是前端哥们没有跟我沟通,我也忘记查评论表字段的字符集了,导致了一个低级问题。 本来事情到这就结束了,但是聪明的我仔细想了下,明明存到计算机里都是0和1,为啥会报错呢? 要解释这个问题,我们还得从字符集编码这个话题开始聊起。 我们知道对于计算机而言,所有的数据都是以0和1的形式存储在磁盘中的,包括在网络上进行传输的信息都需要变为0和1再进行传输。 也就是需要一个机制 把人的语言变为计算机的语言,我们称之为编码。 同样的需要一个机制 把计算机的语言变为人的语言,我们称之为解码。 上面两个场景结合起来,统称为字符编码。 那我们平常总是遇到的乱码是什么情况呢? 那就是同一份数据的编码方和解码方使用的规则不一样导致的。 比如下面这样的编解码就容易友尽。 一、ASCII码既然有了通信的需求,那么就需要指定一套规则。只要大家都遵循这套规则,就万事大吉了,不会鸡同鸭讲了。完美实现同一个世界同一个梦想。 最开始,美国制定了一套字符编码,叫ASCII码,一共规定了128个字符。只需要7位(bit)就可以一一映射。但是一般采用一个字节(8 bit)进行存储编码,最高位统一为0。 比如我们必会的ping对应的编码就是:\u0070\u0069\u006e\u0067 (16进制表示,2进制太长了) 具体的映射规则可以到ASCII码对照表查看。 对于母语是英语的国家ASCII码是完全够用了。但是随着计算机的普及,越来越多的国家加入互联网。那么对于这些国家ASCII码肯定是不够用的。 比如中国,汉字就多达10万左右。ASCII码只使用8个字节进行表示,最多可以表示256个字符,还不够塞牙缝,肯定是不够用的。于是对于汉字就提出了GB2312 编码方式,即使用两个字节表示一个汉字,那么就可以表示 256 * 256 = 65536 个字符。 汉字提出了GB2312编码,那韩文呢,日文呢,泰文呢。世界上那么多国家那么多语言,如果每一个人都提出自己的编码方式,那计算机就得装载各种编码程序,这将极其复杂,也增加了出错的几率。 想想你好不容易下载的日本电影被编解码程序解析为了新闻联播时。 二、大一统-Unicode正所谓天下大势分久必合,合久必分,咳咳咳。 正式因为世界上存在太多的编码方式,一段0101010的二进制数字可以被解释为不同的语义。 因此对沟通上带来了极大的成本。 像上学的时候,看小说还是使用的txt,就总是容易出现乱码。 一般故事发展到这个时候,就会出现一本秘籍统一江湖,这就是Unicode编码。 unicode的思路很简单,你们不是国家多,语言多吗?行,我给世界上的每个字符都分配一个编号。 具体的字符映射表可以到 unicode映射表进行查询。 目前的序号的范围从0×000000到0x10FFFF,一共表示了110多万个字符。 不过这样的编码方式也带来了问题,对于单个英文字符,也需要三个字节进行编码,造成了极大的浪费。要知道互联网每天产生的数据可以绕地球好几圈,如果这么浪费的进行存储,只能说一句:土豪带带我。 故事发展到这个时候,就会出现一本秘籍统一江湖。 三、UTF-8由于互联网的高度普及,提出一种可行的高效的的编码方式迫在眉睫。 于是基于Unicode的编码方式UTF-8横空出世。 UTF-8并不是一种新的编码协议,他是Unicode编码的一种实现,类似的还有UTF-16,UTF-32。这是由于Unicode只是提出了一套映射规范,但是关于怎么存储,怎么提高效率都没有提及。所以才出现了UTF-8 我们知道按照原始的 Unicode 规定的存储,对于低位字符会操作极大的空间浪费。UTF-8提出了变长编码的思路。诶,你不是低位字符浪费吗,那我就用少一点的字节进行表示。 UTF-8 使用1~4个字符表示一个符号,根据符号的Unicode码而变化字节长度。 具体规则如下: 1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此UTF-8 编码是兼容ASCII的。 2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的二进制位,全部为这个符号的 Unicode 码。 \ 那么问题来,计算机怎么知道什么是单字节,什么是n字节。对他不就是010101吗? 别慌,UTF-8 对于专门定义了一张转化表进行了解释:
刚看到这张表,大家肯定一头雾水,别慌。我们举例实战推演帮助大家理解。 比如现在需要编码汉字"中",首先需要找到"中"Unicode编码。"中"Unicode编码是:4E2D(16进制)--> 20013(10进制)。可以发现落在了上表的 2048---65535 区间。也就是"中"对应的utf-8 编码为 1110xxxx 10xxxxxx 10xxxxxx。 第一步完成了,找到了模板,剩下就是填充xxx了。 把4E2D 转为 二进制 得到 100111000101101。 按照从后往前填充,不足补0的规制,把得到的二进制填充到utf-8模板中可得:11100100 10111000 10101101,这就是"中"经过utf-8编码后的结果。 正向编码理解了,那把0和1字符串变为字符就简单了。 首先按顺序读取,如果第一位是0,那证明是单字符,直接取一个字节去掉头部得到一个数字,去Unicode表中找对应的字符。 如果第一位是1,那就继续往后读,指定读到0为止。这个时候读到几个1就证明需要几个字节进行编码。例如"中"的二进制可以知道需要三个字节,所以这里取三个字节,分别去掉第一个字节的三个1,后面两个字节的10,得到一个数字,直接去Unicode表里找到对应字符。 四、大功已成相信学到这里,你已经完全掌握了编码的真谛,下次再遇到日本电影变成新闻联播时也可以从容应对了。 五、扩展通过下面的命令可以查询当前mysql支持的字符集。
这是我安装的mysql(8.0.29)支持的字符集。 虽然mysql支持了那么多字符集,但是经过上面的分析,可以知道utf8mb4基本可以覆盖99%的场景。 |
|