character_set_client:客户端发送的sql语句编码字符集。它的作用就是告诉mysql服务器,本地客户端采用了什么编码环境。insert into t values(‘吴’); 服务器知道这条sql是以character_set_client指定的字符集所编码的,'吴’将被转化为指定的编码格式,比如它的utf8编码为E590B4,最终以0101…发送到server。
character_set_connection:服务器将接收到的character_set_client指定编码的sql语句翻译成character_set_connection指定编码的sql。
character_set_results:指示了服务器将查询结果返回给客户端的字符集。服务器检索库表之后,需根据该变量的值将查询结果的字符集转化为character_set_results所指定的字符集。
每个客户端或者终端在连接上server之后,都有自己特定的系统变量,服务器默认采用客户端的本地化参数初始化系统变量的值,并且在本次会话中可以动态改变其值。Linux下的shell和win下的dos在连接到server之后,本地化参数可能存在差异(编码格式:linux默认一般是utf8,win下一般是gbk),上面三个变量在不同终端下值的不同就是最好的验证,下面的例子连接的是都是相同的数据库。
在mysql workbench下的三个参数:
在navicat下的三个参数:
Windows cmd下的三个参数:
由上面三个图可知,对于不同的客户端,连上数据库之后,会根据本地化参数修改上述的三个参数。
在客户端连接数据库服务器的过程中,三个变量都是session级别的,可以随时通过客户端进行设置,当然一般这三个值都是设置成一样的,而且要确保DB的字符集要包含或者和三个参数设置的字符集一样才能保证不出现乱码。如下面的例子:
DB的字符集:latin1
character_set_client字符集:utf8
character_set_connection字符集:utf8
character_set_results字符集:utf8
因为latin1是1个字节用来存储字符,而utf8如果存储中文用的是3个字节存储一个字符,如果在linux客户端(客户端的编码是utf8)插入中文字符‘吴’他的utf8编码为‘E590B4 ’,那么在步骤(2)插入到数据库中肯定就是乱码。而且插入的时候,还会抛出如下告警(修改数据库sql_mode为非严整模式可插入,但抛出警告):
| Warning | 1366 | Incorrect string value: '\xE5\x90\xB4' for column 't' at row 1 |
这种是属于有损转换,数据库字符是latin1是单字节字符,无法解析utf8的3字节字符,最终会用乱码代替,之后也是无法复原。
如果把:
character_set_client字符集:utf8
character_set_connection字符集:utf8
character_set_results字符集:utf8
数据库的表的字符集编码设置为:utf8
此时通过linux客户端插入中文字符‘吴’,此时是可以正常插入,没有任何问题,但如果在查询的时候,设置了character_set_results=gbk,这个时候查询出来的肯定是乱码,因为‘吴’这个中文字符的编码在gbk里面是找不到对应字符的,所以解码出库时就会显示乱码,这个时候,存入的数据是utf8编码,只要把character_set_results这个变量改为utf8,那么就能正常显示中文了,这种情况是属于无损编码转换,只要设置正确的编码就能还原。
保证character_set_client,character_set_connection,character_set_results三个的编码一致,并保证数据库的库,表,相应字段字符集编码设置与这三个变量相同或者是他们的超集就可以完全避免中文乱码。在实际实践中,设置客户端(包含app,客户端,数据库jdbc驱动)字符集编码为utf8mb4,数据库相应的库,表,字段都设置为utf8mb4,可以显示所有的字符(包含表情)而不产生乱码。