MySQL Laravel 迁移:即使已指定,唯一键也太长了
近期在 Laravel 项目中使用 MySQL 数据迁移时,遇到了一个让人困惑的问题:即使在迁移中显式指定了唯一键的长度,MySQL 仍然会报告唯一键太长的错误。
阅读更多:MySQL 教程
问题描述
在进行 Laravel 数据迁移时,代码类似如下:
public function up()
{
Schema::table('users', function (Blueprint table) {table->string('name')->unique();
});
}
如上代码所示,创建了 users
数据表,并为 name
字段添加了唯一键约束。然而,执行 php artisan migrate
命令时,在 MySQL 中报错:
Specified key was too long; max key length is 1000 bytes (SQL: alter table `users` add unique `users_name_unique`(`name`))
但是,字符数并没有超限。于是,首先解决问题需要先了解唯一键的长度限制和字符集的关系。
字符集与唯一键长度限制
MySQL 数据库为了节省存储空间,采用了可变长度的字符编码,例如 utf8mb4
。在 utf8mb4
编码下,每个字符最多占用 4 个字节,因此唯一键的长度限制是以字节为单位的。MySQL 在执行添加索引语句时,会将整个唯一键的字符串按照字节长度计算,若长度超过限制,则会报错。根据 MySQL 的文档,InnoDB 存储引擎中,唯一键的最大长度为 3072 字节,即字符数为 766。
但是,在 Laravel 5.8 中,string()
方法默认使用的是长度为 255 的 varchar
类型,所以当 key
的字符串长度超过 1000 时,MySQL 报错。
解决方法
一种解决方法是在每次创建数据表字段时,显式地指定该字段的长度和字符集。例如:
$table->string('name', 191)->unique();
在字符集为 utf8mb4
的情况下,长度为 191 的字符数最多为 47,符合唯一键长度的限制。
另一种更为简单的方法是,在 AppServiceProvider.php
中,为所有默认的字符串长度设置成 191:
use Illuminate\Support\Facades\Schema;
public function boot()
{
Schema::defaultStringLength(191);
}
这样,在使用 string()
方法创建新字段时,可以省略长度参数。如下:
$table->string('name')->unique();
这样创建的唯一键长度就会自动被限制在 191。
总结
Laravel 5.8 版本中默认使用的是 varchar(255)
字符串,当 MySQL 字符集采用 utf8mb4
编码时,每个字符会占用 4 个字节,且唯一键长度的限制是字节长度,因此当唯一键所对应字符串的长度超过 1000 时,MySQL 会报错。为此,在迁移时应特别关注唯一键的限制,采用上述两种方法之一即可解决问题。