sql >> データベース >  >> RDS >> PostgreSQL

PostgreSQLの実行速度が遅い?ソースに到達するためのヒントとコツ

    PostgreSQLデータベース管理者として、バックアップをチェックし、DDL変更を適用し、ログにゲームを壊すエラーがないことを確認し、レポートが通常の2倍の時間実行されている開発者からのパニック状態の呼び出しに応答することが日常的に期待されています。 10分で会議があります。

    管理対象データベースの状態を十分に理解していても、パフォーマンスとデータベースの「感触」に関連して、常に新しいケースや新しい問題が発生します。パニックに陥った電子メールであろうと、「データベースが遅いと感じる」ためのオープンチケットであろうと、この一般的なタスクの後には、PostgreSQLに問題があるかどうか、およびその問題が何であるかを確認するためのいくつかの手順を実行できます。

    これは完全なガイドではなく、特定の順序で手順を実行する必要もありません。しかし、それはむしろ、一般的な犯罪者をすばやく見つけ、問題が何であるかについての新しい洞察を得るのに役立つ一連の最初のステップです。開発者はアプリケーションがどのように動作して応答するかを知っているかもしれませんが、データベース管理者はデータベースがどのように動作してアプリケーションに応答するかを知っており、一緒に問題を見つけることができます。

    注: 実行するクエリは、「postgres」などのスーパーユーザー、またはスーパーユーザー権限を付与されたデータベースユーザーとして実行する必要があります。制限付きユーザーは拒否されるか、データが省略されます。

    ステップ0-情報収集

    データベースが遅いと言う人からできるだけ多くの情報を入手してください。特定のクエリ、接続されているアプリケーション、パフォーマンスの低下の時間枠など。情報が多ければ多いほど、問題を見つけやすくなります。

    ステップ1-pg_stat_activityを確認します

    リクエストにはさまざまな形式がありますが、「遅さ」が一般的な問題である場合は、pg_stat_activityをチェックすることが、何が起こっているのかを理解するための最初のステップです。 pg_stat_activityビュー(このビューのすべての列のドキュメントはここにあります)には、すべてのサーバープロセス/クライアントからデータベースへの接続の行が含まれています。このビューには、役立つ情報がいくつかあります。

    注: pg_stat_activityは、時間の経過とともに構造を変更し、提示するデータを改良することが知られています。列自体を理解すると、将来必要に応じて動的にクエリを作成するのに役立ちます。

    pg_stat_activityの注目すべき列は次のとおりです。

    1. query:現在実行中、実行待ち、または最後に実行されたクエリを示すテキスト列(状態によって異なります)。これは、開発者が報告している可能性のあるクエリの実行速度が遅いことを特定するのに役立ちます。
    2. client_addr:この接続とクエリの発信元のIPアドレス。空(またはNull)の場合、ローカルホストから発生しました。
    3. backend_start、xact_start、query_start:これら3つは、それぞれが開始されたときのタイムスタンプを提供します。 Backend_startはデータベースへの接続が確立されたときを表し、xact_startは現在のトランザクションが開始されたとき、query_startは現在の(または最後の)クエリが開始されたときを表します。
    4. state:データベースへの接続の状態。アクティブとは、現在クエリを実行していることを意味し、「アイドル」とは、クライアントからのさらなる入力を待機していることを意味します。「アイドルイントランザクション」とは、開いているトランザクションを保持している間、クライアントからのさらなる入力を待機していることを意味します。 (他にもありますが、その可能性はまれです。詳細については、ドキュメントを参照してください)。
    5. datname:接続が現在接続されているデータベースの名前。複数のデータベースクラスターでは、これは問題のある接続を特定するのに役立ちます。
    6. wait_event_typeおよびwait_event:クエリが待機していない場合、これらの列はnullになりますが、待機している場合は、クエリが待機している理由に関する情報が含まれ、pg_locksを調べると、待機しているものを特定できます。 (PostgreSQL 9.5以前には、「待機中」と呼ばれるブール列しかありません。待機中の場合はtrue、そうでない場合はfalseです。

    1.1。クエリは待機中/ブロックされていますか?

    「遅い」または「ハング」している特定のクエリがある場合は、別のクエリが完了するのを待っているかどうかを確認してください。リレーションロックにより、他のクエリはテーブルをロックし、そのクエリまたはトランザクションが完了するまで、他のクエリがデータにアクセスしたりデータを変更したりできないようにすることができます。

    PostgreSQL 9.5以前:

    SELECT * FROM pg_stat_activity WHERE waiting = TRUE;

    PostgreSQL 9.6:

    SELECT * FROM pg_stat_activity WHERE wait_event IS NOT NULL;

    PostgreSQL 10以降(?):

    SELECT * FROM pg_stat_activity WHERE wait_event IS NOT NULL AND backend_type = 'client backend';

    このクエリの結果には、必要なリレーションのロックを解除するために別の接続を現在待機している接続が表示されます。

    クエリが別の接続によってブロックされている場合、それらが何であるかを確認する方法がいくつかあります。 PostgreSQL 9.6以降では、関数pg_blocking_pids()により、ブロックされているプロセスIDの入力が可能になり、ブロックを担当するプロセスIDの配列が返されます。

    PostgreSQL 9.6以降:

    SELECT * FROM pg_stat_activity 
    WHERE pid IN (SELECT pg_blocking_pids(<pid of blocked query>));

    PostgreSQL 9.5以前:

    SELECT blocked_locks.pid     AS blocked_pid,
             blocked_activity.usename  AS blocked_user,
             blocking_locks.pid     AS blocking_pid,
             blocking_activity.usename AS blocking_user,
             blocked_activity.query    AS blocked_statement,
             blocking_activity.query   AS current_statement_in_blocking_process
       FROM  pg_catalog.pg_locks         blocked_locks
        JOIN pg_catalog.pg_stat_activity blocked_activity  ON blocked_activity.pid = blocked_locks.pid
        JOIN pg_catalog.pg_locks         blocking_locks 
            ON blocking_locks.locktype = blocked_locks.locktype
            AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE
            AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation
            AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page
            AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple
            AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid
            AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid
            AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid
            AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid
            AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid
            AND blocking_locks.pid != blocked_locks.pid
        JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid
       WHERE NOT blocked_locks.GRANTED;

    (PostgreSQL Wikiから入手できます。)

    これらのクエリは、提供された特定のPIDをブロックしているものをすべて指し示します。これにより、ブロックしているクエリまたは接続を強制終了するか、実行させるかを決定できます。

    ステップ2-クエリが実行されている場合、なぜこれほど時間がかかるのですか?

    2.1。プランナーはクエリを効率的に実行していますか?

    問題のクエリ(または一連のクエリ)のステータスが「アクティブ」の場合、実際に実行されています。クエリ全体がpg_stat_activityで利用できない場合は、開発者またはpostgresqlログからクエリを取得して、クエリプランナーの調査を開始します。

    EXPLAIN SELECT * FROM postgres_stats.table_stats t JOIN hosts h ON (t.host_id = h.host_id) WHERE logged_date >= '2018-02-01' AND logged_date < '2018-02-04' AND t.india_romeo = 569;
    Nested Loop  (cost=0.280..1328182.030 rows=2127135 width=335)
      ->  Index Scan using six on victor_oscar echo  (cost=0.280..8.290 rows=1 width=71)
              Index Cond: (india_romeo = 569)
      ->  Append  (cost=0.000..1306902.390 rows=2127135 width=264)
            ->  Seq Scan on india_echo romeo  (cost=0.000..0.000 rows=1 width=264)
                    Filter: ((logged_date >= '2018-02-01'::timestamp with time zone) AND (logged_date < '2018-02-04'::timestamp with time zone) AND (india_romeo = 569))
            ->  Seq Scan on juliet victor_echo  (cost=0.000..437153.700 rows=711789 width=264)
                    Filter: ((logged_date >= '2018-02-01'::timestamp with time zone) AND (logged_date < '2018-02-04'::timestamp with time zone) AND (india_romeo = 569))
            ->  Seq Scan on india_papa quebec_bravo  (cost=0.000..434936.960 rows=700197 width=264)
                    Filter: ((logged_date >= '2018-02-01'::timestamp with time zone) AND (logged_date < '2018-02-04'::timestamp with time zone) AND (india_romeo = 569))
            ->  Seq Scan on two oscar  (cost=0.000..434811.720 rows=715148 width=264)
                    Filter: ((logged_date >= '2018-02-01'::timestamp with time zone) AND (logged_date < '2018-02-04'::timestamp with time zone) AND (india_romeo = 569))

    この例は、パーティション化されたテーブルにもヒットする2つのテーブル結合のクエリプランを示しています。クエリが遅くなる原因となる可能性のあるものを探しています。この場合、プランナーはパーティションに対して複数のシーケンシャルスキャンを実行しており、インデックスが欠落していることを示しています。列「india_romeo」のこれらのテーブルにインデックスを追加すると、このクエリがすぐに改善されます。

    探すべきものは、シーケンシャルスキャン、ネストされたループ、高価な並べ替えなどです。クエリプランナーを理解することは、クエリが可能な限り最高の方法で実行されていることを確認するために重要です。詳細については、公式ドキュメントを参照してください。

    2.2。関係するテーブルは肥大化していますか?

    クエリプランナーが明確なものを指さずにクエリがまだ遅いと感じている場合は、関連するテーブルの状態を確認するときが来ました。彼らは大きすぎますか?彼らは肥大化していますか?

    SELECT n_live_tup, n_dead_tup from pg_stat_user_tables where relname = ‘mytable’;
    n_live_tup  | n_dead_tup
    ------------+------------
          15677 |    8275431
    (1 row)

    ここでは、ライブ行よりもデッド行が何倍も多いことがわかります。つまり、正しい行を見つけるために、エンジンは実際のデータを見つけるのに関連性さえないデータをふるいにかける必要があります。このテーブルのバキューム/バキュームフルは、パフォーマンスを大幅に向上させます。

    ステップ3-ログを確認する

    それでも問題が見つからない場合は、ログで手がかりを確認してください。

    致命的/エラーメッセージ:

    デッドロックやロックを取得するための長い待機時間など、問題を引き起こしている可能性のあるメッセージを探します。

    チェックポイント

    うまくいけば、log_checkpointsがonに設定され、チェックポイント情報がログに書き込まれます。チェックポイントには、時間指定と要求(強制)の2種類があります。チェックポイントが強制されている場合は、さらにクエリを処理する前に、メモリ内のダーティバッファをディスクに書き込む必要があります。これにより、データベースシステムに全体的な「低速」感が生じる可能性があります。 checkpoint_segmentsまたはmax_wal_size(データベースのバージョンに応じて)を増やすと、チェックポインターが作業する余地が増え、バックグラウンドライターが書き込み負荷の一部を負担するのに役立ちます。

    ステップ4-ホストシステムの状態はどうですか?

    データベース自体に手がかりがない場合は、ホスト自体が過負荷になっているか、問題が発生している可能性があります。過負荷のIOシャネルからディスク、メモリのオーバーフローからスワップ、さらにはドライブの障害まで、これらの問題はいずれも、これまで見てきたことでは明らかではありません。データベースが*nixベースのオペレーティングシステムで実行されていると仮定すると、ここに役立つことがいくつかあります。

    4.1。システム負荷

    「top」を使用して、ホストの平均負荷を確認します。数がシステム上のコアの数に近づいているか、それを超えている場合は、データベースにヒットする同時接続が多すぎて、データベースをクロールして追いつくことができない可能性があります。

    load average: 3.43, 5.25, 4.85

    4.2。システムメモリとSWAP

    「無料」を使用して、SWAPが使用されているかどうかを確認します。 PostgreSQLデータベース環境でSWAPにメモリがオーバーフローすると、パフォーマンスが非常に低下します。多くのDBAは、システムの動作が遅いよりも「メモリ不足」エラーの方が望ましいため、データベースホストからSWAPを排除します。

    SWAPが使用されている場合は、システムを再起動するとシステムがクリアされ、システムの総メモリを増やすか、PostgreSQLのメモリ使用量を再構成する(shared_buffersやwork_memを下げるなど)ことが適切な場合があります。

    [[email protected] ~]$ free -m
                  total        used        free      shared  buff/cache   available
    Mem:           7986         225        1297          12        6462        7473
    Swap:          7987        2048        5939

    4.3。ディスクアクセス

    PostgreSQLはメモリ内で多くの作業を実行し、ボトルネックを最小限に抑えるためにディスクへの書き込みを分散しようとしますが、書き込みが多い過負荷のシステムでは、読み取りと書き込みが多いと、システム全体が追いつくのが遅くなることが簡単にわかります。要求に応じて。より高速なディスク、より多くのディスク、およびIOチャネルは、実行できる作業量を増やすためのいくつかの方法です。

    「iostat」や「iotop」などのツールは、ディスクのボトルネックがあるかどうか、およびそれがどこから来ているのかを特定するのに役立ちます。

    4.4。ログを確認してください

    他のすべてが失敗した場合、または失敗した場合でも、ログを常にチェックして、システムが正しくないことを報告しているかどうかを確認する必要があります。 postgresql.logsのチェックについてはすでに説明しましたが、システムログは、ディスクの障害、メモリの障害、ネットワークの問題などの問題に関する情報を提供します。これらの問題のいずれかにより、データベースの動作が遅くなり、予測できない可能性があります。完璧な健康状態は、これらの問題を見つけるのに役立ちます。

    今日のホワイトペーパーをダウンロードするClusterControlを使用したPostgreSQLの管理と自動化PostgreSQLの導入、監視、管理、スケーリングを行うために知っておくべきことについて学ぶホワイトペーパーをダウンロードする

    ステップ5-まだ意味がないことはありますか?

    最も経験豊富な管理者でさえ、意味をなさない新しいことに遭遇するでしょう。そこで、グローバルなPostgreSQLコミュニティが協力してくれます。ステップ0と同じように、コミュニティに提供される情報が明確であればあるほど、コミュニティは支援しやすくなります。

    5.1。 PostgreSQLメーリングリスト

    PostgreSQLはオープンソースコミュニティによって開発および管理されているため、機能、エラー、パフォーマンスの問題など、数え切れないほどのトピックについて話し合うためにメーリングリストを介して話し合う何千人もの人々がいます。メーリングリストはここにあります。パフォーマンスの問題に関するヘルプを探すには、pgsql-adminとpgsql-performanceが最も重要です。

    5.2。 IRC

    Freenodeは、世界中の開発者や管理者と一緒にいくつかのPostgreSQLチャネルをホストしており、問題がどこから来ているのかを追跡するのに役立つ人を見つけるのは難しくありません。詳細については、PostgreSQLIRCページをご覧ください。


    1. SQL SELECT SUM

    2. SQLServerの日時フィールドのデフォルト値をタイムスタンプに追加します

    3. PL/pgSQL関数のSELECTまたはPERFORM

    4. 単一の文字列としてのコンマ区切りリスト、T-SQL