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で運用するために、インデックスのキープレフィックスを拡張する。
-
innodb_large_prefixをenableにする
- 1を有効にするために、
innodb_file_formatをBarracudaにする - 1を有効にするために、
innodb_file_per_tableをenableにする -
innodb_default_row_formatをDYNAMICにする # >= 5.7.9
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の設定
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を追加してくれる。
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がたくさんマージされているので、そのうち。