Django中Employee模型emp_id重复生成问题求助
解决Django中并发创建员工时emp_id重复的问题
问题现象
并发创建员工记录时触发MySQL 1062重复键错误:
2023-03-04 12:38:20,500 - root - ERROR - post - 46 - 员工记录创建失败: (1062, "键 'employee_employee.emp_id' 存在重复条目 '23SNS0002'")
尽管emp_id字段已设置unique=True,自定义save方法在并发场景下仍会生成重复ID,导致保存失败。
问题根源
当前save方法先查询同前缀的最后一条记录、计算新ID再保存。高并发场景下,多个请求会同时执行查询操作,拿到相同的最后一个ID,进而生成完全一致的新emp_id,最终触发唯一键约束冲突。
修复方案
通过数据库事务+行级锁确保生成ID和保存操作的原子性,同时添加重试机制处理极端冲突情况,彻底避免重复ID问题。
修改后的模型代码:
from django.db import models, transaction, IntegrityError from django.utils import timezone class Employee(models.Model): emp_id = models.CharField(editable=False, unique=True, max_length=10, default=None) def save(self, *args, **kwargs): if not self.emp_id: max_retries = 3 # 设置重试次数 for attempt in range(max_retries): try: with transaction.atomic(): # 生成年份前缀 prefix = timezone.now().strftime('%y') + 'SNS' # 对同前缀记录加行级锁,防止并发修改 last_instance = self.__class__.objects.filter(emp_id__startswith=prefix).select_for_update().last() if last_instance: last_seq = int(last_instance.emp_id[-4:]) self.emp_id = f"{prefix}{last_seq + 1:04d}" else: self.emp_id = f"{prefix}0001" # 执行保存 super().save(*args, **kwargs) break # 成功保存则退出循环 except IntegrityError: # 遇到重复键错误时重试 if attempt == max_retries - 1: # 重试耗尽,抛出异常 raise else: super().save(*args, **kwargs)
代码说明
- 事务与行级锁:用
transaction.atomic()包裹查询和保存操作,通过select_for_update()对同前缀员工记录加锁,确保当前事务完成前,其他请求无法修改或插入同前缀记录,避免并发生成重复ID。 - 重试机制:设置重试次数,遇到
IntegrityError时自动重试生成新ID,处理极端冲突场景。 - 简化逻辑:移除原代码中冗余的
self.email_id和重复赋值语句,简化前缀生成逻辑。
额外优化建议
- 若需跨年延续序号,可调整前缀规则或单独维护全局序号序列;
- 超高频并发场景下,可使用数据库自增序列单独维护序号部分,进一步降低冲突概率。
内容的提问来源于stack exchange,提问作者Gokulnath
相关产品推荐
相关产品推荐

