概要
development環境にMySQLを構築して一からmigrationを実行しようとした時に、Index column size too large.
と言われてしまいました。
この記事ではrake db:migrateでIndex column size too large.と言われてしまった場合の対応についてメモ程度にまとめます。
原因
原因は以下の通りです。
- MySQLだとキープレフィックスが767バイト制限がある
- Rails(ActiveRecord)をutf8mb4で動かしたい
- string型のカラムを定義するとvarchar(255)になり、utf8mb4でインデックスを張ると767バイトを超えてしまう
対応
対応は大きく2つあります。
- my.cnfによるMySQLの設定変更
- テーブルのrow_format変更
my.cnfによるMySQLの設定変更
my.cnfの設定
まずは、my.cnfの場所を確認します。
$ mysql --help | grep my.cnf order of preference, my.cnf, $MYSQL_TCP_PORT, /etc/my.cnf /etc/mysql/my.cnf /usr/local/etc/my.cnf ~/.my.cnf
macOSでbrewを使ってMySQLをinstallしているとコイツもいたりするので、注意が必要です。
/usr/local/Cellar/mysql\@5.6/5.6.42/my.cnf
my.cnfが特定できたら以下の設定を追加します。
[mysqld] innodb_file_format_max = Barracuda innodb_file_format = Barracuda innodb_file_per_table = 1 innodb_large_prefix = 1
innodb_large_prefix
オプションはインデックスの767バイト制限を3072バイトまで拡張するオプションです。
このオプションを適用させるには、テーブルのrow_formatをDYNAMICもしくは、COMPRESSEDにする必要があります。
この変更はALTER TABLEを使うか、ActiveRecordのcreate_tableメソッドを書き換えてデフォルトの挙動を変更する方法の2パターンがあります。(後述します)
MySQLの再起動と設定反映の確認
my.cnfの設定を変更したらMySQLの再起動をしなければ設定が読み込まれません。
brew services restart mysql
その後、mysqlに接続し、以下の結果が得られればうまく設定されています。
mysql> show global variables like "innodb_file%"; +--------------------------+-----------+ | Variable_name | Value | +--------------------------+-----------+ | innodb_file_format | Barracuda | | innodb_file_format_check | ON | | innodb_file_format_max | Barracuda | | innodb_file_per_table | ON | +--------------------------+-----------+
mysql> show global variables like "innodb_large_prefix"; +---------------------+-------+ | Variable_name | Value | +---------------------+-------+ | innodb_large_prefix | ON | +---------------------+-------+ 1 row in set (0.01 sec)
テーブルのrow_format変更
次にテーブルがのrow_format
を変更する必要があります。
このrow_format
の変更は2つの方法があります。
- ALTER TABLEによる変更
- ActiveRecordのcreate_tableメソッドのデフォルトのrow_formatをDYNAMICに変更
それぞれ説明します。
ALTER TABLEによる変更
まずは現在の状態を確認します。
(この記事にたどり着いたということは、おそらく対象のテーブルはCompact
になっているはずです。)
mysql> select row_format from information_schema.tables where table_schema="DB名"; +------------+ | row_format | +------------+ | Compact | | Compact | | Compact | ...
次に、対象のテーブル(エラーが出てしまったテーブル)のrow_formatを次のクエリで変更します。
ALTER TABLE テーブル名 ROW_FORMAT=DYNAMIC;
最後に期待の結果になっているか確認します。
mysql> select row_format from information_schema.tables where table_schema="DB名"; +------------+ | row_format | +------------+ | Compact | | DYNAMIC | | Compact | ...
ActiveRecordのcreate_tableメソッドのデフォルトのrow_formatをDYNAMICに変更
先人がいましたので、説明はそちらに譲ります!
最後にbundle exec rake db:migrate
を実行し、うまくいくかを確認します。
まとめ
今回はrake db:migrate
でIndex column size too large.
が出てしまう問題(通称767バイト問題)の解決方法を紹介しました。
これは、MySQLのmy.cnfの設定の変更と、テーブルのrow_format
の変更が必要です。
テーブルのrow_format
はALTER TABLE
で直接修正する方法もありますし、ActiveRecord
のデフォルトの挙動を変更して対応することも可能です。