MySQL 8.0.28 から performance_schema に CPU_TIME を記録できるようになりました。
これまでも実行時間は performance_schema から確認できていました。ただ、実行時間が長いだけでは、そのクエリが必ずしもCPUに負荷をかけているクエリとは言いきれませんでした。
今回の機能追加で、CPUに負荷をかけているクエリがバシっと performance_schema から確認できるようになったのは嬉しいですね(欲を言うと、CPU_TIME を スロークエリログにも出してほしいなぁ)。
デフォルトではOFFになっています。以下の設定を my.cnf に記述し、有効化します。
performance-schema-consumer-events-statements-cpu=ON
sys.statement_analysis にも cpu_latency が追加され、レスポンスタイムに占めるCPU時間が確認できます。
この例では total_time = cpu_latency になってます。テーブルが小さく、ほとんどIOの発生しないクエリだったため、このような結果になってます。
mysql> select * from sys.statement_analysis limit 1\G
*************************** 1. row ***************************
query: SELECT SUM ( `salary` ) FROM `salaries`
db: employees
full_scan: *
exec_count: 5
err_count: 0
warn_count: 0
total_latency: 2.12 s
max_latency: 523.29 ms
avg_latency: 424.49 ms
lock_latency: 17.00 us
cpu_latency: 2.12 s
<snip>
1 row in set (0.00 sec)
total_letency != cpu_latency となりそうな、IOバウンドなクエリで試してみます*1。
7.14 min のうち、cpu_latency が 15.80 s と IOに時間がかかったことが、しっかり読み取れました。
mysql> SELECT * FROM sbtest.sbtest1 WHERE c = 'X';
Empty set (7 min 5.33 sec)
mysql> select * from sys.statement_analysis LIMIT 1\G
*************************** 1. row ***************************
query: SELECT * FROM `sbtest` . `sbtest1` WHERE `c` = ?
db: NULL
full_scan: *
exec_count: 2
err_count: 1
warn_count: 0
total_latency: 7.14 min
max_latency: 7.09 min
avg_latency: 3.57 min
lock_latency: 19.00 us
cpu_latency: 15.80 s
<snip>
実装
CPU時間は clock_gettime() を利用して取得してる。
mysys/my_rdtsc.cc
/**
THREAD_CPU timer.
Expressed in nanoseconds.
*/
ulonglong my_timer_thread_cpu(void) {
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_THREAD_CPUTIME_ID)
{
struct timespec tp;
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp);
return (ulonglong)tp.tv_sec * 1000000000 + (ulonglong)tp.tv_nsec;
}