一、为什么要做数据库压测?

想象你开了一家网红餐厅,开业当天突然涌进来500个客人,结果厨房瘫痪、服务员手忙脚乱。数据库就像这个厨房,压测就是提前模拟"客流高峰",看看系统什么时候会"翻台"。

真实案例:某电商去年双十一前没做压测,大促时订单库响应延迟高达15秒,直接损失300万订单。通过压测可以:

  1. 发现性能瓶颈(比如CPU跑满/内存泄漏)
  2. 确定最大承载量(就像知道餐厅最多能接待多少桌)
  3. 验证优化效果(索引优化后性能提升多少)

二、PostgreSQL压测工具箱

2.1 内置武器:pgbench

这是PostgreSQL自带的"跑步机",可以模拟简单业务场景:

-- 技术栈:PostgreSQL 14
-- 初始化测试数据(生成10万账户的测试库)
pgbench -i -s 10 -U test_user test_db

-- 执行压测(100个并发连接,持续60秒)
pgbench -c 100 -T 60 -U test_user test_db

参数说明:

  • -s 10:缩放因子,每个因子约等于10万条记录
  • -c 100:模拟100个客户端同时操作
  • -T 60:持续运行60秒

2.2 高级装备:自定义测试脚本

真实业务往往更复杂,可以自己编写测试脚本:

-- 技术栈:PostgreSQL 14
-- custom_benchmark.sql
\set uid random(1, 100000)  -- 随机选择用户ID
BEGIN;
  UPDATE accounts SET balance = balance + 1 WHERE user_id = :uid;
  INSERT INTO transactions VALUES (:uid, now(), 1);
COMMIT;

执行命令:

pgbench -c 50 -T 120 -f custom_benchmark.sql test_db

三、实战压测五步法

3.1 环境准备

就像运动员热身,需要:

  • 关闭其他应用服务
  • 记录初始硬件状态(top命令看CPU/内存)
  • 备份数据库(防止测试数据污染)
# 记录初始状态
vmstat 1 60 > baseline.log &
pg_stat_activity > pg_before.log

3.2 渐进式加压

不要一上来就"暴力测试",要像汽车转速表那样逐步升档:

  1. 先用10个并发测基础性能
  2. 每次增加20%并发数
  3. 记录每次的TPS(每秒事务数)变化
for clients in 10 30 50 80 100; do
  pgbench -c $clients -T 30 test_db | tee log_$clients.txt
done

3.3 监控关键指标

这些指标就像汽车的仪表盘:

  • CPU使用率:超过70%就要警惕
  • 磁盘IOiostat查看await值(超过10ms说明磁盘忙)
  • 锁等待SELECT * FROM pg_locks查看锁冲突

3.4 结果分析

典型性能曲线有三种情况:

  1. 理想型:TPS随并发线性增长
  2. 瓶颈型:达到某个点后TPS不再上升
  3. 崩溃型:超过阈值后性能断崖下跌

3.5 优化验证

假设发现索引缺失导致慢查询:

-- 优化前(全表扫描)
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 12345;

-- 添加索引后对比
CREATE INDEX idx_orders_user ON orders(user_id);
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 12345;

四、避坑指南

4.1 测试数据陷阱

不要用1万条数据测100万条数据的性能!数据量要接近生产环境:

-- 生成百万级测试数据
INSERT INTO users 
SELECT generate_series(1,1000000), 
       md5(random()::text), 
       random()*1000;

4.2 网络延迟干扰

如果应用服务器和数据库分开部署,建议:

  1. 在数据库本机直接测试,排除网络因素
  2. 使用pingtraceroute检查网络质量

4.3 缓存预热

冷启动和热启动能差10倍性能!测试前先预热:

-- 手动加载常用表到内存
SELECT pg_prewarm('orders');
SELECT pg_prewarm('users');

五、进阶技巧

5.1 模拟混合读写

真实业务往往是读写混合的,可以这样设计:

-- 技术栈:PostgreSQL 14
\set read_prob 0.8  -- 80%概率是读操作
\set uid random(1, 1000000)

-- 读操作
SELECT * FROM products WHERE id = :uid;

-- 写操作
\if :read_prob < 0.8
  UPDATE products SET stock = stock - 1 WHERE id = :uid;
\endif

5.2 连接池测试

连接池就像餐厅的候客区,测试连接池大小的影响:

# 测试不同连接池配置
for pool_size in 20 50 100; do
  sed -i "s/max_connections = .*/max_connections = $pool_size/" postgresql.conf
  pg_ctl restart
  pgbench -c $((pool_size+10)) -T 60 test_db
done

六、总结与建议

经过完整压测后,你应该能回答这些问题:

  1. 系统能承受多少并发用户?
  2. 响应时间在多少并发时开始恶化?
  3. 硬件资源瓶颈在哪里?

最后给三个实用建议:

  1. 定期压测:就像汽车定期保养,建议每季度做一次
  2. 真实模拟:测试场景要尽量接近真实业务
  3. 留有余量:最大承载量按测试结果的70%来规划

记住:压测不是考试,而是体检。目的是发现问题而不是追求高分!