6. 事务 ACID 怎么结合业务理解?
事务是数据库面试里最容易被背成口号的知识点。很多同学会说:事务有 ACID,分别是原子性、一致性、隔离性、持久性。这个答案只能算入门,面试官继续问“你在测试中怎么验证事务?”“支付失败后怎么判断有没有回滚?”“库存扣了但订单没生成是什么问题?”很多人就答不上来了。
测试工程师理解事务,不是为了背定义,而是为了验证复杂业务的一致性。真实业务中,很多操作不是只改一张表,而是一组动作:下单要生成订单、生成明细、扣库存、锁优惠券;支付要更新订单状态、生成支付流水、扣积分、通知发货;退款要更新订单、生成退款流水、释放库存或返还优惠券。事务的价值就是保证这些相关操作在关键场景下不会只成功一半。
一、事务解决的核心问题
事务解决的是“一组数据库操作的一致性问题”。简单说:该一起成功的必须一起成功,该一起失败的必须一起失败。
以电商下单为例,用户提交订单后,系统可能做这些操作:
- 在订单主表插入订单;
- 在订单明细表插入商品明细;
- 扣减商品库存;
- 锁定优惠券;
- 冻结积分;
- 写操作日志。
如果订单主表插入成功,但库存扣减失败,系统就会出现脏数据:用户看到订单,但库存没有变化。如果库存扣了但订单没生成,就更严重,用户没买到东西但库存少了。这些都是事务或补偿机制要解决的问题。
二、ACID 怎么结合业务理解
1. 原子性 Atomicity
原子性就是一组操作要么都成功,要么都失败。测试时最常见的验证点是异常中断。
例如创建订单时,如果订单明细插入失败,订单主表是否回滚?库存是否恢复?优惠券是否释放?
测试设计可以包括:
- 商品库存不足时下单;
- 优惠券已过期时下单;
- 支付接口中途异常;
- 模拟重复提交;
- 后端某一步抛异常后检查相关表。
2. 一致性 Consistency
一致性是指事务执行前后,数据要符合业务规则。比如支付成功后,订单状态必须是已支付,支付流水必须成功,支付金额必须等于订单金额。
测试时不要只看某一张表。比如订单状态是已支付,但支付流水失败,这就是不一致;库存不能为负数,优惠券不能既未使用又绑定订单,这些都属于一致性规则。
3. 隔离性 Isolation
隔离性解决并发情况下互相影响的问题。比如两个用户同时购买最后一件商品,不能都下单成功;两个审批人同时处理同一单据,不能出现重复通过;支付回调重复到达,不能生成两条成功流水。
测试隔离性时,经常要设计并发场景和重复请求场景。
4. 持久性 Durability
持久性是指事务提交后,数据应该可靠保存。测试环境中不太会直接验证数据库崩溃恢复,但可以从业务角度理解:支付成功后刷新页面、重新登录、重新查询接口,状态仍应保持已支付,而不是只存在于缓存或前端状态中。
三、事务测试的重点场景
1. 下单失败回滚
场景:用户选择商品和优惠券后提交订单,但优惠券已被别人使用或库存不足。
验证点:
- 订单主表是否未生成,或状态是否为失败;
- 库存是否没有扣减;
- 优惠券是否没有被锁定;
- 购物车状态是否符合需求;
- 页面提示是否准确。
2. 支付成功一致性
支付成功后要查:
select order_no, status, pay_status, total_amount
from t_order
where order_no = 'NO202604300001';
select pay_no, order_no, pay_amount, pay_status
from t_pay_record
where order_no = 'NO202604300001';
订单状态、支付流水、支付金额必须一致。
3. 支付失败回滚或保持待支付
支付失败时,订单不能变成已支付,流水不能是成功状态。如果生成失败流水,也要状态正确。
4. 重复支付幂等
用户多次点击支付,或第三方支付回调重复通知,系统不能重复扣款、不能生成多条成功流水、不能重复发货。
5. 退款事务
退款要关注订单状态、退款流水、支付流水、库存、优惠券、积分。退款失败时不能只改了订单状态却没生成退款流水。
四、事务和分布式业务的区别
面试时要注意,不是所有业务都靠单数据库事务解决。现在很多系统是微服务或异步架构,下单、支付、库存、优惠券可能在不同服务中。此时很难用一个本地事务包住所有操作,可能采用消息队列、最终一致性、补偿任务、状态机等方式。
测试回答时可以这样说:
如果是单体系统或同一个数据库内的操作,可以通过数据库事务保证强一致;如果是分布式系统,可能更多依赖最终一致性,比如消息队列、补偿任务和幂等控制。测试时我会根据架构确认是强一致还是最终一致,然后设计不同校验方式。
这个回答会比单纯背 ACID 更成熟。
五、怎么判断事务有没有问题
1. 查多张表是否一致
比如下单后查订单、明细、库存、优惠券。支付后查订单和流水。审批后查单据状态和审批记录。
2. 制造异常场景
正常流程通常不容易暴露事务问题,异常流程更关键。比如库存不足、支付失败、接口超时、重复提交。
3. 观察回滚结果
如果某一步失败,要检查前面已经写入的数据是否回滚,或者是否有失败状态记录。
4. 验证补偿任务
如果系统采用最终一致性,要确认补偿任务是否能修复中间状态,例如支付成功但订单状态暂未更新,后续是否能补偿成功。
六、完整案例:下单扣库存事务测试
假设商品库存为 1,用户提交订单后系统创建订单并扣库存。
测试用例可以这样设计:
- 查询商品初始库存;
- 用户 A 下单成功;
- 查询订单表是否生成订单;
- 查询订单明细是否正确;
- 查询库存是否从 1 变成 0;
- 用户 B 再次下单;
- 验证下单失败;
- 查询库存不能变成负数;
- 查询用户 B 是否生成异常订单;
- 如果生成失败订单,状态要符合需求。
SQL 示例:
select product_id, stock
from t_product_stock
where product_id = 101;
select order_no, status
from t_order
where user_id = 1001
order by create_time desc;
重点不是 SQL 多复杂,而是验证数据是否符合事务规则。
七、面试回答模板
如果面试官问“你怎么理解事务 ACID”,可以这样回答:
我不会只把事务理解成数据库概念,而是会结合业务一致性来看。比如下单场景里,创建订单、写订单明细、扣库存、锁优惠券应该作为一组操作处理,不能出现订单生成了但库存没扣,或者库存扣了但订单没生成。ACID 里原子性保证要么都成功要么都失败;一致性保证支付后订单状态和流水金额一致;隔离性用于处理并发抢库存、重复审批这类场景;持久性保证提交后的状态能被后续查询到。测试时我会重点覆盖异常中断、库存不足、支付失败、重复提交和并发操作,并通过查询多张表验证数据一致性。
八、常见追问
追问:事务测试重点测什么?
重点测异常流程、并发流程、重复请求和多表一致性。正常流程只能证明基本可用,不能证明事务健壮。
追问:订单状态变了但流水没生成是什么问题?
可能是事务边界不完整、异步消息失败、支付服务异常、补偿任务没执行或数据写入失败。要结合日志、消息队列和数据库状态定位。
追问:分布式系统怎么保证一致性?
可能通过最终一致性、消息队列、补偿任务、幂等控制、状态机等方式。测试要确认系统设计口径,不能只按单库事务理解。
九、练习清单
- 设计下单成功的数据校验点;
- 设计库存不足下单失败用例;
- 设计支付失败后订单状态校验;
- 设计重复支付测试;
- 设计退款失败测试;
- 设计优惠券锁定失败回滚测试;
- 设计审批失败回滚测试;
- 查询订单、流水、库存三张表一致性;
- 区分强一致和最终一致;
- 准备一段事务面试回答。
事务的核心不是背 ACID,而是发现和验证业务数据不一致。你能把事务讲到下单、支付、退款、库存、优惠券这些场景里,面试表现会明显更有项目感。
配套刷题:

