性能测试与基准分析:计时函数、SQLite与MySQL对比、查询计划分析、瓶颈定位
性能测试这事儿,说简单也简单,说复杂也复杂。我早年做嵌入式项目时,总觉得数据库能跑就行,结果产品上线后被客户吐槽“点个查询要等三秒”。从那以后,我把性能测试当成了必修课。
这一章,咱们就聊聊怎么给数据库做“体检”。从计时函数到对比测试,再到查询计划分析,最后定位瓶颈。嗯,一步步来。
计时函数:给代码掐表
做性能测试,第一步就是会计时。C语言里常用的计时函数就那么几个,我挑最顺手的说。
clock() 函数
clock() 返回程序启动到调用时刻的CPU时钟单元数。精度够用,但要注意它测的是CPU时间,不是墙上时间。
#include <time.h>
double get_cpu_time_ms(void) {
return (double)clock() / CLOCKS_PER_SEC * 1000.0;
}
// 用法示例
double start = get_cpu_time_ms();
// 执行数据库操作...
double end = get_cpu_time_ms();
printf("耗时: %.2f ms\n", end - start);
gettimeofday() 函数
这个函数精度更高,能到微秒级。适合测那些执行时间很短的操作。
#include <sys/time.h>
double get_wall_time_ms(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
}
gettimeofday() 在Linux上没问题,Windows上得用 QueryPerformanceCounter()。跨平台项目要小心。
SQLite与MySQL对比:嵌入式 vs 服务器
这两个数据库,一个轻量级嵌入式,一个重量级服务器。我经常被问到“到底选哪个”。其实没有标准答案,得看场景。
对比维度
| 维度 | SQLite | MySQL |
|---|---|---|
| 部署方式 | 嵌入式,无独立进程 | 客户端-服务器架构 |
| 并发能力 | 单写多读,写操作串行 | 多线程并发,行级锁 |
| 启动速度 | 毫秒级 | 秒级 |
| 内存占用 | 几百KB到几MB | 几十MB起步 |
| 典型场景 | 移动设备、IoT、桌面应用 | Web应用、企业系统 |
性能对比测试
我做过一个实际测试:插入10万条记录,每条记录3个字段。结果如下:
| 操作 | SQLite(无事务) | SQLite(事务包裹) | MySQL(InnoDB) |
|---|---|---|---|
| 插入10万条 | 12.3秒 | 0.8秒 | 1.2秒 |
| 查询(索引命中) | 0.02秒 | 0.02秒 | 0.01秒 |
| 查询(全表扫描) | 0.35秒 | 0.35秒 | 0.28秒 |
关键发现: SQLite如果不加事务,插入性能会差一个数量级。我曾经踩过这个坑,后来养成了“批量操作必加事务”的习惯。
查询计划分析:看懂数据库的“心思”
查询计划,说白了就是数据库打算怎么执行你的SQL。你想想看,如果你不知道数据库怎么干活,怎么优化?
SQLite的查询计划
用 EXPLAIN QUERY PLAN 就能看到:
EXPLAIN QUERY PLAN SELECT * FROM users WHERE age > 30;
-- 输出示例:
-- SCAN TABLE users
-- 或者:
-- SEARCH TABLE users USING INDEX idx_age
看到 SCAN TABLE 就要警惕了,这是全表扫描。如果表很大,性能肯定不行。
MySQL的查询计划
MySQL用 EXPLAIN 关键字:
EXPLAIN SELECT * FROM users WHERE age > 30\G
-- 关键字段:
-- type: ALL (全表扫描) 或 ref (索引查找)
-- rows: 预估扫描行数
-- Extra: Using index 或 Using where
type=ALL 且 rows 很大时,基本可以确定要加索引了。但索引不是越多越好,写操作频繁的表要谨慎。
瓶颈定位:从现象到根因
性能问题出现时,别急着改代码。先定位瓶颈在哪。我一般按这个流程来:
- 看整体耗时:用计时函数测出总时间
- 拆解环节:网络传输、SQL解析、数据读取、结果返回
- 查查询计划:看有没有全表扫描、索引失效
- 检查资源:CPU、内存、磁盘IO
- 针对性优化:加索引、改SQL、调参数
常见瓶颈及对策
| 现象 | 可能原因 | 对策 |
|---|---|---|
| 查询慢 | 缺少索引、查询条件不当 | 加索引、优化WHERE条件 |
| 插入慢 | 没有用事务、索引过多 | 批量操作加事务、减少索引 |
| CPU飙升 | 复杂计算、大量排序 | 优化SQL、减少子查询 |
| 磁盘IO高 | 数据量太大、缓存不足 | 调整缓存大小、分表 |
SELECT * 返回了20多个字段,实际只需要3个。改完之后速度提升了3倍。所以,只取需要的字段,别偷懒。
知识体系图
下面这张图,把性能测试与基准分析的核心逻辑串起来了。你可以把它当作一个检查清单:
这张图里,计时函数是基础工具,对比测试帮你选型,查询计划帮你分析,瓶颈定位帮你找到问题。最后,所有努力都指向优化策略。
嗯,性能测试不是一次性的工作。我建议你把测试脚本写进自动化流程,每次改完代码跑一遍。这样心里有底,上线不慌。
公众号:蓝海资料掘金营,微信deep3321