OLTP(オンライントランザクション処理)データベースを使用する場合、ユーザーエクスペリエンスに直接影響するため、クエリのパフォーマンスが最も重要です。クエリが遅いということは、アプリケーションが応答しなくなり、遅く感じられることを意味します。これにより、コンバージョン率が低下し、ユーザーが不満になり、すべての問題が発生します。
OLTPはPostgreSQLの一般的なユースケースの1つであるため、クエリをできるだけスムーズに実行する必要があります。このブログでは、PostgreSQLで低速クエリの問題を特定する方法について説明します。
一般的に、PostgreSQLのパフォーマンスの問題を特定する最も一般的な方法は、遅いクエリを収集することです。あなたがそれをすることができるいくつかの方法があります。まず、単一のデータベースで有効にできます:
pgbench=# ALTER DATABASE pgbench SET log_min_duration_statement=0;
ALTER DATABASE
この後、「pgbench」データベースへのすべての新しい接続がPostgreSQLログに記録されます。
次を追加することで、これをグローバルに有効にすることもできます。
log_min_duration_statement = 0
PostgreSQL構成に移動してから、構成を再ロードします:
pgbench=# SELECT pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)
これにより、PostgreSQL内のすべてのデータベースにわたるすべてのクエリのログを記録できます。ログが表示されない場合は、logging_collector=onも有効にすることをお勧めします。ログにはPostgreSQLシステムテーブルに到達するすべてのトラフィックが含まれるため、ノイズが多くなります。私たちの目的のために、データベースレベルのロギングに固執しましょう。
ログに表示されるのは、次のようなエントリです。
2020-02-21 09:45:39.022 UTC [13542] LOG: duration: 0.145 ms statement: SELECT abalance FROM pgbench_accounts WHERE aid = 29817899;
2020-02-21 09:45:39.022 UTC [13544] LOG: duration: 0.107 ms statement: SELECT abalance FROM pgbench_accounts WHERE aid = 11782597;
2020-02-21 09:45:39.022 UTC [13529] LOG: duration: 0.065 ms statement: SELECT abalance FROM pgbench_accounts WHERE aid = 16318529;
2020-02-21 09:45:39.022 UTC [13529] LOG: duration: 0.082 ms statement: UPDATE pgbench_tellers SET tbalance = tbalance + 3063 WHERE tid = 3244;
2020-02-21 09:45:39.022 UTC [13526] LOG: duration: 16.450 ms statement: UPDATE pgbench_branches SET bbalance = bbalance + 1359 WHERE bid = 195;
2020-02-21 09:45:39.023 UTC [13523] LOG: duration: 15.824 ms statement: UPDATE pgbench_accounts SET abalance = abalance + -3726 WHERE aid = 5290358;
2020-02-21 09:45:39.023 UTC [13542] LOG: duration: 0.107 ms statement: UPDATE pgbench_tellers SET tbalance = tbalance + -2716 WHERE tid = 1794;
2020-02-21 09:45:39.024 UTC [13544] LOG: duration: 0.112 ms statement: UPDATE pgbench_tellers SET tbalance = tbalance + -3814 WHERE tid = 278;
2020-02-21 09:45:39.024 UTC [13526] LOG: duration: 0.060 ms statement: INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (4876, 195, 39955137, 1359, CURRENT_TIMESTAMP);
2020-02-21 09:45:39.024 UTC [13529] LOG: duration: 0.081 ms statement: UPDATE pgbench_branches SET bbalance = bbalance + 3063 WHERE bid = 369;
2020-02-21 09:45:39.024 UTC [13523] LOG: duration: 0.063 ms statement: SELECT abalance FROM pgbench_accounts WHERE aid = 5290358;
2020-02-21 09:45:39.024 UTC [13542] LOG: duration: 0.100 ms statement: UPDATE pgbench_branches SET bbalance = bbalance + -2716 WHERE bid = 210;
2020-02-21 09:45:39.026 UTC [13523] LOG: duration: 0.092 ms statement: UPDATE pgbench_tellers SET tbalance = tbalance + -3726 WHERE tid = 67;
2020-02-21 09:45:39.026 UTC [13529] LOG: duration: 0.090 ms statement: INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (3244, 369, 16318529, 3063, CURRENT_TIMESTAMP);
与えられたクエリが本当に改善したいものであると判断したら、クエリ実行プランを確認する必要があります。まず第一に、それについて私たちにできることは何もないことが起こるかもしれません、そして私たちは与えられたクエリがただ遅いことを受け入れる必要があります。次に、クエリ実行計画が変更される可能性があります。オプティマイザは常に最適な実行プランを選択しようとしますが、データのサンプルのみに基づいて決定を行うため、クエリ実行プランが時間の経過とともに変更される可能性があります。 PostgreSQLでは、2つの方法で実行プランを確認できます。まず、EXPLAINを使用した推定実行プラン:
pgbench=# EXPLAIN SELECT abalance FROM pgbench_accounts WHERE aid = 5290358;
QUERY PLAN
----------------------------------------------------------------------------------------------
Index Scan using pgbench_accounts_pkey on pgbench_accounts (cost=0.56..8.58 rows=1 width=4)
Index Cond: (aid = 5290358)
ご覧のとおり、主キールックアップを使用してデータにアクセスすることが期待されています。クエリがどの程度正確に実行されるかを再確認する場合は、EXPLAINANALYZEを使用できます。
pgbench=# EXPLAIN ANALYZE SELECT abalance FROM pgbench_accounts WHERE aid = 5290358;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------
Index Scan using pgbench_accounts_pkey on pgbench_accounts (cost=0.56..8.58 rows=1 width=4) (actual time=0.046..0.065 rows=1 loops=1)
Index Cond: (aid = 5290358)
Planning time: 0.053 ms
Execution time: 0.084 ms
(4 rows)
これで、PostgreSQLがこのクエリを実行し、実行プラン、アクセスされた行数などに関して、見積もりだけでなく正確な数も教えてくれます。すべてのクエリをログに記録すると、システムに深刻なオーバーヘッドが発生する可能性があることに注意してください。また、ログを監視し、ログが適切に回転していることを確認する必要があります。
Pg_stat_statements
Pg_stat_statementsは、さまざまなクエリタイプの実行統計を収集する拡張機能です。
pgbench=# select query, calls, total_time, min_time, max_time, mean_time, stddev_time, rows from public.pg_stat_statements order by calls desc LIMIT 10;
query | calls | total_time | min_time | max_time | mean_time | stddev_time | rows
------------------------------------------------------------------------------------------------------+-------+------------------+----------+------------+---------------------+---------------------+-------
UPDATE pgbench_branches SET bbalance = bbalance + $1 WHERE bid = $2 | 30437 | 6636.83641200002 | 0.006533 | 83.832148 | 0.218051595492329 | 1.84977058799388 | 30437
BEGIN | 30437 | 231.095600000001 | 0.000205 | 20.260355 | 0.00759258796859083 | 0.26671126085716 | 0
END | 30437 | 229.483213999999 | 0.000211 | 16.980678 | 0.0075396134310215 | 0.223837608828596 | 0
UPDATE pgbench_accounts SET abalance = abalance + $1 WHERE aid = $2 | 30437 | 290021.784321001 | 0.019568 | 805.171845 | 9.52859297305914 | 13.6632712046825 | 30437
UPDATE pgbench_tellers SET tbalance = tbalance + $1 WHERE tid = $2 | 30437 | 6667.27243200002 | 0.00732 | 212.479269 | 0.219051563294674 | 2.13585110968012 | 30437
SELECT abalance FROM pgbench_accounts WHERE aid = $1 | 30437 | 3702.19730600006 | 0.00627 | 38.860846 | 0.121634763807208 | 1.07735927551245 | 30437
INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP) | 30437 | 2349.22475800002 | 0.003218 | 61.372127 | 0.0771831901304325 | 0.971590327400244 | 30437
SELECT $1 | 6847 | 60.785467 | 0.002321 | 7.882384 | 0.00887767883744706 | 0.105198744982906 | 6847
insert into pgbench_tellers(tid,bid,tbalance) values ($1,$2,$3) | 5000 | 18.592042 | 0.001572 | 0.741427 | 0.0037184084 | 0.0137660355678027 | 5000
insert into pgbench_tellers(tid,bid,tbalance) values ($1,$2,$3) | 3000 | 7.323788 | 0.001598 | 0.40152 | 0.00244126266666667 | 0.00834442591085048 | 3000
(10 rows)
上記のデータからわかるように、さまざまなクエリのリストとそれらの実行時間に関する情報があります。これはpg_stat_statementsに表示されるデータの一部にすぎませんが、主キーの検索が完了するまでに39秒近くかかることがあることを理解しました。これは見栄えがよくなく、間違いなく調査したいものです。
pg_stat_statementsを有効にしていない場合は、標準的な方法で有効にできます。構成ファイルと
のいずれかを介してshared_preload_libraries = 'pg_stat_statements'
または、PostgreSQLコマンドラインから有効にすることができます:
pgbench=# CREATE EXTENSION pg_stat_statements;
CREATE EXTENSION
ClusterControlを使用して低速クエリを排除する
ClusterControlを使用してPostgreSQLデータベースを管理している場合は、それを使用して低速クエリに関するデータを収集できます。
ご覧のとおり、クエリの実行に関するデータを収集します。調べた、実行時間の統計など。これを使用すると、最もコストのかかるクエリを簡単に特定し、平均実行時間と最大実行時間がどのようになるかを確認できます。デフォルトでは、ClusterControlは、完了するまでに0.5秒以上かかったクエリを収集します。これは、設定で変更できます。
この短いブログは、PostgreSQLのクエリパフォーマンスの問題を特定して解決するのに役立つすべての側面とツールを網羅しているわけではありません。これが良いスタートであり、クエリが遅い原因を特定するために何ができるかを理解するのに役立つことを願っています。