执行php artisan migrate创建索引时触发SQLSTATE[HY000] [2002]连接拒绝错误
问题现象
在全新环境执行php artisan migrate命令时,多数数据表可正常创建,但遇到包含特定联合索引的迁移文件时会报错,错误信息开头为:
SQLSTATE[HY000] [2002] Connection refused (Connection: mysql, SQL: alter table
注释掉迁移代码中的$table->index(['item_id', 'is_active']);行后,迁移可正常完成。
引发错误的迁移代码
Schema::create('item_data', function (Blueprint $table) { $table->engine = 'InnoDB'; $table->charset = 'utf8mb4'; $table->collation = 'utf8mb4_general_ci'; $table->id(); $table->bigInteger('item_id', false, true); $table->bigInteger('section_element_id', false, true); $table->text('content'); $table->enum('is_active', DefaultOption::justKeys())->default(DefaultOption::YES->value); $table->dateTime('created_at'); $table->bigInteger('created_by',false, true)->nullable(); $table->dateTime('updated_at')->nullable(); $table->bigInteger('updated_by',false, true)->nullable(); $table->foreign('item_id')->references('id')->on('item')->onUpdate('no action')->onDelete('cascade'); $table->foreign('section_element_id')->references('id')->on('section_element')->onUpdate('no action')->onDelete('cascade'); $table->index(['item_id', 'is_active']); // 注释此行后迁移正常 $table->foreign('created_by')->references('id')->on('users')->onUpdate('no action')->onDelete('set null'); $table->foreign('updated_by')->references('id')->on('users')->onUpdate('no action')->onDelete('set null'); });
模拟执行的SQL语句
执行php artisan migrate --path=/database/migrations/2023_08_07_000006_item_data_create.php --pretend得到实际执行的SQL:
create table `item_data` (`id` bigint unsigned not null auto_increment primary key, `item_id` bigint unsigned not null, `section_element_id` bigint unsigned not null, `content` text not null, `is_active` enum('yes', 'no') not null default 'yes', `created_at` datetime not null, `created_by` bigint unsigned null, `updated_at` datetime null, `updated_by` bigint unsigned null) default character set utf8mb4 collate 'utf8mb4_general_ci' engine = InnoDB; alter table `item_data` add constraint `item_data_item_id_foreign` foreign key (`item_id`) references `item` (`id`) on delete cascade on update no action; alter table `item_data` add constraint `item_data_section_element_id_foreign` foreign key (`section_element_id`) references `section_element` (`id`) on delete cascade on update no action; alter table `item_data` add index `item_data_item_id_is_active_index`(`item_id`, `is_active`); alter table `item_data` add constraint `item_data_created_by_foreign` foreign key (`created_by`) references `users` (`id`) on delete set null on update no action; alter table `item_data` add constraint `item_data_updated_by_foreign` foreign key (`updated_by`) references `users` (`id`) on delete set null on update no action;
额外测试结果
直接在数据库中执行上述SQL时,同样在执行alter table item_dataadd indexitem_data_item_id_is_active_index(item_id, is_active);步骤报错;但先创建表(跳过索引语句),之后单独执行该索引语句则无任何错误。
问题原因分析
InnoDB连续ALTER操作的锁与连接问题
InnoDB表执行ALTER操作时会获取表级锁,短时间内连续执行多个ALTER(外键创建、索引创建)时,可能因锁等待或资源占用导致数据库连接短暂中断,在资源有限的开发环境(如轻量Docker容器、本地低配置数据库)中更容易触发。枚举字段联合索引的隐式处理开销
联合索引包含is_active枚举字段,MySQL在创建表的同时处理包含枚举的联合索引时,存在额外的隐式类型校验或索引构建逻辑,导致操作耗时超出数据库连接超时阈值,被数据库主动断开连接。数据库连接超时配置限制
若数据库的wait_timeout或interactive_timeout参数设置过短,连续执行ALTER操作的间隙可能触发连接超时,导致连接被拒绝。
验证与解决方向
- 检查数据库配置,适当调大
wait_timeout和interactive_timeout的值(例如设置为300秒) - 将联合索引的创建逻辑拆分到单独的迁移文件中,与表创建操作分开执行
- 调整迁移代码中索引与外键的定义顺序,尝试先定义索引再定义外键
内容的提问来源于stack exchange,提问作者Gabriel

