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

Express API中TypeORM.save()并发调用重复插入问题求解

解决TypeORM并发Save主键冲突的方案

针对你遇到的并发请求下save()因主键重复报错的问题,有几个靠谱的解决方案,不用搞延迟请求这种临时方案:

1. 用TypeORM的upsert替代save(推荐)

TypeORM提供了专门的upsert方法,直接告诉数据库:如果主键存在就更新,不存在就插入,从根源上避免冲突。

调用方式很简单,把原来的save换成:

await getManager().upsert(User, userEntity, ['id']);

第三个参数['id']指定用id作为冲突判断的唯一键,TypeORM会根据你使用的数据库自动生成对应的SQL(比如MySQL的INSERT ... ON DUPLICATE KEY UPDATE,PostgreSQL的INSERT ... ON CONFLICT (id) DO UPDATE)。不管多少并发请求,数据库都会原子性处理冲突,不会报错。

2. 数据库悲观锁控制并发查询

如果必须保留save逻辑,可以在查询用户是否存在的时候加悲观写锁,让第二个请求等待第一个请求完成后再执行:

// 查询时加悲观写锁
const existingUser = await getManager().findOne(User, {
  where: { id: userId },
  lock: { mode: 'pessimistic_write' }
});

if (!existingUser) {
  // 从外部服务获取数据并创建实体
  const user = new User();
  user.id = userId;
  // ...赋值其他字段
  await getManager().save(user);
}

第一个请求拿到锁后,后续请求会等待锁释放,确保只有第一个请求会执行插入操作,后续请求直接读取已存在的用户。缺点是会增加请求等待时间,适合对一致性要求高但并发量不大的场景。

3. 应用层加内存锁避免重复逻辑

在getUser函数里加一个内存级别的锁,让相同userId的并发请求复用同一个异步操作,既避免重复调用外部服务,也避免并发插入:

// 用Map存正在处理的用户请求Promise
const pendingUserRequests = new Map<string, Promise<User>>();

async function getUser(userId: string): Promise<User> {
  // 如果已有相同userId的请求在处理,直接复用其Promise
  if (pendingUserRequests.has(userId)) {
    return pendingUserRequests.get(userId)!;
  }

  const requestPromise = (async () => {
    try {
      let user = await getManager().findOne(User, { id: userId });
      if (user) return user;

      // 从外部服务获取数据
      const externalData = await fetchUserFromExternalService(userId);
      user = new User();
      user.id = userId;
      // ...赋值其他字段

      // 这里也可以用upsert进一步保险
      await getManager().upsert(User, user, ['id']);
      return user;
    } finally {
      // 处理完成后移除锁
      pendingUserRequests.delete(userId);
    }
  })();

  pendingUserRequests.set(userId, requestPromise);
  return requestPromise;
}

这个方案在应用层拦截了重复请求,既减少了外部服务调用次数,也避免了数据库层面的冲突,性能最优,但要注意如果应用是多实例部署,内存锁只在单个实例生效,这种情况还是得结合数据库层面的方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 11:40:31