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

并发请求引发用户重复创建问题及解决方案咨询

并发场景下重复创建用户问题的解决方案

我们系统通过中间件实现「检查用户是否存在,不存在则创建新用户」的逻辑,但同一新用户的多并发请求会触发重复创建用户的问题。尝试提升事务隔离级别到REPEATABLE READ无效,使用SERIALIZABLE又出现死锁错误,相关代码如下:

async somefunction(usrId: number) {
        const t = await sequelize.transaction();
        try {
            const user = await usermodel.findOne({
                where: {
                    userId: usrId,
                },
                transaction: t,
            });

            if (!user) {
                const puraUser = await mainusermodel.findOne({
                    where: {
                        id: usrId,
                    },
                    transaction: t,
                });

                const dob = puraUser.dob;
                const age = dob
                    ? UserRepository.calculateAge(new Date(dob))
                    : 0;
                try {
                    const dbUser = await usermodel.create(
                        {
                            userId: usrId,
                            age,
                            streakDays: 0,
                        },
                        {
                            transaction: t,
                        }
                    );

                    await tblUserSettingsModel.create(
                        {
                            userId: dbUser.id,
                            lowerLimit: defaultUserSettings.lowerLimit,
                            upperLimit: defaultUserSettings.upperLimit,
                            unitId: defaultUserSettings.unitId,
                        },
                        {
                            transaction: t,
                        }
                    );

                    await t.commit();
                    return dbUser;
                } catch (error) {
                    if (error.name === 'SequelizeUniqueConstraintError') {
                        const dbUser = await tblUserDetailModel.findOne({
                            where: { userId: usrId },
                            transaction: t,
                        });
                        await t.commit();
                        return dbUser;
                    } else {
                        return null;
                    }
                }
            } else {
                await t.commit();
                return user;
            }
        } catch (err) {
            await t.rollback();
            logger.error(`CREATE_USER for userId ${usrId}`, err);
            return null;
        }
    }

可行解决方法

1. 唯一约束+重试机制优化

首先确认usermodel的userId字段已设置唯一约束(代码中已捕获SequelizeUniqueConstraintError,说明约束存在,但需确保生效)。并发时第一个请求创建成功后,后续请求会触发唯一约束错误,此时通过重试查询获取已创建的用户,避免事务隔离导致的不可见问题。

修改后代码示例:

async somefunction(usrId: number, retryCount = 3) {
    const t = await sequelize.transaction();
    try {
        const user = await usermodel.findOne({
            where: { userId: usrId },
            transaction: t,
        });

        if (!user) {
            const puraUser = await mainusermodel.findOne({
                where: { id: usrId },
                transaction: t,
            });

            if (!puraUser) {
                await t.rollback();
                return null;
            }

            const dob = puraUser.dob;
            const age = dob ? UserRepository.calculateAge(new Date(dob)) : 0;
            
            const dbUser = await usermodel.create(
                { userId: usrId, age, streakDays: 0 },
                { transaction: t }
            );

            await tblUserSettingsModel.create(
                {
                    userId: dbUser.id,
                    lowerLimit: defaultUserSettings.lowerLimit,
                    upperLimit: defaultUserSettings.upperLimit,
                    unitId: defaultUserSettings.unitId,
                },
                { transaction: t }
            );

            await t.commit();
            return dbUser;
        } else {
            await t.commit();
            return user;
        }
    } catch (err) {
        await t.rollback();
        if (err.name === 'SequelizeUniqueConstraintError' && retryCount > 0) {
            await new Promise(resolve => setTimeout(resolve, 100));
            return this.somefunction(usrId, retryCount - 1);
        }
        logger.error(`CREATE_USER for userId ${usrId}`, err);
        return null;
    }
}

2. 使用行锁(SELECT ... FOR UPDATE)

在查询用户是否存在时添加行锁,强制并发请求排队执行,避免多个请求同时进入创建分支。注意仅针对新用户查询加锁,减少对已有用户查询的性能影响。

修改查询部分:

const user = await usermodel.findOne({
    where: { userId: usrId },
    transaction: t,
    lock: t.LOCK.UPDATE, // 启用行锁
});

MySQL中SELECT ... FOR UPDATE会锁定匹配行,若行不存在则不锁,但结合唯一约束可有效避免并发创建。

3. 原子化创建(INSERT ... ON DUPLICATE KEY UPDATE,仅MySQL支持)

利用MySQL原生的INSERT ... ON DUPLICATE KEY UPDATE语法,跳过先查后写的步骤,让数据库原子性处理创建或查询操作,从根源避免并发问题。

示例代码:

async somefunction(usrId: number) {
    try {
        const puraUser = await mainusermodel.findOne({ where: { id: usrId } });
        if (!puraUser) return null;

        const dob = puraUser.dob;
        const age = dob ? UserRepository.calculateAge(new Date(dob)) : 0;

        // 原子性创建或查询用户,依赖userId的唯一约束
        const [dbUser, created] = await usermodel.findOrCreate({
            where: { userId: usrId },
            defaults: { age, streakDays: 0 },
        });

        if (created) {
            await tblUserSettingsModel.create({
                userId: dbUser.id,
                lowerLimit: defaultUserSettings.lowerLimit,
                upperLimit: defaultUserSettings.upperLimit,
                unitId: defaultUserSettings.unitId,
            });
        }

        return dbUser;
    } catch (err) {
        logger.error(`CREATE_USER for userId ${usrId}`, err);
        return null;
    }
}

关键注意事项

  • 唯一约束是核心:所有方案都依赖usermodel.userId的唯一索引/约束,必须确保其生效。
  • 缩小事务范围:将主用户查询移到事务外,减少锁持有时间,降低冲突概率。
  • 避免高隔离级别滥用:SERIALIZABLE会导致严重性能问题和死锁,优先用数据库原生原子操作替代。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 17:48:17