Node.js+Sequelize+MySQL场景下插入数据前如何通过TransID校验重复
交易去重入库最优实现方案
1. 先加数据库唯一约束(必备兜底)
首先要在交易表的TransID字段上创建唯一索引,这是底层防重的核心,哪怕应用层校验出现并发漏判,数据库也会直接拒绝重复数据写入。
在Sequelize的交易模型中直接配置即可,同步表结构时会自动生成唯一索引:
const { DataTypes } = require('sequelize'); const Transaction = sequelize.define('Transaction', { TransID: { type: DataTypes.STRING, allowNull: false, unique: true // 核心配置,自动创建唯一索引 }, TransTime: DataTypes.DATE, Amount: DataTypes.DECIMAL(10, 2), BillRefNumber: DataTypes.STRING, MSISDN: DataTypes.STRING, FirstName: DataTypes.STRING, MiddleName: DataTypes.STRING, LastName: DataTypes.STRING, ShortCode: DataTypes.STRING }, {});
2. 应用层用findOrCreate实现校验+插入逻辑
Sequelize内置的findOrCreate方法会先按指定条件查询记录,不存在时才执行插入,比手动写「查询+判断+插入」的逻辑更简洁,配合异常捕获可以覆盖所有场景:
const { Transaction } = require('./models/transaction'); async function processNewTrans(transData) { try { // 第一个返回值是记录实例,第二个是是否为新创建的布尔值 const [transRecord, isCreated] = await Transaction.findOrCreate({ where: { TransID: transData.TransID }, defaults: transData // 查不到时用来创建新记录的完整数据 }); if (isCreated) { // 新交易入库成功的后续逻辑 return { code: 0, msg: '交易入库成功', data: transRecord }; } else { // 交易已存在的处理逻辑 return { code: 1, msg: '交易已存在,无需重复入库', data: transRecord }; } } catch (err) { // 处理极端并发场景下的唯一约束冲突:两个相同TransID的请求同时查不到,同时插入触发报错 if (err.name === 'SequelizeUniqueConstraintError') { const existRecord = await Transaction.findOne({ where: { TransID: transData.TransID } }); return { code: 1, msg: '交易已存在,无需重复入库', data: existRecord }; } // 其他业务错误抛给上层处理 throw err; } } // 调用示例 processNewTrans(Data);
方案优势
- 双层防重保障:数据库唯一索引兜底+应用层校验,100%避免重复数据,高并发场景也不会出问题
- 性能损耗极低:
TransID有唯一索引,查询速度接近O(1),不会增加额外负担 - 代码易维护:用Sequelize原生方法,无需手写冗余的判断逻辑
可选高并发优化
如果你的交易接口QPS很高,可以额外加一层Redis缓存,把已入库的TransID缓存起来,设置合理的过期时间,请求进来先查缓存,存在直接返回跳过数据库操作,能进一步降低数据库压力。
内容的提问来源于stack exchange,提问作者Daniel Kariuki
相关产品推荐
相关产品推荐

