You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

代码说明

  1. 事务与行级锁:用transaction.atomic()包裹查询和保存操作,通过select_for_update()对同前缀员工记录加锁,确保当前事务完成前,其他请求无法修改或插入同前缀记录,避免并发生成重复ID。
  2. 重试机制:设置重试次数,遇到IntegrityError时自动重试生成新ID,处理极端冲突场景。
  3. 简化逻辑:移除原代码中冗余的self.email_id和重复赋值语句,简化前缀生成逻辑。

额外优化建议

  • 若需跨年延续序号,可调整前缀规则或单独维护全局序号序列;
  • 超高频并发场景下,可使用数据库自增序列单独维护序号部分,进一步降低冲突概率。

内容的提问来源于stack exchange,提问作者Gokulnath

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:25:01