这个问题源自苹果客户端emoji表情,插入乱码引发的
应用日志报错

2012年曾经在项目上遇到类似的问题,戳这里

但是这两年在上一个公司这个问题也一直争论不下,争论点还是在实例是否需要重启的问题上

这两天正巧业务在测试环境需要修改,抓住这个机会,请应用运维协助测试了下

环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
语言:Java
应用服务:Tomcat
驱动:mysql-connector-java-5.1.37
URL:database.jdbcUrl=jdbc:mysql://192.168.1.1:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE
数据库:Percona MySQL 5.5.30
+--------------------------+-----------------------------------------------------+
| Variable_name | Value |
+--------------------------+-----------------------------------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
+--------------------------+-----------------------------------------------------+

官方说明

MySQL Connector/J Change log
官方驱动说明

我们先来说一下

正确的操作方法和步骤

1.修改mysql实例配置文件

1
2
3
4
vi my.cnf
将 character_set_server=utf8
修改为 character_set_server=utf8mb4

2.重启mysql,查看字符集

1
2
3
4
5
6
7
8
9
10
11
12
13
show variables like 'character%';
+--------------------------+-----------------------------------------------------+
| Variable_name | Value |
+--------------------------+-----------------------------------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8mb4 |
| character_set_system | utf8 |
+--------------------------+-----------------------------------------------------+

3.打开general_log

1
2
3
4
5
6
7
set global general_log=on;
+--------------------------+-----------------------------------------------------+
| Variable_name | Value |
+--------------------------+-----------------------------------------------------+
| general_log | ON |
+--------------------------+-----------------------------------------------------+

3.应用配置不变

推荐使用

1
2
3
4
这里配置了characterEncoding=utf8
而MySQL配置已修改,连接建立后字符集会被MySQL配置覆盖,使用utf8mb4
database.jdbcUrl=jdbc:mysql://192.168.1.1:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE

不推荐

1
2
3
4
5
这里去掉了characterEncoding选项
如果程序中在QUERY语句前包含SET NAMES UTF8MB4; 可以这样做
否则,插入失败
database.jdbcUrl=jdbc:mysql://192.168.1.1:3306/test?useUnicode=true&autoReconnect=true&rewriteBatchedStatements=TRUE

4.重启应用,建立重连

5.查看general_log
general_log

6.应用验证,插入成功


感兴趣的朋友可以继续往下看

测试场景

下面是我们进行了排列组合各种配置的修改测试,其目的是想找到在不重启DB的情况下,如何完成修改的方法。结论是我们失败了,哈哈

开始测试

所有测试,建议DBA打开general_log

1
2
3
4
5
6
7
set global general_log=on;
+--------------------------+-----------------------------------------------------+
| Variable_name | Value |
+--------------------------+-----------------------------------------------------+
| general_log | ON |
+--------------------------+-----------------------------------------------------+

先来纠正下之前的例子

1
set global character_set_database=utf8mb4;

这个参数对于新创建的数据库来说,新建表默认为database字符集,而对于已存在的数据库是没用的,修改这个参数没有意义。

我们有同学说曾经测试过,无需重启DB,想想就开心,测试就此开始。。。

DB必须做的修改

按照官方文档,我们应该修改character_set_server

1
2
3
4
5
6
7
8
9
10
11
12
13
set global character_set_server=utf8mb4;
+--------------------------+-----------------------------------------------------+
| Variable_name | Value |
+--------------------------+-----------------------------------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8mb4 |
| character_set_system | utf8 |
+--------------------------+-----------------------------------------------------+

将需要用到utf8mb4字符集的表结构修改

1
ALTER TABLE t1 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

开发会说,我只修改特定字段就行了

在这里,DBA不强制修改表,但是对于维护来说,将表字符集修改比较方便管理

应用配置修改

1
2
3
database.jdbcUrl=jdbc:mysql://192.168.1.1:3306/test?useUnicode=true&characterEncoding=utf8mb4&autoReconnect=true&rewriteBatchedStatements=TRUE
应用重启失败

1
2
3
4
5
傻傻按照官方文档修改,说实话没看懂文档"..."的意思,望高手解答
database.jdbcUrl=jdbc:mysql://192.168.1.1:3306/test?useUnicode=true&characterEncoding=...&autoReconnect=true&rewriteBatchedStatements=TRUE
应用重启失败
1
2
3
database.jdbcUrl=jdbc:mysql://192.168.1.1:3306/test?useUnicode=true&characterEncoding=.&autoReconnect=true&rewriteBatchedStatements=TRUE
插入失败
1
2
3
database.jdbcUrl=jdbc:mysql://192.168.1.1:3306/test?useUnicode=true&autoReconnect=true&rewriteBatchedStatements=TRUE
插入失败

general_log

1
2
3
database.jdbcUrl=jdbc:mysql://192.168.1.1:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true&rewriteBatchedStatements=TRUE
插入失败

general_log

结论:character_set_server这个参数在线修改是无效的,虽然show出来是ok的,但是应用插入依然报错,DBA只能重启实例

有同学说,之前测试是可以的,我们的结论是驱动的问题,有的驱动会使用character_set_server参数,有的驱动会使用character_set_database或者database的字符集(那么, 对于使用database的,需要增加ALTER DATABASE db1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;)。这也合理的解释了为什么有的case不重启DB,也成功