production.log

株式会社リブセンスでエンジニアをやっている星直史のブログです。

rake db:migrateでIndex column size too large.と言われてしまった場合の対応

概要

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つあります。

  1. my.cnfによるMySQLの設定変更
  2. テーブルの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つの方法があります。

  1. ALTER TABLEによる変更
  2. 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に変更

先人がいましたので、説明はそちらに譲ります!

qiita.com

最後にbundle exec rake db:migrateを実行し、うまくいくかを確認します。

まとめ

今回はrake db:migrateIndex column size too large.が出てしまう問題(通称767バイト問題)の解決方法を紹介しました。 これは、MySQLのmy.cnfの設定の変更と、テーブルのrow_formatの変更が必要です。 テーブルのrow_formatALTER TABLEで直接修正する方法もありますし、ActiveRecordのデフォルトの挙動を変更して対応することも可能です。