最近在搞项目,但是在数据库初始化这一块老是会出现数据库乱码的情况出现。网上找了很多资料,对于数据库编码这一块有点理解,记录一下
问题出现
由于之前一直依赖于数据库可视化工具,例如mysql workbench
,navicat
之类的,这一类书数据库可视化工具一般都有默认的编码,例如mysql workbench
使用的编码就是utf8mb4
编码。
之前一直使用的是mysql workbench
初始化sql,由于项目快要完结了,要对项目镜像容器化处理,mysql也作为容器之一(方便部署,实际不会容器化数据库)。那么必不可免的就是处理相关建库建表的操作了。对于容器化的操作而言,直接将init.sql
放在/docker-entrypoint-initdb.d
文件夹下就会自动执行。
但是用这种方式初始化的表,如果存在一些中文字符的话,会出出现乱码的情况发生,而使用一些可视化工具就不会出现这类问题。
问题分析
这里我们要理解一下数据库编码的问题。mysql
默认编码为latin1,使用的是单字节来表示编码,作为ascii的一种拓展。而中国文化博大精深,仅仅使用单字节肯定是不够的,所以我们一般使用的是unicode
编码,而utf8
编码作为unicode
编码的一种子集,被广泛使用于我们日常生活。
utf8
最多使用3字节来表示一个中文字符,但是有些emjoy
表情包就无法存储到我们的数据库中了,于是有一种编码叫做utf8mb4
(utf8 most byte 4)允许使用4个字节来存储emjoy
表情。当然了,还有一些大小写敏感,口音敏感的问题这里就不展开说了,有兴趣可以看一下这篇文章:从一个慢查询到MySQL字符集编码
使用show variables like 'char%'
查看数据库的编码(mysql workbench)
使用show variables like 'char%'
查看数据库的编码(docker 容器里面)
> show variables like 'char%';
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | utf8mb4 |
| character_set_system | utf8mb3 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
我们这里补充一下编码的知识,数据库之所以会产生乱码就是编码和解码的方式不一致产生的。而数据库编码总的来说有三大块:客户端编码(client),数据库编码(database),还有结果输出编码(results)。
这里我们可以看到,不同的地方在于character_set_client
,character_set_connection
,character_set_results
,其中mysql workbench
使用的是utf8mb4
而docker mysql
使用的是latin1
也就是说,由于我的项目使用的是utf8mb4
编码读取的,使用mysql workbench
来初始化数据库的时候,默认使用的utf8mb4
编码初始化,由于编码和解码的方式一致,所以不管数据库采用什么方式存储数据都不会产生乱码的情况(除非长度不够产生字符截断的情况)。而使用docker mysql
直接执行初始化的时候,使用的是latin1
编码区入库的,而我的项目仍然是utf8mb4
编码读取,由于编码和解码的方式不一致导致乱码。
问题解决
我们在实行初始化语句的的时候使用utf8mb4
编码即可解决问题。
SET character_set_client = utf8mb4;
注意,我们这里还有一个character_set_connection
编码,是连接字符集。相关介绍可以看一下上面提到的文章(从一个慢查询到MySQL字符集编码)。
假设MySQL当前没有character_set_connection这个参数,SQL语句在server端通过character_set_client参数解码之后变进入内部字符集进行比较,那么例如这样子的SQL中的字符串(“ABC”)的编码也会是character_set_client的编码值:select * from order where orderid =“ABC”。那么如果开发者想让"ABC"拥有其他编码怎么办,MySQL提供了一个叫做Character Set Introducers的[8]方法,可以这么指定 select * from order where orderid = _utf8 “ABC”。后面发现每个语句都写一个Character Set Introducers来指定编码太累了,于是提供了character_set_connection参数,对于没有Character Set Introducers的字符串,都编码成character_set_connection所指定的编码。
总结
所以mysql执行语句的时候应该是这样的一个编码的过程