Sequelize中使用findOrCreate批量处理时出现数据重复创建问题
解决Sequelize中findOrCreate并行执行导致重复创建数据的问题
你碰到的这个问题其实是并行操作引发的竞态条件,在批量处理包含重复值的数据时很容易出现,我来帮你拆解原因和解决方案:
问题根源
Promise.map会同时触发所有的findOrCreate请求,当两个针对name: 'Tope'的请求同时发起时,它们几乎在同一时间去数据库检查是否存在该记录:
- 第一个请求还没完成创建操作,第二个请求查询时会发现数据库里还没有这条记录
- 于是两个请求都会执行创建逻辑,最终导致重复数据,且三次
created都返回true
虽然findOrCreate本身是原子操作,但并行执行时的竞态会打破这个原子性。
解决方案
1. 先对数据去重(推荐)
先把数组里重复的name过滤掉,只保留唯一的条目,再进行批量处理,从根源上避免重复请求:
// 用Map去重,保留每个name的第一个出现项 const uniqueData = [...new Map(data.map(item => [item.name, item])).values()]; models.sequelize.Promise.map(uniqueData, pValue => { return models.User.findOrCreate({ where: { name: pValue.name }, defaults: { name: pValue.name, organization_id: req.user.org_id } }).then(result => { const [resultData, created] = result; console.log(created); console.log(resultData.get()); }); });
2. 改用串行执行
如果需要保留原数组的所有条目(即使重复),可以用Promise.each代替Promise.map,它会在前一个操作完成后再执行下一个,彻底避免竞态:
models.sequelize.Promise.each(data, pValue => { return models.User.findOrCreate({ where: { name: pValue.name }, defaults: { name: pValue.name, organization_id: req.user.org_id } }).then(result => { const [resultData, created] = result; console.log(created); console.log(resultData.get()); }); });
3. 数据库层面加唯一约束(兜底)
为了从根本上防止重复数据,建议在User模型的name字段(或者结合organization_id,根据你的业务需求)添加唯一约束:
// 在User模型定义中 name: { type: DataTypes.STRING, allowNull: false, // 如果是单字段唯一 unique: true, // 如果是和organization_id联合唯一(同一个组织下name不能重复) // unique: ['organization_id', 'name'] }
这样即使出现竞态,数据库也会抛出错误阻止重复插入,保证数据一致性。
内容的提问来源于stack exchange,提问作者Adeyemi Kayode
相关产品推荐
相关产品推荐

