我的测试学习站TEST LEARNING HUB
开始学习
首页/精华文章/7. 数据库锁和并发问题测试怎么讲

7. 数据库锁和并发问题测试怎么讲?

数据库锁和并发问题,是区分普通功能测试和有质量意识测试的重要分水岭。很多业务在单用户操作时完全正常,一到多人同时操作就出问题,比如库存超卖、重复扣款、重复审批、优惠券重复使用、状态被覆盖。这类问题往往不是页面点点就能发现的,需要测试人员主动设计并发场景。

面试中问锁和并发,面试官通常不是想让你讲数据库锁的全部底层实现,而是想知道你有没有高风险业务意识:哪些场景容易并发出问题,怎么模拟并发,怎么判断数据是否异常,发现问题后怎么定位。测试工程师可以不深入到数据库内核,但必须能结合业务说清楚。

一、为什么会有并发问题

并发问题的本质是多个请求同时操作同一份或相关数据。如果系统没有做好锁、事务、幂等、状态校验,就可能出现数据错乱。

典型例子:商品库存只剩 1 件,两个用户同时提交订单。如果系统处理不当,两个订单都成功,库存变成 -1,这就是超卖。

再比如审批系统中,同一个单据同时被两个审批人点击通过,如果没有状态校验和并发控制,可能生成两条审批通过记录,或者后提交的人覆盖先提交的状态。

支付系统也常见:第三方支付回调可能重复通知,如果接口不幂等,就可能重复生成支付流水或重复发货。

二、测试中最常见的并发风险场景

1. 库存扣减

库存是并发测试最经典场景。关注点:库存不能为负、不能超卖、失败订单不能扣库存、取消订单是否释放库存。

2. 秒杀和抢购

大量用户抢少量商品,除了库存一致性,还要关注接口限流、排队、失败提示、响应时间。

3. 优惠券领取和使用

优惠券库存有限时,不能超发;同一张券不能被重复使用;支付失败后锁定券是否释放。

4. 支付回调

支付平台可能多次通知同一笔订单。系统必须保证重复回调不会重复改状态、重复发货、重复记账。

5. 审批流

多人同时审批同一节点,不能出现重复审批、状态回退、审批记录错乱。

6. 重复提交

用户连续点击提交按钮,或者前端重试,可能导致重复创建订单、重复提交工单、重复报名。

三、数据库锁怎么简单理解

测试面试不需要把锁讲成数据库课程,但要有基本概念。

行锁

锁住某一行数据,常用于更新库存、订单状态。比如更新某个商品库存时,只锁这条商品库存记录。

表锁

锁住整张表,粒度大,影响并发性能。一般业务系统应尽量避免不必要表锁。

悲观锁

假设并发冲突一定会发生,所以先加锁再操作。例如查询库存时加 for update,其他事务要等锁释放。

乐观锁

假设冲突不一定发生,更新时通过版本号或条件判断。例如库存扣减时加条件:

update t_stock
set stock = stock - 1
where product_id = 101
  and stock > 0;

如果影响行数为 0,说明库存不足或被别人抢先扣了。

幂等控制

严格说幂等不等于锁,但并发测试中经常一起出现。比如同一个支付回调通知多次,系统应该只处理一次。

四、怎么设计并发测试

1. 明确并发对象

先明确多个请求是否操作同一资源。比如同一商品库存、同一订单、同一优惠券、同一审批单。

2. 明确预期结果

并发测试必须先定义正确结果。例如库存 1,同时 10 人下单,最多只能成功 1 单,其余应失败,库存不能小于 0。

3. 准备初始数据

并发前要查清楚初始库存、订单状态、优惠券状态。

4. 发起并发请求

可以用 JMeter、Postman Runner、脚本、自动化框架,也可以让开发提供压测接口。重点是尽量同时发起请求。

5. 校验数据库结果

并发请求结束后,不能只看响应成功数,还要查数据库:成功订单数、库存剩余、流水数量、状态记录。

五、完整案例:库存超卖测试

假设商品库存为 1,10 个用户同时购买。

测试前:

select product_id, stock
from t_product_stock
where product_id = 101;

预期:库存是 1。

并发请求:10 个用户同时调用创建订单接口。

测试后查询:

select count(*)
from t_order
where product_id = 101
  and status in ('WAIT_PAY', 'PAID');

select product_id, stock
from t_product_stock
where product_id = 101;

正确结果:成功订单数最多 1,库存不能小于 0。

如果成功订单数大于 1,说明超卖;如果库存为 -1,也说明扣减逻辑有问题;如果所有请求都成功但库存没扣,说明库存校验缺失。

六、完整案例:支付回调重复通知

支付回调是实际项目高风险点。第三方支付平台可能因为网络原因重复通知同一笔支付结果。

测试步骤:

  1. 创建待支付订单;
  2. 模拟支付成功回调;
  3. 连续发送多次相同回调;
  4. 查询订单状态;
  5. 查询支付流水;
  6. 查询发货或权益发放记录。

预期:

  • 订单只从待支付变成已支付一次;
  • 成功支付流水只能有一条;
  • 权益只能发放一次;
  • 重复回调返回成功或已处理,但不能重复执行业务。

SQL 示例:

select order_no, status, pay_status
from t_order
where order_no = 'NO202604300001';

select count(*)
from t_pay_record
where order_no = 'NO202604300001'
  and pay_status = 'SUCCESS';

七、常见定位思路

并发问题出现后,不要只说“系统有 Bug”。要能定位方向。

1. 看请求时间线

确认多个请求是否真的同时到达,还是顺序执行。

2. 看响应成功数

统计成功和失败数量,判断是否超过业务预期。

3. 查数据库最终状态

看库存、订单、流水、审批记录是否一致。

4. 看日志

关注是否有重复处理、事务回滚、唯一键冲突、锁等待、超时。

5. 分析是否缺少控制

可能缺少库存扣减条件、唯一约束、状态校验、幂等表、版本号或锁。

八、面试回答模板

如果面试官问“数据库锁和并发问题怎么测试”,可以这样答:

我会先从业务风险场景入手,比如库存扣减、优惠券领取、支付回调、审批流和重复提交。这些场景多个请求会同时操作同一份数据,容易出现超卖、重复扣款、重复审批或状态覆盖。测试时我会先准备明确的初始数据,比如库存为 1,然后用 JMeter 或脚本并发发起多个请求,最后不只看接口响应,还会查数据库里的成功订单数、库存剩余、支付流水和状态记录。对于定位,我会结合请求时间线、服务日志和数据库结果判断是否缺少锁、事务、幂等或状态校验。

九、常见追问

追问:怎么测超卖?

设置库存为 1,多个用户并发下单,预期最多 1 个成功,库存不能小于 0,失败订单不能扣库存。

追问:悲观锁和乐观锁怎么理解?

悲观锁是先锁住再操作,适合冲突高场景;乐观锁是更新时通过版本号或条件判断是否冲突,适合读多写少或冲突不高场景。

追问:重复支付怎么测?

对同一订单多次发起支付或重复发送支付回调,验证只能生成一条成功流水,订单状态不能重复变更,权益不能重复发放。

十、练习清单

  1. 设计库存为 1 的并发下单测试;
  2. 设计优惠券库存为 1 的并发领取测试;
  3. 设计同一订单重复支付测试;
  4. 设计支付回调重复通知测试;
  5. 设计审批单重复审批测试;
  6. 设计连续点击提交按钮测试;
  7. 用 SQL 验证库存不能为负;
  8. 用 SQL 验证成功流水唯一;
  9. 用 JMeter 模拟 10 个并发请求;
  10. 准备一段并发问题定位话术。

锁和并发测试的核心不是会说几个锁名,而是能识别高风险业务,设计并发场景,查数据库验证结果,并能定位可能缺少的控制机制。这才是面试官真正想听的。

配套刷题: