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

MySQL账号注册并行请求触发死锁的解决方法咨询

死锁原因确认

你的判断基本正确,本质是InnoDB的间隙锁(GAP Lock)机制:当使用FOR UPDATE查询不存在的行时,InnoDB不会加行锁,而是会给查询条件覆盖的索引范围加间隙锁,避免其他事务插入该范围的数据引发幻读。多个事务可以同时持有同一范围的间隙锁,当两个持锁事务同时尝试往该间隙插入数据时,会互相等待对方释放锁,最终触发死锁。

适配需求的解决方案

你需要保留前置校验逻辑、避免自增ID浪费的需求,优先推荐以下两种方案:

方案1:使用MySQL用户级锁按邮箱维度隔离(推荐)

利用MySQL自带的GET_LOCK()函数实现细粒度的分布式锁,完全避免间隙锁触发,也不会浪费自增ID:

  • 每个邮箱对应唯一的锁名称,只有拿到对应锁的请求才能进入注册逻辑
  • 锁是连接级别的,超时时间可自定义,不会长期占用资源

TypeORM代码改造示例:

public async registerUser(email: string, password: string, displayName?: string) {
  const connection = await getConnection();
  // 生成邮箱对应的唯一锁名
  const lockName = `user_reg_${email}`;
  // 尝试获取锁,最大等待时间设置为3秒,可根据业务调整
  const getLock = await connection.query(`SELECT GET_LOCK(?, 3) as lock_status`, [lockName]);
  if (getLock[0].lock_status !== 1) {
    throw new UserError('操作频繁,请稍后重试');
  }
  try {
    return await connection.transaction(async (manager) => {
      const hasAlreadyRegistered = await this.findUser(email, manager);
      if (hasAlreadyRegistered) throw new UserError('Email has already been registered.');

      const user = new User();
      user.email = email;
      user.displayName = displayName || randomBytes(8).toString('hex');
      user.strategies = [authentication];
      await manager.save(user);
      logger.silly('> Created user row.');

      return user;
    });
  } finally {
    // 无论事务成功失败,都释放锁
    await connection.query(`SELECT RELEASE_LOCK(?)`, [lockName]);
  }
}

方案2:调整事务隔离级别关闭间隙锁

如果不想引入用户级锁,可以将注册事务的隔离级别调整为读已提交(READ COMMITTED),该级别下InnoDB会禁用普通查询的间隙锁,从根源上避免本次死锁:

  • 注意该方案下并行请求同时注册同一邮箱时,第二个请求插入会触发唯一键冲突,你可以捕获该异常后返回已注册提示,实际生产中这种冲突概率极低,自增ID浪费的量级可以忽略
  • TypeORM中设置事务隔离级别示例:
await connection.transaction('READ COMMITTED', async (manager) => {
  // 原有逻辑不变
})
补充注意事项
  • 确保findUser方法确实加了悲观写锁,TypeORM中写法参考:
await manager.findOne(User, { where: { email }, lock: { mode: 'pessimistic_write' } });
  • 如果是极低并发场景,也可以临时用表锁替代,但不推荐高并发场景使用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 16:45:02