問題の説明
ApsaraDB RDS for PostgreSQL の使用中に、CPU 使用率が高くなり、場合によっては 100% になることがあります。この記事では、CPU 使用率が 100% になったシナリオを例にとって、その原因と解決策について説明します。
解決策
CPU 使用率が 100% になった場合、次の方法で原因を突き止め、問題を解決できます。
アクティブ接続数の確認
原因として最初に考えられるのは、業務のピーク時にアクティ接続が急増し、データベースの予約済みリソースで対応できなくなることです。問題の発生時に、アクティ接続数が普段よりはるかに増加したかを確認する必要があります。
ApsaraDB RDS for PostgreSQL インスタンスの場合、接続数は、RDL コンソールのモニターとアラームセクションで確認でき、アクティ接続数は、データベースに接続し、次の文を実行して取得できます。
select count( * ) from pg_stat_activity where state not like '%idle';アクティ接続数の変動が通常の範囲にある場合は、次をご参照ください。
スロー SQL の特定
アクティブ接続数が通常の範囲内で変動する場合、低パフォーマンスの SQL 文が大量にあるかどうかを確認してください。。
RDS のスロー SQL ログを分析できますが、通常の場合、問題の発生時に、システムが停止状態になり、すべての SQL が影響を受けて遅くなるため、問題の原因となっているスロー SQL を特定することは困難です。
次に、スロー SQL の特定方法をいくつか紹介します。
方法一:pg_stat_statements プラグインを使用する (ApsaraDB RDS for PostgreSQL のみ対応)
この方法では、カウント情報 (SQL の累計実行時間など) を参照します。データベースにプラグインがない場合は手動で作成してください。
次のコマンドを実行し、カウンターをリセットします。
create extension pg_stat_statements; select pg_stat_reset(); select pg_stat_statements_reset();カウンターに一定期間 (1 分間など) の情報が蓄積されるまで待ちます。
次のコマンドを参照し、時間のかかる SQL (すなわち問題の原因) をクエリします。
select * from pg_stat_statements order by total_time desc limit 5;次のコマンドを参照し、バッファ読み取り回数が一番大きい SQL をクエリします。クエリ対象データのインデックスがないと、バッファーの頻繁な読み取りが発生し、大量な CPU を消費することになります。
select * from pg_stat_statements order by shared_blks_hit+shared_blks_read desc limit 5;
方法二:pg_stat_activity ビューを使用する
pg_stat_activity ビューを使用し、次のコマンドを参照して、長時間実行されている SQL をクエリします。
select datname,
usename,
client_addr,
application_name,
state,
backend_start,
xact_start,
xact_stay,
query_start,
query_stay,
replace(query, chr(10), ' ') as query
from
(select pgsa.datname as datname,
pgsa.usename as usename,
pgsa.client_addr client_addr,
pgsa.application_name as application_name,
pgsa.state as state,
pgsa.backend_start as backend_start,
pgsa.xact_start as xact_start,
extract(epoch
from (now() - pgsa.xact_start)) as xact_stay,
pgsa.query_start as query_start,
extract(epoch
from (now() - pgsa.query_start)) as query_stay,
pgsa.query as query
from pg_stat_activity as pgsa
where pgsa.state != 'idle'
and pgsa.state != 'idle in transaction'
and pgsa.state != 'idle in transaction (aborted)') idleconnections
order by query_stay desc
limit 5;方法三:テーブルスキャンの記録を参照する
この方法では、テーブルスキャンの記録から、インデックスのないテーブルをクエリします。
テーブルのインデックスがなく、ほとんどのホットデータがメモリに格納されている場合 (例えば、8 GB のメモリに 6 GB のホットデータ)、テーブルスキャンを実行してメモリ内の大量の無関係なレコードを処理することになるため、CPU 使用量が増加します。特に 100 レコードを超えるテーブルに対してテーブルスキャンを実行すると、CPU 使用量が 100% 近くになり、複数の接続 (数百など) が同時に開始します。
次のコマンドを参照して、スキャンするレコード数の最も多いテーブルをクエリします。
select * from pg_stat_user_tables where n_live_tup > 100000 and seq_scan > 0 order by seq_tup_read desc limit 10;次のコマンドを参照して、上記のテーブルにアクセスしている実行中のスロー SQL をクエリます。
select * from pg_stat_activity where query ilike '%<table name>%' and query_start - now() > interval '10 seconds';説明pg_stat_statements プラグインを使用し、次のコマンドを参照して、上記のテーブルに関連している SQL をクエリすることもできます。
select * from pg_stat_statements where query ilike '%<table>%'order by shared_blks_hit+shared_blks_read desc limit 3;
スロー SQL の処理
上記の方法で取得したスロー SQL が業務に必要でない場合、次のコマンドを参照してスロー SQL を終了し、業務を回復させます。
select pg_cancel_backend(pid) from pg_stat_activity where query like '%<query text>%' and pid != pg_backend_pid();
select pg_terminate_backend(pid) from pg_stat_activity where query like '%<query text>%' and pid != pg_backend_pid();業務に必要な場合、次の方法で最適化する必要があります。
スロー SQL に関連しているテーブルに、
ANALYZE [ $Table ]またはVACUUM ANALYZE [ $Table ]文を実行して、テーブルの統計を更新します。これにより、クエリ実行計画の精度が向上します。業務への影響を回避するため、業務のオフピーク時に実行してください。
説明[ $Table ] は、スロー SQL に関連しているテーブルを表します。
次のコマンドのいずれかを実行して、SQL の実行計画を表示します。テーブルスキャンが行われるテーブルに対し、インデックスを作成します。
1 つ目のコマンドの場合、SQL 文は実行されません。
2 つ目のコマンドの場合、SQL 文が実行され、実行情報の詳細も取得できます。
explain [ $Query_Text ] explain (buffers true, analyze true, verbose true) [ $Query_Text ]説明[ $Query_Text ] は、SQL ファイルまたは文を表します。
不要なサブクエリを削除したり、UNION ALL を書き直したり、JOIN 句を使用して結合順序を固定したりするなど、スロー SQL 文を最適化します。
適用範囲
ApsaraDB RDS for PostgreSQL