Application无法传递Emoji字符到MySQL的问题

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中去。

相关推荐