Edited at

Rails(ActiveRecord)とMySQLでutf8mb4を扱う設定

More than 3 years have passed since last update.


ActiveRecordからutf8mb4を扱えない主な理由

ActiveRecordのstring型カラムがvarchar(255)で定義されるので、utf8mb4ではインデックスのキープレフィックスが767byteを超えてしまう。


ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes


この問題をMySQLの設定とRailsへのパッチで解決する。


MySQLの設定

文字コードをutf8mb4で運用するために、インデックスのキープレフィックスを拡張する。



  1. innodb_large_prefixenableにする


    • 1を有効にするために、innodb_file_formatBarracudaにする

    • 1を有効にするために、innodb_file_per_tableenableにする




  2. innodb_default_row_formatDYNAMICにする # >= 5.7.9


my.cnfのオプション


my.cnf

innodb_file_per_table = 1

innodb_file_format = Barracuda
innodb_large_prefix = 1
innodb_default_row_format = DYNAMIC # >= 5.7.9


MySQL5.6

いくつかのパラメータを変更する。



  • innodb_file_per_tableのデフォルトはenable (>= 5.6.6)


  • innodb_file_formatのデフォルトはAntelopeなので、Barracudaへの変更が必要


  • innodb_large_prefixのデフォルトはdisableなので、enableに変更する


  • innodb_default_row_formatバージョン5.6系にこのオプションは無い


    • デフォルトがCOMPACTであり、かつコンフィグから指定できない

    • テーブル作成時のオプションで指定してやる必要がある




MySQL5.7

5.7.9以上であるなら、デフォルト設定でよい。



  • innodb_file_per_tableのデフォルトはenable


  • innodb_file_formatのデフォルトはBarracuda (>= 5.7.7)


  • innodb_large_prefixのデフォルトはenable (>= 5.7.7)


  • innodb_default_row_formatのデフォルトはDYNAMIC (>= 5.7.9)


Railsの設定

MySQL5.7.9以上を利用しているならdatabase.ymlにおいてutf8mb4を設定すればよく、とくべつなことをする必要はない。


database.ymlの設定


config/database.yml

default: &default

adapter: mysql2
encoding: utf8mb4
charset: utf8mb4
collation: utf8mb4_general_ci

MySQL5.7未満では、テーブル作成時にROW_FORMAT=DYNAMICを渡してやらなければならない。

もしくはカラムオプションかインデックスオプションにlimit: 191を付けてやる。


add_index :table1, :column1, length: 191



Rails4

kamipo神のモンキーパッチを使って、テーブル作成時のオプションにROW_FORMAT=DYNAMICを追加してやる。

テーブルのCREATEオプションとしてENGINE=InnoDB ROW_FORMAT=DYNAMICを追加してくれる。


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

参考: http://qiita.com/kamipo/items/101aaf8159cf1470d823

しかし、db/schema.rbにダンプされるテーブルオプションにはENGINE=InnoDB ROW_FORMAT=DYNAMICがついていない。

これもまたkamipo神によるactiverecord-mysql-awesomeをGemfileに追加しておくと、オプションを正しくダンプしてくれる。

RailsからMySQLのutf8mb4を扱えるようになるまでの道のりは、kamipoさんのおかげで切り開かれたと言っても過言ではないのでとっても感謝です。


余談でRails5

beta3ではデフォの状態だとRails4と同様にまだコケる。

kamipoさんのPRがたくさんマージされているので、そのうち。