性能测试与基准分析:计时函数、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);
我的习惯: 每次测试至少跑5次,取平均值。单次结果波动太大,容易误导。

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=ALLrows 很大时,基本可以确定要加索引了。但索引不是越多越好,写操作频繁的表要谨慎。

瓶颈定位:从现象到根因

性能问题出现时,别急着改代码。先定位瓶颈在哪。我一般按这个流程来:

  1. 看整体耗时:用计时函数测出总时间
  2. 拆解环节:网络传输、SQL解析、数据读取、结果返回
  3. 查查询计划:看有没有全表扫描、索引失效
  4. 检查资源:CPU、内存、磁盘IO
  5. 针对性优化:加索引、改SQL、调参数

常见瓶颈及对策

现象 可能原因 对策
查询慢 缺少索引、查询条件不当 加索引、优化WHERE条件
插入慢 没有用事务、索引过多 批量操作加事务、减少索引
CPU飙升 复杂计算、大量排序 优化SQL、减少子查询
磁盘IO高 数据量太大、缓存不足 调整缓存大小、分表
避坑指南: 我曾经遇到一个案例,查询慢是因为用了 SELECT * 返回了20多个字段,实际只需要3个。改完之后速度提升了3倍。所以,只取需要的字段,别偷懒。

知识体系图

下面这张图,把性能测试与基准分析的核心逻辑串起来了。你可以把它当作一个检查清单:

性能测试与基准分析 计时函数 clock() / gettimeofday() SQLite vs MySQL 插入/查询/并发对比 查询计划分析 EXPLAIN / EXPLAIN QUERY PLAN 瓶颈定位 CPU / IO / 索引 / SQL 优化策略 加索引 → 优化SQL → 调整参数 → 架构升级

这张图里,计时函数是基础工具,对比测试帮你选型,查询计划帮你分析,瓶颈定位帮你找到问题。最后,所有努力都指向优化策略。

嗯,性能测试不是一次性的工作。我建议你把测试脚本写进自动化流程,每次改完代码跑一遍。这样心里有底,上线不慌。


公众号:蓝海资料掘金营,微信deep3321