KDOC 659: 1秒より短いクエリも Performance Insights の負荷には実行時間どおりに出る
この文書のステータス
- 作成
- 2026-10-13 貴島
- レビュー
- 2026-10-13 貴島
概要
RDS Performance Insights は1秒間隔でサンプリングするが、1秒かからないクエリはどうなるのかと考えた。たしかに1回ずつは確率的にしか標本に出ないが、標本が多ければ負荷の大きさは実行時間どおりに出て問題ない。
Performance Insights の DB 負荷は、毎秒アクティブなセッションを数えた標本から求める(AAS)。いっぽう pg_stat_statements は、すべての呼び出しの実行時間を足している(AAE)。
Database Insights は、特定期間の総セッション数を総サンプル数で割って AAS を計算します。
(略)
AAE を計算するために、Database Insights では、クエリの合計実行時間を時間間隔で割ります。
1秒ごとに pg_stat_activity でアクティブなセッションを数える標本と、pg_stat_statements の実行時間を並べて比べる。1000 ms、100 ms、10 ms の文を、どれも実行時間の合計が経過時間の1割になる頻度で流しながら、300回標本を取る。
# 負荷を流す側と標本を取る側の2つのセッションが同時に要るので、pgpg のプロキシを通さず PostgreSQL に直接(15433)つなぐ sample() { psql -h localhost -p 15433 -U postgres -d postgres -qAt <<'SQL' CREATE TEMP TABLE samples (query_id bigint); -- Performance Insights と同じく、1秒ごとにアクティブなセッションを300回数える DO $$ BEGIN FOR i IN 1..300 LOOP PERFORM pg_stat_clear_snapshot(); INSERT INTO samples SELECT query_id FROM pg_stat_activity WHERE application_name = 'pgbench' AND state = 'active'; PERFORM pg_sleep(1); END LOOP; END $$; -- 実行時間から求めた負荷(AAE)と、標本から求めた負荷(AAS)を並べる SELECT format('| %s ms | %s | %s | %s | %s |', round(s.mean_exec_time), s.calls, round((s.total_exec_time / 1000 / 300)::numeric, 3), n, round(n / 300.0, 3)) FROM pg_stat_statements s, LATERAL (SELECT count(*) AS n FROM samples WHERE query_id = s.queryid) c WHERE s.query LIKE 'SELECT%pg_sleep%' ORDER BY s.mean_exec_time DESC; SQL } docker exec pgpg-postgres-1 psql -U postgres -qAt -c "SELECT pg_stat_statements_reset()" > /dev/null # pg_stat_statements は定数をまとめるので、文の形を変えて別の文として数えさせる printf 'SELECT pg_sleep(1);\n' | docker exec -i pgpg-postgres-1 sh -c 'cat > /tmp/s1000.sql' printf 'SELECT 1 FROM pg_sleep(0.1);\n' | docker exec -i pgpg-postgres-1 sh -c 'cat > /tmp/s100.sql' printf 'SELECT 1, 2 FROM pg_sleep(0.01);\n' | docker exec -i pgpg-postgres-1 sh -c 'cat > /tmp/s10.sql' # どれも実行時間の合計が経過時間の1割(AAE 0.1)になるように、1回の時間に反比例した頻度で流す docker exec pgpg-postgres-1 pgbench -U postgres -n -R 0.1 -T 301 -f /tmp/s1000.sql postgres > /dev/null 2>&1 & docker exec pgpg-postgres-1 pgbench -U postgres -n -R 1 -T 301 -f /tmp/s100.sql postgres > /dev/null 2>&1 & docker exec pgpg-postgres-1 pgbench -U postgres -n -R 10 -T 301 -f /tmp/s10.sql postgres > /dev/null 2>&1 & sleep 0.5 echo '| 文の時間 | 呼び出し | AAE | 標本(300回中) | AAS |' echo '|-' sample wait
| 文の時間 | 呼び出し | AAE | 標本(300回中) | AAS |
|---|---|---|---|---|
| 1001 ms | 26 | 0.087(26*1.001/300) | 26 | 0.087 (26/300) |
| 101 ms | 305 | 0.103(305*0.101/300) | 31 | 0.103 (31/300) |
| 10 ms | 3000 | 0.101(3000*0.0101/300) | 29 | 0.097 (29/300) |
- どの長さの文も、標本から求めた AAS は実行時間から求めた AAE とほぼ同じだった
- 1000 ms の文は、26回の呼び出しが26回の標本に出た。1回の呼び出しが、ほぼ1回の標本に出ている
- 100 ms の文は305回呼ばれて標本に出たのは31回、10 ms の文は3000回呼ばれて29回だった。1秒より短い文は、呼び出しのうち「文の時間 / 1秒」くらいの割合しか標本に出ない
- 1秒より短い文は、1回ずつは確率的にしか標本に出ないが、数が集まれば負荷の大きさは実行時間どおりに出る
Figure 1: 1秒ごとの標本に捕まる実行の模式図。短い文ほど1回ずつは捕まりにくいが、回数が多いので、捕まる数は同じくらいになる