最近,将一个部署在阿里云上的 Rails 项目,连接的 MySQL 数据库的编码,由 utf8 调整为 utf8mb4。 实施过程不是非常顺利,目前,已经部署完成,观察了一段时间,比较稳定。 写了本篇总结,希望可以帮到有需要的朋友。
根本的原因在于,采用 utf8 编码的 MySQL 无法保存占位是 4 个字节的 Emoji 表情。 为了使后端的项目,全面支持客户端输入的 Emoji 表情,升级编码为 utf8mb4 是最佳解决方案。 之前一篇博文有讲到,不调整 MySQL 编码,使用 rumoji 替换 Emoji 表情为字母编号。 博客原文链接:Ruby on Rails Use MySQL DB Support iPhone emoji http://manageyp.github.com/ruby-on-rails/2014/12/10/ruby-on-rails-use-mysql-db-support-iphone-emoji.html
但是,这样处理至少有两个问题:
另外,看到很多网友建议,迁移 MySQL 至 PostgreSQL、MongoDB 等,迁移成本不小,暂时不考虑。
备注:MySQL 5.5.3 版本之后,引入了 utf8mb4 字符集。 在 mysql client 端,输入以下命令,确认 mysql server 是否支持 utf8mb4 编码。
mysql> SHOW CHAR SET WHERE Charset LIKE "%utf8%";
+---------+---------------+--------------------+--------+
| Charset | Description | Default collation | Maxlen |
+---------+---------------+--------------------+--------+
| utf8 | UTF-8 Unicode | utf8_general_ci | 3 |
| utf8mb4 | UTF-8 Unicode | utf8mb4_general_ci | 4 |
+---------+---------------+--------------------+--------+
至于排序规则(collation)选择默认的 utf8mb4_general_ci,还是 utf8mb4_unicode_ci。 请参考下面文章的介绍,Character Set & Collation In MySQL: http://infopotato.com/blog/index/mysql_character_set_and_collation 作者从排序的准确性,以及性能方面,告诉我们应该选用 utf8mb4_unicode_ci 。
名称为:production_new
字符集选择:utf8mb4
# config/database.yml
encoding: utf8mb4
"string".force_encoding("UTF-8")
(4)停止 Ruby 进程,停止队列等服务
(5)部署项目报错 正式版部署之前,有在本地和 Staging 做过测试。 信心满满的在正式版部署,执行的过程中,遇到下面的错误:
Character set 'utf8mb4' is not a compiled character set and is not specified in the '/path/mysql/charsets/Index.xml' file
rake aborted!
Mysql2::Error: Can't initialize character set utf8mb4 (path: /path/mysql/charsets/)
/path/.rvm/gems/ruby-2.0.0-p598/gems/mysql2-0.3.16/lib/mysql2/client.rb:70:in `connect'
/path/.rvm/gems/ruby-2.0.0-p598/gems/mysql2-0.3.16/lib/mysql2/client.rb:70:in `initialize'
经过一番检查后发现,错误是由于当前系统上的 MySQL 客户端版本过低导致。
# 列出所有被安装的 mysql 包
rpm -qa | grep mysql
mysql-devel-5.1.73-3.el6_5.x86_64
mysql-5.1.73-3.el6_5.x86_64
mysql-libs-5.1.73-3.el6_5.x86_64
# 清理现有 mysql
yum list installed | grep -i mysql
yum remove mysql mysql-*
# 修改安装源,不修改源,重新安装的仍然是 5.1 的版本
rpm -Uvh http://repo.webtatic.com/yum/el6/latest.rpm
# 安装 5.5 版本的 mysql client
yum install libmysqlclient16 --enablerepo=webtatic
yum install mysql55w mysql55w-libs mysql55w-devel --enablerepo=webtatic
# 如果你需要安装 mysql server,请执行
yum install mysql55w-server --enablerepo=webtatic
MySQL 5.5 客户端安装完毕之后,再次运行项目,同样出现上面的报错信息。 于是,决定重新安装一遍 mysql2 Gem。安装完成之后,项目运行正常。
# 重新安装 mysql2 Gem
gem uninstall mysql2
gem install mysql2 -v '0.3.16'
# 备份现有数据库
mysqldump -h host -u user -p production_old > production_old.sql
# 还原至新数据库
mysql -h host -u user -p production_new < production_old.sql
mysql -h host -u user -p production_new < db/sql/utf8mb4_charset.sql
SQL 文件内容,举例说明如下: 通常只需要修改 table 的编码,若该 table 内有字段 (column) 是 VARCHAR,或者 TEXT, MySQL 将自动调整该字段的编码,与 table 的默认编码保持一致
ALTER TABLE `versions` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
MySQL 索引长度的限制错误
在执行 SQL 文件时,发现有些 table 更改编码时报错,信息如下:
ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes
报错原因在于:MySQL Innodb 的索引长度限制为 767 字节,UTF8mb4 字符集是 4 个字节, 767 字节 / 4 字节每字符 = 191 字符(即默认的索引最大长度) 因此在 varchar(255) 类型字段上,创建索引会失败,提示最大索引长度为 767 字节。
实践中发现,MySQL 5.5.x 的版本,只有 Unique 的索引,或者联合索引,才会报错。 普通索引,Rails 的 Migration 会自动对索引 Index 的长度增加一个 191 的限制。 而 MySQL 5.6.x 的版本,即使普通索引也会报错。
处理思路:先移除索引,然后修改表的编码,修改成功之后。调整索引对应字段的长度为 191,最后再次创建索引。
ALTER TABLE `users` DROP INDEX index_users_on_email;
ALTER TABLE `users` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `users` MODIFY `email` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE `users` ADD UNIQUE index_users_on_email (email);
# 查看一下最新的表结构
SHOW CREATE TABLE `users`;
使用 Migration 迁移 Table 编码
如果没有索引的问题,可以使用下面的代码,修改表的编码,简洁许多。 也可以使用下面的代码,打印 SQL 语句到 .sql 文件。 然后,对引起索引 Index 错误的 Table 单独增加处理的 SQL 语句。
def up
char_set = 'utf8mb4'
collation = 'utf8mb4_unicode_ci'
table_names = ActiveRecord::Base.connection.tables
table_names.each do |table_name|
execute "ALTER TABLE `#{table_name}` CONVERT TO CHARACTER SET #{char_set} COLLATE #{collation};"
end
end
原文链接 Aliyun RDS Change MySQL Charset From utf8 To utf8mb4: http://manageyp.github.com/ruby-on-rails/2015/03/05/aliyun-rds-change-mysql-charset-from-utf8-to-utf8mb4.html
参考链接 (推荐阅读): Support Emoji in Rails 3.2.14 http://mumaren.me/blog/2013/11/27/support-emoji-in-rails-3-dot-2-14/
active_record, MySQL, and emoji http://tech.taskrabbit.com/blog/2014/04/24/active-record-mysql-and-emoji/
update MySQL version from 5.1 to 5.5 in CentOS 6.2 http://stackoverflow.com/questions/9361720/update-mysql-version-from-5-1-to-5-5-in-centos-6-2
Character Set & Collation In MySQL http://infopotato.com/blog/index/mysql_character_set_and_collation