并发测试是如何产生锁、脏数据的

张开发
2026/4/23 4:50:06 15 分钟阅读

分享文章

并发测试是如何产生锁、脏数据的
结合数据库底层、事务隔离级别、并发场景用最直白好懂的逻辑一次性讲清并发测试为什么会造出锁、脏读、不可重复读、幻读、脏数据附带真实业务场景、SQL 过程、锁升级全过程。一、先搞懂前提并发是什么并发测试 多个线程 / 用户同时操作同一张表、同一行数据数据库默认事务 ACID为了保证数据不乱就必须加锁锁控制不好、隔离级别太低就直接出脏数据。简单因果链多线程同时改数据 → 数据库必须上锁防冲突 → 锁粒度 / 隔离级别不合适 → 脏读、不可重复读、幻读、数据覆盖错乱 → 脏数据二、并发为什么会产生【锁】1. 不加锁会发生什么假设两个事务同时修改同一行余额A余额 100要减 50B余额 100要减 50不加锁并行执行A 读取余额100B 同时读取余额100A 计算100-5050准备写回B 计算100-5050准备写回最后数据库余额变成50实际应该扣两次0这就是丢失更新属于最典型脏数据。2. 数据库为了解决上面问题自动加锁数据库规则同一行数据写操作必须排他只要有事务在修改、插入、删除数据库就自动上锁禁止其他事务同时修改。锁本质就是资源独占标记。常见锁分类并发测试必遇行锁InnoDB 默认只锁被修改的那一行并发好、阻塞少。表锁直接锁整张表其他所有读写都阻塞并发差。意向锁、共享锁 (S)、排他锁 (X)共享锁读加锁允许多个读禁止写排他锁写加锁禁止读、禁止其他写3. 并发压测时锁会越来越严重锁升级高并发下会出现大量单行行锁 → 数据库认为行锁太多开销大 →自动升级为表锁结果整个表被锁死所有接口超时、TPS 暴跌、服务雪崩。这就是并发压测经常测出来的锁等待、锁超时、数据库卡顿。三、并发如何产生【脏数据】三大经典问题 业务脏数据数据库标准三大并发问题全部由隔离级别太低 事务未提交导致。MySQL 默认隔离级别可重复读 RR仍挡不住幻读最低隔离级别读未提交 Read Uncommitted全问题暴露。1. 脏读Dirty Read—— 最典型脏数据过程事务 A 修改数据但还没提交事务 B 直接读到了 A 未提交的数据之后事务 A回滚修改作废事务 B 读到的数据就是无效、虚假脏数据例子转账A 给 B 转 100事务内更新余额未提交B 查询余额看到多了 100A 事务报错回滚余额恢复原样B 之前查到的数据就是脏数据2. 不可重复读Non-repeatable Read同一个事务内多次查询同一行结果不一样原因其他事务中途提交了更新、修改。侧重同一行数据被修改场景事务 B 第一次查余额100事务 A 修改余额为 200 并提交事务 B同个事务内再次查询变成 200两次结果不一致。3. 幻读Phantom Read同一个事务内前后查询记录条数不一样原因其他事务中途插入、删除数据。侧重记录行数增减场景事务 B 查询用户总数10 人事务 A 新增一个用户并提交事务 B 再次查询11 人像凭空多出来数据像幻觉一样。4. 业务真实脏数据开发最常踩坑压测必现不止数据库标准问题业务层面脏数据丢失更新前面余额例子多线程覆盖更新后执行的把先执行的结果冲掉。重复扣款、重复下单并发请求重复提交同一订单创建多条、余额重复扣减。库存超卖高并发减库存行锁没控制好库存减成负数。事务长 并发高 → 死锁A 锁 B 资源B 锁 A 资源互相等待数据库死锁接口永久阻塞。四、完整串起来并发测试全过程你做并发压测时整个链路是这样发生的JMeter/LoadRunner 起大量线程同时请求接口接口同时开启数据库事务执行 update/insertInnoDB 为防止数据错乱自动加行排他锁并发量越大并行事务越多锁竞争越激烈部分事务读取其他未提交事务的数据→脏读多个事务同时更新同一行 →丢失更新行锁数量过多 →锁升级为表锁数据库阻塞事务互相等待资源 →死锁超时回滚异常、数据错乱、余额不对、库存超卖 →最终脏数据五、一句话总结并发产生锁的原因多线程同时修改数据数据库为防止数据错乱强制加锁独占资源并发越高锁竞争越严重甚至锁升级、死锁。并发产生脏数据的原因隔离级别低读到未提交回滚的数据脏读事务交叉修改覆盖更新、丢失更新插入删除导致前后查询不一致幻读长事务 锁竞争导致异常回滚、数据错乱、业务超卖重复操作六、附带解决方案面试必背提高数据库隔离级别RR → Serializable加悲观锁select ... for update乐观锁版本号 version 控制更新业务加分布式锁Redis 锁防止重复请求缩短事务时长避免长事务锁占用索引优化避免无索引导致行锁升级表锁接口幂等设计防重复下单扣款

更多文章