本文目录导读:

- 缓存(Buffer Pool/Cache)命中率的差异
- 锁与并发争抢的热点
- 索引选择与执行计划
- 写入模式的差异(Insert/Update/Delete)
- 数据倾斜(Join/Sort/Group By)的影响
- 一个具体的反例
这是一个非常专业且切中要害的问题,简单直接的回答是:因为真实SQL分布决定了数据库的“痛点”和“瓶颈”在哪里,使用均匀或假定的SQL分布进行压测,结果可能严重失真,误导容量规划和优化方向。
下面从几个核心维度来详细解释:
缓存(Buffer Pool/Cache)命中率的差异
数据库最核心的性能保障就是内存缓存,真实的SQL分布通常遵循二八原则或幂律分布(20%的热点数据被访问了80%的次数)。
- 真实分布: 大量的查询集中在少数“热”页或索引上,这些页面会长期驻留在Buffer Pool中,导致逻辑读(在内存中找到)的比例极高,压测出来的结果会很好看(高QPS、低延迟),但一旦热点偏移或数据量增长,性能可能断崖式下跌。
- 均匀分布: 如果压测工具随机生成SQL(Key均匀),它会随机访问大量不同的数据页,这意味着:
- Buffer Pool不断被冲刷:刚加载一个页面,查询下一个完全不同范围的Key,这个页面就被淘汰了。
- 物理读比例剧增:大部分请求都要走磁盘IO,性能会远低于真实情况。
- 结果误导:你可能会得出“这台机器性能不行”的结论,而实际上真实场景下它可能表现良好。
锁与并发争抢的热点
数据库的锁机制(行锁、间隙锁、意向锁等)在高并发下的行为,完全依赖于SQL的分布。
- 真实分布: 热点行/页会被大量并发事务同时访问和修改,这会产生严重的锁等待、死锁、热点页争抢,这通常是数据库的真实瓶颈所在(例如秒杀场景、社交媒体的点赞计数器)。
- 均匀分布: 每个事务修改的都是不同的行,锁冲突概率极低,压测出来的TPS(每秒事务数)可能远高于真实场景能支撑的TPS,让你误判系统的并发处理能力。
索引选择与执行计划
数据库优化器会根据SQL的查询条件和数据分布生成执行计划。
- 真实分布: 某些查询可能因为选择性极差(例如查询一个状态为“待审核”的稀有状态 vs 一个状态为“正常”的普遍状态),导致优化器选择全表扫描而不是索引扫描,压测必须包含这些“坏”SQL。
- 不均匀分布: 如果数据分布导致索引的某个范围非常倾斜(比如时间戳集中在最近一天),索引扫描虽然是范围扫描,但实际扫描的叶节点页数过多,性能会退化。
- 均匀分布: 压测工具生成的SQL通常选择性很好(
WHERE id = RANDOM()),总能走最优索引,这样测出的结果掩盖了真实场景中烂SQL、索引失效、索引碎片、统计信息过时等实际痛点。
写入模式的差异(Insert/Update/Delete)
- 真实分布:
- 写入热点: 消息表”中,所有数据都写入到表的末尾(时间戳递增),这会导致B+树的最右叶子节点成为热点页,产生严重的页分裂和Redo Log写入瓶颈。
- 写入倾斜: 更新某些特定类型的用户(如VIP用户),导致这些行的版本链(MVCC)过长,回滚段变大。
- 批量删除/归档:在非业务高峰期删除大量过期的数据(这会生成大量Binlog/Redo Log,并产生大事务)。
- 均匀分布: 随机写入多个Page,页分裂和IO是分散的,测不出“热点写入”这种典型的数据库压力模式。
数据倾斜(Join/Sort/Group By)的影响
- 真实分布: 一个用户可能有100万条订单,另一个用户只有1条订单,执行
JOIN、GROUP BY或ORDER BY时,这些极端行会消耗大量的临时表和排序内存。 - 均匀分布: 每个用户的订单数都差不多,Join的计算量均匀,排序所需的内存稳定,压测无法暴露真实场景中因数据倾斜导致的排序溢出到磁盘、临时表膨胀、内存不足等问题。
一个具体的反例
假设有一个 user_friend 表,有 user_id 和 friend_id 两列,真实场景中,每个用户的好友数量遵循幂律分布(几个网红有百万粉丝,大多数普通人只有几百好友)。
- 用均匀分布压测: 模拟每个用户随机查询好友列表(很快,因为有索引),结果:QPS 10万,延迟1ms,你觉得系统很强。
- 用真实分布压测: 查询网红A的100万好友列表(需要回表、大量IO),另一个查询也在执行
COUNT(*)统计所有好友(导致全表扫描),结果:QPS瞬间跌到2000,大量查询超时,这才是系统真实承受的压力。
| 维度 | 使用真实SQL分布 | 均匀/假想SQL分布 |
|---|---|---|
| 缓存 | 体现Buffer Pool在热点数据下的高命中率 | 导致Buffer Pool频繁刷新,制造不存在的磁盘IO |
| 锁争抢 | 暴露真实的热点行/页锁等待 | 大幅低估锁争抢导致的瓶颈 |
| 执行计划 | 暴露索引选择错误、全表扫描等真实问题 | 掩盖烂SQL,结果过分理想 |
| 写入模式 | 测出页分裂、Redo Log写满等写入瓶颈 | 写压力分散,掩盖写入热点 |
| 数据倾斜 | 暴露临时表、排序溢出等内存/磁盘问题 | 均匀数据使内存使用稳定,掩盖异常 |
压测的目的是复现真实场景下的极限和瓶颈,而非测试数据库处理“完美”随机请求的吞吐量,只有使用与生产环境一致的SQL流量比例、执行频率、数据分布和并发模型,压测结果才有实际参考价值,才能指导有效的性能优化(如调整索引、分库分表、使用缓存、优化SQL等),否则,压测就变成了一场“关公战秦琼”的测试。