Transactions 数据库事务处理 , MySQL and Sequelize

Transactions 数据库事务处理 , MySQL and Sequelize
最新回答
雾涣风月

2022-02-13 06:44:44

答案:在 MySQL 和 Sequelize 中,数据库事务处理是确保数据一致性的关键机制。Sequelize 提供了两种事务处理方案:非托管式(Unmanaged)托管式(Managed)。以下是具体实现及优化建议:

1. 非托管式事务(Unmanaged Transactions)

需手动控制事务的提交(commit)和回滚(rollback),适合需要精细控制流程的场景。

示例代码

const t = await sequelize.transaction();try { // 执行多个操作,传入事务对象 t const user = await User.create({ firstName: 'Bart', lastName: 'Simpson' }, { transaction: t }); await user.addSibling({ firstName: 'Lisa', lastName: 'Simpson' }, { transaction: t }); // 全部成功则提交 await t.commit();} catch (error) { // 任意失败则回滚 await t.rollback(); throw error; // 可选择抛出错误}

特点

  • 显式调用 commit() 或 rollback()。
  • 需自行处理错误传播。
2. 托管式事务(Managed Transactions)

通过回调函数自动处理提交/回滚,代码更简洁,推荐优先使用。

示例代码

try { const result = await sequelize.transaction(async (t) => { const user = await User.create( { firstName: 'Abraham', lastName: 'Lincoln' }, { transaction: t } ); await user.setShooter( { firstName: 'John', lastName: 'Boothe' }, { transaction: t } ); return user; // 回调函数的返回值即为 result }); // 事务已自动提交} catch (error) { // 事务已自动回滚 throw error;}

特点

  • 自动回滚:若回调函数抛出错误,Sequelize 会自动回滚。
  • 隐式提交:成功执行后自动提交,无需显式调用 commit()。
3. 订单支付场景优化

在订单支付成功后更新 Order 和 User 表时,托管事务可简化逻辑:

async function paidSuccess(outTradeNo, tradeNo, paidAt) { try { await sequelize.transaction(async (t) => { // 查询订单 const order = await Order.findOne({ where: { outTradeNo }, transaction: t }); // 避免重复处理 if (order.status > 0) return; // 更新订单状态 await order.update({ tradeNo, status: 1, paymentMethod: 0, paidAt }, { transaction: t }); // 更新用户会员信息 const user = await User.findByPk(order.userId, { transaction: t }); if (user.role === 0) user.role = 1; user.membershipExpiredAt = moment(user.membershipExpiredAt || new Date()) .add(order.membershipMonths, 'months') .toDate(); await user.save({ transaction: t }); }); } catch (error) { throw error; // 抛出错误供上层处理 }}

关键点

  • 原子性:所有操作(订单更新、用户信息修改)要么全部成功,要么全部回滚。
  • 幂等性:通过检查 order.status 避免重复处理。
  • 事务隔离:使用同一事务对象 t 确保操作一致性。
4. 事务并发控制

Sequelize 支持嵌套事务,但需注意默认行为(依赖 CLS 上下文):

sequelize.transaction((t1) => { return sequelize.transaction((t2) => { // 默认情况下,内部查询使用 t2 return Promise.all([ User.create({ name: 'Bob' }, { transaction: null }), // 显式禁用事务 User.create({ name: 'Mallory' }, { transaction: t1 }), // 指定外层事务 User.create({ name: 'John' }) // 默认使用 t2 ]); });});

注意事项

  • 嵌套事务默认继承最内层事务(需启用 CLS)。
  • 通过 transaction: null 可禁用事务。
  • 显式传递事务对象(如 t1)可覆盖默认行为。
5. 总结建议
  • 优先使用托管事务:减少手动管理错误的风险。
  • 保持事务短小:避免长时间占用连接,影响并发性能。
  • 明确错误处理:在顶层捕获并记录错误,确保回滚生效。
  • 复杂场景结合 CLS:如需跨函数共享事务,使用 cls-hooked 模块管理上下文。

通过合理使用 Sequelize 的事务机制,可以高效保障 MySQL 操作的原子性和数据一致性。