タイトル: 1071 Specified key was too long; max key length is 767 bytes
SEOタイトル: Laravel migration「1071 Specified key was too long」の原因と 3
| この記事の要点 |
|
エラー内容
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) |
|---|---|---|---|
| latin1 | 1 byte | 255 byte | 191 byte |
| utf8 (3 byte UTF-8) | 3 byte | 765 byte ⚠️ | 573 byte |
| utf8mb4 (4 byte UTF-8) | 4 byte | 1020 byte ❌ | 764 byte ✅ |
MySQL 5.6 以前 / 古い InnoDB ではインデックスキー上限が 767 バイトのため、utf8mb4 で VARCHAR(255) は不可。
対処 1: Laravel で defaultStringLength(191)
Laravel 公式が推奨する最も簡単な方法:
<?php
// app/Providers/AppServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
// utf8mb4 + 旧 MySQL 用: 文字列カラムの既定長を 191 に
Schema::defaultStringLength(191);
}
}
これで $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 文字だけに絞る方法:
<?php
// Laravel migration
Schema::create('users', function (Blueprint $table) {
$table->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: 該当カラムだけ短くする
<?php
Schema::create('users', function (Blueprint $table) {
$table->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 bytes | DYNAMIC でも 3072 超過。さらに短くする必要 |
1709 Index column size too large | 同根の別表現(MySQL 5.6) |
1118 Row size too large | 1 行のサイズ超過。列を 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 だけで失敗するのはこのため。