1.

Laravel migration「1071 Specified key was too long」の原因と 3

編集
この記事の要点
  • 原因: MySQL 5.6 以前または innodb_large_prefix=OFF だと、InnoDB のインデックスキー上限が 767 バイト。utf8mb4 では 1 文字 4 バイトなので VARCHAR(255) = 1020 バイトで超過
  • 対処 1(Laravel 推奨): AppServiceProvider::boot()Schema::defaultStringLength(191)(191 × 4 = 764 バイト)
  • 対処 2: MySQL 5.7+ で innodb_large_prefix=ON + ROW_FORMAT=DYNAMIC または COMPRESSED にして上限 3072 バイト化
  • 対処 3: 個別カラムだけ ->index("col(191)") でプレフィクスインデックス化
  • MySQL 8.0 は既定で innodb_default_row_format=DYNAMIC なので通常発生しない

エラー内容

SQLSTATE[42000]: Syntax error or access violation:
1071 Specified key was too long; max key length is 767 bytes

(SQL: alter table `users` add unique `users_email_unique`(`email`))

Laravel の php artisan migrate でよく遭遇する MySQL エラーです。VARCHAR(255) に utf8mb4 でユニークインデックスを張ろうとして出ます。

なぜ起きるのか: バイト数計算

文字コード1 文字VARCHAR(255)VARCHAR(191)
latin11 byte255 byte191 byte
utf8 (3 byte UTF-8)3 byte765 byte ⚠️573 byte
utf8mb4 (4 byte UTF-8)4 byte1020 byte ❌764 byte ✅

MySQL 5.6 以前 / 古い InnoDB ではインデックスキー上限が 767 バイトのため、utf8mb4 で VARCHAR(255) は不可。

対処 1: Laravel で defaultStringLength(191)

Laravel 公式が推奨する最も簡単な方法:

これで $table->string('email')VARCHAR(191) 相当になり、191 × 4 = 764 バイト で 767 制限内に収まります。

対処 2: MySQL の row_format / innodb_large_prefix

サーバ設定を変えれば 191 制約は不要に:

# /etc/mysql/mysql.conf.d/mysqld.cnf

[mysqld]
innodb_file_format = Barracuda
innodb_file_per_table = 1
innodb_large_prefix = 1
innodb_default_row_format = DYNAMIC
# 再起動
sudo systemctl restart mysql

# 設定確認
mysql -e "SHOW VARIABLES LIKE 'innodb_large_prefix'"
mysql -e "SHOW VARIABLES LIKE 'innodb_default_row_format'"
mysql -e "SHOW VARIABLES LIKE 'innodb_file_format'"

既存テーブルは作成時の row_format で固定されているので、必要なら変更:

ALTER TABLE users ROW_FORMAT=DYNAMIC;

-- 確認
SELECT TABLE_NAME, ROW_FORMAT
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE();

MySQL 8.0 は既定で DYNAMICなので、この設定は不要です。

対処 3: 個別カラムだけプレフィクスインデックス

長い VARCHAR を保ったまま、インデックスの先頭 N 文字だけに絞る方法:

id();
    $table->string('email', 255);   // フルサイズ
    $table->string('name', 255);
    $table->timestamps();
});

// インデックスは先頭 191 文字だけ
DB::statement('ALTER TABLE users ADD UNIQUE users_email_unique (email(191))');

注意: プレフィクスインデックスは先頭 N 文字でのみ一意性が保証されるので、メールアドレスのように頭が衝突しない値に限ります。

対処 4: 該当カラムだけ短くする

id();
    $table->string('email', 191)->unique();  // 191 文字に明示
    $table->string('username', 64)->unique();
    $table->string('display_name', 255);     // インデックス不要なら 255 で OK
    $table->timestamps();
});

どれを選ぶか

状況推奨
MySQL 8.0+ を使える何もしなくて OK(既定 DYNAMIC)
MySQL 5.7 でサーバ設定変更可対処 2(innodb_large_prefix)
MySQL 5.7 / 共有レンタル等で設定不可対処 1(defaultStringLength(191))
個別テーブルだけ対処したい対処 3 or 4
MariaDB 10.2+既定で DYNAMIC(対処不要)

defaultStringLength(191) の副作用

全テーブルの string が 191 文字制限になるため:

  • 長い URL を保存する場合は $table->string('url', 2048) のように個別指定
  • 本文系は ->text() / ->longText() を使う
  • 既に作成済テーブルへの影響はなし(新規テーブル/カラムのみ)

関連エラー

エラー意味
1071 max key length is 767 bytes本記事の対象
1071 max key length is 3072 bytesDYNAMIC でも 3072 超過。さらに短くする必要
1709 Index column size too large同根の別表現(MySQL 5.6)
1118 Row size too large1 行のサイズ超過。列を TEXT / BLOB

FAQ

Q: 既に migration 済テーブルを後から DYNAMIC にしたい
A: ALTER TABLE x ROW_FORMAT=DYNAMIC。データ量が大きいテーブルは数分〜時間がかかります。

Q: defaultStringLength(191) を入れたら他の migration がエラー
A: 既に 191 超で作られたカラムにユニークを足そうとした可能性。該当 string に ->charset('utf8') を指定するか、明示的に短くする。

Q: MariaDB だと 1071 が出ない理由
A: MariaDB 10.2+ は既定で innodb_default_row_format=DYNAMIC + innodb_large_prefix=ON。同じ migration が MySQL 5.7 だけで失敗するのはこのため。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 1071 Specified key was too long; max key length is 767 bytes
  2. ERROR 1063 (42000): Incorrect column specifier for column '~'
  3. mysqld: Can't change dir to '...\MySQL\MySQL Server X.X\data\' (OS errno 2 - No such file or directory)
  4. Install/Remove of the Service Denied!
  5. Datetime 型が NULL に見える
  6. Warning: World-writable config file '/etc/mysql/my.cnf' is ignored
  7. ERROR 1698 (28000): Access denied for user 'root'@'localhost'
  8. Exception: Wrong MySQL configuration
  9. [Warning] TIMESTAMP with implicit DEFAULT value is deprecated.
  10. ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.
  11. ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
  12. Incorrect column specifier for column 'カラム名'
  13. BLOB/TEXT column 'description' used in key specification without a key length
  14. ERROR: /bin/sh: mysql_config: コマンドが見つかりません
  15. Host '...' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'
  16. CSVエクスポート時に「ERROR 1045 (28000): Access denied for user 'username'@'localhost'」エラーが表示される
  17. Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT
  18. 1
  19. 1
  20. 1)
  21. 1
  22. 1
  23. 1
  24. 1
  25. 1
  26. 1
  27. 1
  28. 1
  29. 1
  30. 1
  31. 1
  32. 1
  33. 1"'`--
  34. 1
  35. 1
  36. 1
  37. 1
  38. 1
  39. 1
  40. 1
  41. 1
  42. 1
  43. 1)
  44. 1
  45. 1
  46. 1
  47. 1
  48. 1
  49. 1
  50. 1
  51. 1
  52. 1
  53. 1"'`--
  54. 1
  55. 1
  56. SQLSTATE[22007]: Invalid datetime format: 1366 Incorrect string value: ~