Rails5 MySQL(innoDB)でutfmb4を使う
"Index column size too large. The maximum column size is 767 bytes"とエラー
入社2ヵ月の新米エンジニアです。ログとして残す。もっと良い解決の仕方があれば教えて欲しいというのも理由の一つ。 Railsのdb:migrate途中でエラー。 エラー内容は以下。
Mysql2::Error: Index column size too large. The maximum column size is 767 bytes.: CREATE UNIQUE INDEX
とか
Specified key was too long; max key length is 767 bytes
原因
直接の原因はdatabase.ymlのencodingをutf8からutf8mb4に変更したこと。 絵文字への対応が変更の目的。
ActiveRecordのString型カラムはvarchar(255)定義され、デフォルトだと最大で767byteまでしか使えない。utf8は1~3byteで1~4byteになるutf8mb4だと767byteをオーバーしてしまいエラーになる。
環境
- AWSのEC2サーバーの/var/www以下にRails 5.1.2
- anyanvで管理しているrbenvでruby2.4.1をglobal *devise用のモデルのdb:migrate中に今回のエラーが起きた
解決まで
最大767byteまでしか使えないならその制限を広げてしまえば解決出来るはず。MySQLにはinnodb_large_prefixというオプションが追加されているらしいのでこれをONにしたい。ONにしたら3072byteまで広げることが出来る。それをするためにinnodb_file_formatをAntelopeからBarracudaにしないといけない。
※MySQL 5.7.7以降はデフォルトでBarracuda。innodb_large_prefixもデフォルトでON。
自分の環境の設定が知りたい時はMySQLにログインして
show global variables like 'innodb%';
上のコマンドを叩くとinnodb云々の設定が確認出来る。
MySQLの設定を変更してdb:migrate
# /etc/my.cnf innodb_file_format=Barracuda innodb_file_format_max=Barracuda innodb_file_per_table=1 innodb_large_prefix=1
1はON。 設定を変更後
$ sudo service mysqld restart
してindexの所でエラーになったためテーブルが作成されている。drop tableしてdb:migrate。 ネットの情報見たらmy.cnfの書き方は様々だがここまででエラー解決してる人が多い。書き間違いがないか確認したりmysqlのエラーログを見たりするも解決できず。
databaseを削除して作成し直してからdb:migrate
drop database hoges; create database hoges;
してdb:migrateしたらマイグレーションに成功。
innodb_file_per_tableが原因だった。デフォルトでは1つのテーブルスペースファイルに全てのDBテーブルのデータが格納されるため、データ量が肥大化したらこのファイルも巨大化する。このオプションをオンにするとテーブル単位でスペースファイルが作成される。テーブル単位で別ディスクへの移動やバックアップ、リストアが出来て管理がしやすくなるらしい。ただオンとオフがあるってことは良し悪しがあるってことで、テーブル毎にバイナリファイルを作ってくれるためディスクI/Oが減るメリットの変わりにテーブルがあまりに多いとクラッシュした時にリカバリに時間がかかったりする。
参考になったサイト qiita.com