Rails5 - utf8mb4(主にiOSの絵文字)に対応する
実行環境
文字コードのutf8mb4に対応する
今回、主にiOSの絵文字に対応するためにutf8mb4
に変更する必要がありました。MySQLであればバージョン5.5以降でutf8mb4
をサポートしています。
以下がそのために行った設定です。
config/database.yml
を設定する
default: &default adapter: mysql2 pool: 5 timeout: 5000 encoding: utf8mb4 collation: utf8mb4_general_ci socket: /var/lib/mysql/mysql.sock
今回、重要なのはencoding
とcollation
です。それぞれの大まかな意味としては、以下のとおりです。
MySQL自体の文字コード設定を確認する
show variables like 'char%';
で現在の全体的なMySQLの文字コード関連の設定を参照できます。
MariaDB [(none)]> show variables like 'char%'; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8 | | character_set_connection | utf8 | | character_set_database | latin1 | | character_set_filesystem | binary | | character_set_results | utf8 | | character_set_server | latin1 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+ 8 rows in set (0.00 sec)
utf8mb4
には対応できていないので、設定する必要があります。
文字コードをutf8mb4
に変更する
/etc/my.cnf.d/server.cnf
にあたる設定ファイルを編集します。
$ sudo vi /etc/my.cnf.d/server.cnf
[server] character-set-server = utf8mb4 [mysqld] character-set-server = utf8mb4 plugin-load = handlersocket.so [client] default-character-set = utf8mb4
再起動して、文字コードが反映されたか確認してみます。
$ sudo service mysql stop Shutting down MySQL... SUCCESS! $ sudo service mysql start Starting MySQL.170531 23:11:49 mysqld_safe Logging to '/var/lib/mysql/localhost.localdomain.err'. 170531 23:11:49 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql SUCCESS! $ mysql -uroot -p MariaDB [(none)]> show variables like 'char%'; +--------------------------+----------------------------+ | Variable_name | Value | +--------------------------+----------------------------+ | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | utf8mb4 | | character_set_filesystem | binary | | character_set_results | utf8mb4 | | character_set_server | utf8mb4 | | character_set_system | utf8 | | character_sets_dir | /usr/share/mysql/charsets/ | +--------------------------+----------------------------+ 8 rows in set (0.00 sec)
うまく設定が反映されているのがわかります。character_set_system
がutf8
のままですが、こちらの変数はシステムの使用する文字セットで常にutf8が使用される設定のため問題ありません。
試しにマイグレーションしてみる
今回は新たにマイグレーションしてみることで、utf8mb4
に対応できているかを確認してみます。マイグレーション生成は省略します。
$ rake db:migrate rake aborted! StandardError: An error has occurred, all later migrations canceled: Mysql2::Error: Specified key was too long; max key length is 767 bytes: CREATE UNIQUE INDEX `index_piyo` ON `piyo_tables` (`hoge`, `piyo`) Tasks: TOP => db:migrate (See full trace by running task with --trace)
Mysql2::Error: Specified key was too long; max key length is 767 bytes
といったエラーが出ます。
Mysql2::Error: Specified key was too long; max key length is 767 bytesを解決する
このエラー自体は以下の説明が分かりやすいです。
デフォルトでは最大インデックス長は767バイトですので、UTF8では255文字まで、UTF8MB4では191文字までしかインデックスを張れません。 しかし、こちらはMySQL 5.5.14以降からINNODB_LARGE_PREFIXオプションで最大3072バイトまで拡張できるので、そんなに大きな問題ではないです。
これに対応するために、MySQLの設定ファイルにINNODB_LARGE_PREFIXオプションを追加し、インデックスのキープレフィックスを拡張します。
$ sudo vi /etc/my.cnf # 以下を追加 [mysqld] innodb_file_format = Barracuda innodb_file_per_table = 1 innodb_large_prefix
主に3つの指定をしています。
innodb_large_prefix
を有効にする- 上記設定を有効にするために
innodb_file_format
をBarracuda
にするinnodb_file_per_table
を有効にする
- 上記設定を有効にするために
設定後、MySQLを再起動します。
$ sudo service mysql stop Shutting down MySQL.. SUCCESS! $ sudo service mysql start Starting MySQL.170531 23:24:12 mysqld_safe Logging to '/var/lib/mysql/localhost.localdomain.err'. 170531 23:24:12 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql SUCCESS!
これでおそらく、インデックスのキープレフィックスが拡張できました。
テーブル作成時にオプションを追加するようにパッチを当てる
kamipoさんのモンキーパッチを使い、テーブル作成時のオプションを指定します。
config/initializers/ar_innodb_row_format.rb
ActiveSupport.on_load :active_record do module ActiveRecord::ConnectionAdapters class AbstractMysqlAdapter def create_table_with_innodb_row_format(table_name, options = {}) table_options = options.merge(:options => 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC') create_table_without_innodb_row_format(table_name, table_options) do |td| yield td if block_given? end end alias_method_chain :create_table, :innodb_row_format end end end
再度、マイグレーションしてみる
$ rake db:migrate:reset Dropped database 'piyo_development' Dropped database 'piyo_test' Created database 'piyo_development' Created database 'piyo_test' == 20170531130735 CreatePiyos: migrating ========================= -- create_table(:piyos) -> 0.0109s == 20170531130735 CreatePiyors: migrated (0.0191s) ================
完了できました。
db/schema.rb
にダンプされたテーブルを見てみる
ActiveRecord::Schema.define(version: 20170531130735) do create_table "piyos", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" do |t| 省略 end end
うまく設定ができています。
以上でutf8mb4
対応は完了です。