Laravel中使用save()操作数据库时出现重复数据及死锁问题求助
问题解决方案
一、解决重复Item记录问题
重复插入的核心原因是并发场景下,多个事务同时执行Item的插入操作,且未做唯一性校验,需从代码+数据库两层做防护:
- 数据库层面:添加唯一约束
给items表的field字段添加唯一索引(假设field是业务上的唯一标识),从底层阻止重复数据插入:
ALTER TABLE items ADD UNIQUE INDEX idx_items_field (field);
- 代码层面:使用
firstOrCreate替代直接新建保存
Laravel的firstOrCreate会先按条件查询,不存在则创建,内部会处理并发场景的锁竞争,避免重复插入:
public function saveData($data1, $data2) { DB::beginTransaction(); try { // 按field查询,不存在则创建,确保相同data1仅生成一条Item $item = Item::firstOrCreate( ['field' => $data1], // 唯一匹配条件 ['field' => $data1] // 插入时的字段值 ); $related = new Related; $related->item_id = $item->id; $related->field = $data2; $related->save(); DB::commit(); } catch (\Throwable $e) { DB::rollBack(); throw $e; } return $item; }
二、解决Related插入时的死锁问题
死锁通常源于锁竞争顺序不一致或缺失索引导致全表锁,可通过以下方式解决:
- 给Related表添加索引
确保relateds表的item_id字段存在普通索引,避免插入时触发全表扫描加表锁:
ALTER TABLE relateds ADD INDEX idx_relateds_item_id (item_id);
如果业务上item_id和field的组合是唯一的,建议添加联合唯一索引,进一步减少锁冲突:
ALTER TABLE relateds ADD UNIQUE INDEX idx_relateds_item_field (item_id, field);
- 改用
updateOrCreate避免重复关联数据
若不允许同一Item下存在相同field的Related,用updateOrCreate替代直接新建,减少不必要的插入操作,降低锁竞争:
// 替换原有的Related新建保存代码 $related = Related::updateOrCreate( ['item_id' => $item->id, 'field' => $data2], // 唯一匹配条件 ['item_id' => $item->id, 'field' => $data2] // 插入/更新的字段值 );
- 缩短事务执行时间
确保事务内仅包含必要操作,避免耗时逻辑,减少锁的持有时间,降低死锁概率。
内容的提问来源于stack exchange,提问作者meowmeow
相关产品推荐
相关产品推荐

