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

MongoDB数据校验存储问题:重复数据未触发预期报错

问题排查:重复数据校验未触发报错

问题描述

尝试实现数据存储逻辑:先校验数据是否存在,若已存在则抛出错误,否则存储数据。现有代码包含重复的name与icc字段数据,但保存第一条记录后未触发预期的重复报错。

代码示例

let Records = [{
    name: 'test',
    icc: 'testId'
},
{
    name: 'test',
    icc: 'testId'
}]
await Promise.all(Records.map(async (item) => {
    try {
        item['id'] = id
        let ItemObj = table_name.build();
        item.name = item.name;
        item.icc = item.icc;
        let found = await table_name.findOne({ where: { name: item.name, id: id } })
        console.log("11111111111111111");
        if (found) {
            console.log("22222222222222222222");
            throw new Error(item.name + " is already exist!");
        } else {
            console.log("333333333333333333333333333333");
            Object.assign(ItemObj.dataValues, item);
            console.log("4444444444444444444")
            await ItemObj.save()
            console.log("5555555555555555555")
        }
    } catch (err) {
        throw new Error(err);
    }
})
).then((data) => {
    console.log("final");
}).catch((err) => {
    console.log("error logged");
})

控制台输出

11111111111111111
33333333333333333
44444444444444444

11111111111111111
33333333333333333
44444444444444444

55555555555555555
55555555555555555

问题原因及解决方案

核心问题:并发竞态条件

使用Promise.all同时处理两条记录时,两个校验查询几乎同时执行,此时第一条记录还未完成保存操作,第二条记录的查询自然找不到已存在的数据,最终两条记录都成功保存,无法触发重复校验逻辑。

解决方案

  1. 串行处理记录
    将并行处理改为串行遍历,确保前一条记录完成校验和保存后,再处理下一条,避免竞态条件:
for (const item of Records) {
    try {
        item['id'] = id;
        let ItemObj = table_name.build();
        let found = await table_name.findOne({ where: { name: item.name, id: id } });
        console.log("11111111111111111");
        if (found) {
            console.log("22222222222222222222");
            throw new Error(item.name + " 已存在!");
        } else {
            console.log("333333333333333333333333333333");
            Object.assign(ItemObj.dataValues, item);
            console.log("4444444444444444444");
            await ItemObj.save();
            console.log("5555555555555555555");
        }
    } catch (err) {
        throw new Error(err);
    }
}
  1. 数据库层面添加唯一约束
    这是最可靠的方案,直接在数据库中给name和id(或你需要的组合字段)添加唯一索引,即使代码层面出现并发问题,数据库也会直接抛出重复键错误,从根源避免重复数据:
  • 若使用MongoDB,执行以下命令创建复合唯一索引:
    db.collection.createIndex({ name: 1, id: 1 }, { unique: true })
    
  • 若使用Sequelize(从代码的build、findOne方法判断更接近关系型数据库操作),可在模型定义中添加复合唯一约束:
    name: {
        type: DataTypes.STRING,
        allowNull: false,
        unique: 'compositeIndex'
    },
    id: {
        type: DataTypes.STRING,
        allowNull: false,
        unique: 'compositeIndex'
    }
    
  1. 清理冗余代码
    代码中item.name = item.name;和item.icc = item.icc;属于无效冗余代码,可直接删除。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 11:26:52