不能用benchmark()或unix_timestamp():benchmark()用于压测表达式重复执行次数,返回布尔值,不反映真实耗时;unix_timestamp()精度仅秒级,对毫秒级过程无效,差值恒为0。

MySQL存储过程内部无法直接获取语句级高精度耗时,NOW(6)是唯一可用的微秒级时间戳函数,但必须配合手动打点、避免事务干扰和并发覆盖才能得到可信结果。
为什么不能用BENCHMARK()或UNIX_TIMESTAMP()
BENCHMARK()不是计时器,它只用于压测表达式重复执行次数,返回布尔值,完全不反映真实耗时;UNIX_TIMESTAMP()只精确到秒,对毫秒级过程毫无意义,差值恒为0。误用会导致你“测了个寂寞”。
- 用
BENCHMARK(1, SLEEP(0.1))测耗时?它返回1,且实际执行时间远超0.1秒,不可控 - 用
UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(@start)?结果永远是整数秒,丢失全部亚秒精度 - 在事务中多次调用
NOW(6)?可能因MVCC快照或复制延迟导致前后值偏差,尤其在READ COMMITTED级别下
怎么用NOW(6)安全打点
声明局部变量+微秒级相减是目前最稳的实操路径,但必须守住三个边界:变量作用域、函数调用时机、结果处理方式。
- 必须用
DECLARE start_time DATETIME(6) DEFAULT NOW(6);,不能用会话变量@start_time——后者在嵌套调用或并发场景下会被覆盖 - 打点位置要包住真正想测的SQL块,例如:
SET start_time = NOW(6); SELECT ... ; SELECT TIMESTAMPDIFF(MICROSECOND, start_time, NOW(6)) AS us_elapsed; - 别用
TIMEDIFF()——它最大支持838:59:59,超时直接截断;TIMESTAMPDIFF(MICROSECOND, ...)可安全覆盖数小时范围 - 结果建议存入日志表而非
SELECT输出,否则客户端工具(如MySQL Shell)可能把输出延迟计入耗时
为什么推荐把计时逻辑移出存储过程
MySQL存储过程调试能力弱、无原生性能探针、无法捕获等待事件,硬塞计时反而掩盖真实瓶颈。比如你测出某段SQL耗时2秒,但根本不知道它是卡在磁盘I/O、锁等待,还是CPU忙等——这些NOW(6)完全不体现。
- 真正需要监控的是语句级资源消耗,应启用
performance_schema,查events_statements_history_long中对应线程的TIMER_WAIT和EVENT_NAME - 若需长期埋点,应用层发起
CALL前记录SYSDATETIME()(MySQL 8.0.28+支持SYSUTCDATETIME()),再结合sys.statement_analysis反查内部SQL - 存储过程里写
INSERT INTO perf_log记耗时?同步落库会拖慢主流程20–200ms,且audit_log表若没索引或高频插入,极易引发页分裂和锁竞争
最常被忽略的一点:你测出来的“耗时”,大概率不是SQL执行时间,而是它前面那条没加索引的CREATE TEMPORARY TABLE触发的全表扫描,或者游标FETCH时隐式持有的行锁等待。计时本身容易,但解读计时结果需要穿透到performance_schema.events_waits_history_long里看等待事件——这才是MySQL存储过程性能分析的真实入口。











