dedebug 2019-07-01
MySQL中无法存储Emoji字符,源自于MySQL 5.5之前utf8编码只支持最大3个字节,而Emoji需要4个字节。从MySQL 5.5开始,其utf8编码开始有了支持4个字节的utf8类型:utf8mb4 .
通常,如果你的MySQL版本是5.5+但又无法在某个具体的列存储Emoji字符,就是源自于这个列的类型还停留于过去的utf8类型,没有升级到最新的支持4字节的utf8mb4.
解决方案的思路也很简单,就是在确保你的MySQL是5.5+的前提下,将需要存储Emoji字符的列的编码改为utf8mb4.
网上的大部分资料采用了更改 "列、表、库" 的编码类型,再配上修改MySQL配置文件,并重启MySQL的方式来解决问题:
-- 修改一列: ALTER TABLE table_name CHANGE column_name column_name VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 修改表: ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 修改库: ALTER DATABASE database_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; -- ============================== -- 修改配置文件 /etc/my.cnf [client] default-character-set = utf8mb4 [mysql] default-character-set = utf8mb4 [mysqld] character-set-client-handshake = FALSE character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci
这样的解决方案有些粗糙,并涉及到MySQL的重启,对生产环境来讲,重启db是一件代价极其高昂的事情。
事实上,如果只是某个列存在Emoji字符的存储问题,那就只需要修改这个列的编码类型即可。如果确实需要修改到"表、库"这个级别,那再做修改不迟。
做完这一步后,应该可以在db客户端直接存储Emoji字符了。但是,对于需要连接到MySQL的应用来讲,仅仅上述步骤的修改还不够,还无法使应用将Emoji字符成功地存储MySQL。这涉及到应用与MySQL建立连接的问题。
由于我们想要不重启MySQL,那么MySQL当中默认给出的 "建立新连接" (set connection) 所支持的字符编码依旧是utf8。虽然db里面的某个特定的列已经支持了utf8mb4,但"连接"不支持对utf8mb4字符的传输,Emoji字符也就无法顺利运输到db中。
所以问题就变成了,如何在不重启MySQL的前提下,重新设置connection的字符编码。
有一条MySQL命令是 set names 'xxx'
,会将character_set_client、character_set_connection、character_set_results的编码统一设置。使用这条命令 set names utf8mb4;
便可以达到我们的目的。
但问题是,应该在哪里设置这条命令呢?很多人会直接在自己的MySQL客户端运行这条命令,但发现自己的应用依旧无法正确传递Emoji字符。这是因为,当你在MySQL客户端运行这条命令时,只是设置了你的客户端同MySQL的connection字符编码,但你的应用却使用了自己的connection,当然无法达到目的。
所以正确的做法是,你需要在自己应用建立connection后,在代码中运行这条命令。
而如果你使用的是Druid DataSource(如果你使用的是Tomcat DataSource,那么你可以大胆切换到Druid DataSource),那么这件事情就更容易了,只需要在配置文件的 connectionInitSqls
中完成这件事。以Java为例,你只需要:
DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl(url); dataSource.setUsername(userName); dataSource.setPassword(password); // set 'utf8mb4' to support emoji character dataSource.setConnectionInitSqls(Arrays.asList("set names utf8mb4;"));
如果你使用的是xml来配置bean,则只需要:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName" value="${jdbc-driver}"/> <property name="url" value="${jdbc-url}"/> <property name="username" value="${jdbc-user}"/> <property name="password" value="${jdbc-password}"/> <property name="connectionInitSqls" value="set names utf8mb4;"/> </bean>
如此,便能在不重启MySQL的情况下让你应用将Emoji字符传递到db中去。