ab适用于已封装为api的查询服务压测,不适用直连数据库;需监控db连接数、慢日志和cpu;python直连应使用连接池并捕获重试;pgbench更准但需初始化;关键指标是p95延迟、失败率和cpu使用率。
用 ab 模拟多窗口并发查询最直接
不用写脚本、不依赖编程语言,ab(apache bench)是验证数据库查询层并发能力的最快入口。它本质是发 http 请求,所以只适用于已封装成 api 的查询服务(比如一个 flask/fastapi 接口查 mysql)。如果你的数据库直连没走 web 层,ab 就不适用——别硬套。
常见错误现象:ab -n 1000 -c 100 http://localhost:8000/query 跑完发现 QPS 很高,但数据库连接池早爆了,日志里全是 Too many connections。这是因为 ab 只压接口,不感知后端资源水位。
- 必须提前在数据库侧开监控:比如 MySQL 看
SHOW STATUS LIKE 'Threads_connected',观察是否随并发线性上涨 -
-c值不是越大越好,建议从 20 开始,每次 +20,观察 DB 连接数、慢查询日志、CPU 是否突增 - URL 必须带真实参数,例如
http://l/h/q?id=123,否则缓存可能掩盖真实压力
Python 多线程直连 MySQL 容易踩连接复用坑
想绕过 Web 层直接测数据库?用 Python threading + pymysql 最常见,但默认每个线程新建连接,很快耗尽 max_connections。
使用场景:你有权限改应用代码,或能临时起一个测试脚本直连 DB;目标是看纯 SQL 查询在并发下的响应波动和失败率。
- 别在每个线程里调
pymysql.connect()—— 改用ThreadPoolExecutor配合连接池,如DBUtils.PooledDB - MySQL 默认
wait_timeout=28800(8 小时),但空闲连接被池管理器回收后,线程取到失效连接会抛OperationalError: (2013, 'Lost connection') - 务必捕获异常并重试一次,而不是让整个线程崩掉;重试前加
time.sleep(0.01)避免雪崩
PostgreSQL 用 pgbench 更准,但得先初始化测试表
pgbench 是 PostgreSQL 自带的压测工具,比通用工具更贴近真实数据库行为,因为它能执行自定义 SQL 脚本、支持事务控制、自动管理连接生命周期。
性能影响点:它默认跑的是 TPC-B 类型的混合读写,如果你只想测“纯 SELECT”,必须自己写脚本并用 -f 指定,否则结果完全不反映你的查询场景。
- 初始化命令是
pgbench -i -s 10 mydb,其中-s 10表示比例因子,数据量约百万级;不初始化就跑压测会报错relation "pgbench_accounts" does not exist - 写自定义脚本时,所有 SQL 必须以分号结尾,且不能含注释;例如
SELECT * FROM users WHERE id = :id;,变量用:id,由pgbench随机代入 - 并发数用
-c,总请求数用-t;注意-c 50不代表只开 50 连接——它会复用,实际连接数通常为-c的 1–2 倍
并发测试结果里最该盯住的三个数字
不是 QPS 越高越好,也不是平均响应时间越低越稳。真正决定系统能否上线的,是这三个指标的组合表现:
-
95th percentile latency(P95 延迟):如果 P95 是 200ms,但平均只有 50ms,说明有少量查询严重拖后腿,大概率是锁等待或磁盘 I/O 毛刺 - 失败请求占比:哪怕只有 0.3%,也要查日志——是
Lock wait timeout exceeded还是Out of memory?类型不同,优化路径完全不同 - 数据库 CPU 使用率是否贴着 100%:如果 CPU 没打满但 QPS 上不去,问题大概率在 IO 或锁,不是算力瓶颈
复杂点在于,这三个数永远互相牵制。调高连接数可能抬升 P95,降低并发数又让失败率归零但吞吐不足。没有银弹,只有反复换参数、看日志、比数字。










