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 では、クエリの合計実行時間を時間間隔で割ります。

データベース負荷 - Amazon Relational Database Service

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回ずつは確率的にしか標本に出ないが、数が集まれば負荷の大きさは実行時間どおりに出る

20261013-pi-sampling.svg

Figure 1: 1秒ごとの標本に捕まる実行の模式図。短い文ほど1回ずつは捕まりにくいが、回数が多いので、捕まる数は同じくらいになる

関連