Sequelize事务运行异常,findOne无法获取最新数据是否需要加锁?
问题解答
事务执行逻辑说明
Sequelize不会自动对事务做串行处理,每次调用sequelize.transaction()生成的都是独立的数据库事务,多个事务会并行在数据库层执行,不存在默认串行的逻辑。
问题根因
你遇到的findOne读取不到最新值的问题,是典型的并发事务读写竞争问题:默认的事务隔离级别下,并行执行的事务无法读取到其他事务未提交的修改,多个请求同时发起查询时,会同时拿到同一个还没满员的房间,后续都往这个房间加人,最终导致人数超出上限。
修复方案
不需要引入额外的分布式锁,直接用Sequelize自带的查询行级锁即可解决,修改你代码中的findOne查询逻辑,加排他锁阻塞其他并行事务的查询,直到当前事务提交后其他事务才能读取最新数据:
// 首先从sequelize引入Transaction const { Transaction } = require('sequelize'); const test_create_transaction = async (serviceID, res) => { try { return sequelize.transaction(async (t) => { let serviceRoom; // Find a service room with less than a certain number of customers serviceRoom = await ServiceRooms.findOne({ where: { serviceID, numberCustomers: { [Op.lt]: MAX_CUSTOMERS_PER_ROOM, }, }, transaction: t, // 新增行级更新锁,当前事务释放前其他事务无法修改或查询匹配的记录 lock: Transaction.LOCK.UPDATE, }); // If no such room exists, create a new room if (!serviceRoom) { serviceRoom = await ServiceRooms.create( { serviceID, }, { transaction: t } ); } const testUserUID = "testUID" + Date.now().toString(); const { serviceRoomUID } = serviceRoom; // Add the customer to that room await ServiceRoomEnrollments.create( { serviceRoomUID: serviceRoomUID, userUID: testUserUID, serviceID: serviceID, }, { transaction: t } ); // Increment the number of customers in that room await ServiceRooms.increment("numberCustomers", { by: 1, transaction: t, where: { serviceRoomUID: serviceRoomUID, }, }); return res.send("test"); }); } catch (error) { console.log("error: ", error.message); res.end(); } };
额外优化建议
- 可以在数据库层为
ServiceRooms表的numberCustomers字段添加检查约束,确保数值永远不会超过MAX_CUSTOMERS_PER_ROOM,作为兜底校验避免代码逻辑漏洞 - 如果业务并发量极高,也可以考虑将房间分配逻辑放到数据库存储过程中执行,进一步降低网络交互带来的竞争窗口
内容的提问来源于stack exchange,提问作者dev_jeo
相关产品推荐
相关产品推荐

