web-dev-qa-db-ja.com

Postgres 10クエリがIOで待機する:BufFileWriteが原因で新しいデータベース接続の取得に失敗する

ダッシュボードを実行しているすべてのデバイス(通常、ピーク時に500以上のデバイス)から5〜7秒ごとに実行されるクエリ(クエリプランに付属)があります。このクエリは、最初は待機状態_IO:BufFileWrite_で時間を費やしているように見えます。

AWS Aurora Performance Insightsダッシュボードから、問題のクエリが_IO: BUfFileWrite_待機状態でより多くの時間を費やしていることがわかります(グラフの水色)

Performance_Insights

Postgres設定/詳細:

  1. AWS Aurora PostgreSQL 10.6
  2. R5.4Xラージインスタンス(128 GB RAM)
  3. _work_mem = 80MB_
  4. Hikari接続プールを使用してDB接続を管理しています。

1週間前、アプリサーバーで多くのエラーが発生し始めました。

_Failed to get database connection errors_

および/または

_Java.sql.SQLTimeoutException: Timeout after 30000ms of waiting for a connection_

少しデバッグし、PGAdminの助けを借りて、ほとんどの接続が_IO: BufFileWrite_で待機していることがわかり、デフォルト4MB_work_mem_はクエリには不十分でした。

PGAdmin stats

PgBadgerの助けを借りて、一時ファイルの平均サイズが70MBであることを確認したため、_work_mem_を80MBに更新しました。

Temp_File_Pg_Badger

これは間違いなく助けになり、DB接続の問題が少なくなり始めましたが、完全に解消されませんでした。

Postgres Visualize AnalyzerExplain Depesz を使用してクエリプランを理解し、インデックスのみのスキャンにTotal Cost of 4984972.45と行を計画する111195272。このテーブル(_students_points_)は実際には1億行以上あり、サイズが15GB以下であり、パーティション化されていません。

上記のスキャンのコストが改善されることを期待して、部分インデックス(create index students_latest_point_idx ON students_points (student_id, evaluation_date desc))を追加しようとしましたが、無駄でした。

関連するテーブルでVACUUM ( FULL, ANALYZE, VERBOSE);およびREINDEXを実行しましたが、目に見えるパフォーマンスの向上はありません。

以下についてサポートが必要です

  1. クエリプランの_never executed_部分はどういう意味ですか? -私はウェブ上の文献をチェックしましたが、Postgresエンジン以外の満足のいく説明では、それが関連性がないとは考えられず、0行を返しました。
  2. クエリプランから(== --- ==)Total Cost of 4984972.45 and Plan Rows 111195272。と表示されますが、実行されなかった
  3. 待機状態のBufFileWriteで過大な時間が費やされる原因は何ですか?私の理解から、並べ替え/フィルターが適用されている場合、一時ファイルが使用され、これはBufFileWrite待機状態として表示されます。 Monitoring_Postgres
  4. IO BufFileWriteの待機状態でのクエリに費やされる時間を削減するために、どこから始めればよいですか?-私は試しました、Vacuum、Reindex、新しい部分インデックスの追加-役に立たない。
  5. 私の心の1つは、_students_points_テーブル(1行、すべての学生、すべてのテスト、彼は毎週、4年以上かかる)を使用する代わりに、速く構築され、新しいテーブルを作成することですこれは、すべての学生の最新のポイントのみを保持し(したがって、学生と同じ数の行のみ)、クエリでそれを使用します。

どんな助けでもありがたいです。前もって感謝します。さらに情報が必要な場合は、私が提供させていただきます。

クエリとプラン

_EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, TIMING, SUMMARY) 
SELECT 
    SUM(CASE WHEN just_pass = 1 THEN 1 ELSE 0 END) point,
    SUM(CASE WHEN week_of_birthday = TRUE THEN 1 ELSE 0 END) birthday, 
    SUM(CASE WHEN fresh_grad = 1 THEN 1 ELSE 0 end) fresher, 
    SUM(CASE WHEN intervention_type::INT = 2 OR intervention_type::INT = 3 THEN 1 ELSE 0 END) attention 
FROM 
    (
        SELECT 
            checkins.student_id, intercepts.intervention_type ,max(evaluation_date), just_pass, 
            compute_week_of_birthday(student_birthdate, 4 , 'US/Central') as week_of_birthday, 
            CASE 
                WHEN student_enrolment_date NOTNULL AND student_enrolment_date >= '2017-01-29' AND student_enrolment_date < '2016-11-30' 
                THEN 1 ELSE 0 END AS fresh_grad 
            FROM 
            (
                SELECT 
                    student_id 
                FROM 
                    checkin_table 
                WHERE 
                    house_id = 9001 
                AND 
                    timestamp> '2019-06-11 01:00:40' AND timestamp<= '2019-06-11 01:00:50'
                GROUP BY 
                student_id
            ) 
            checkins 
        LEFT JOIN 
            students 
        ON 
            checkins.student_id = students.student_id 
        LEFT JOIN 
            students_points points 
        ON 
            checkins.student_id = points.student_id 
        LEFT JOIN 
            (
                select 
                    record_id, student_id, intervention_type, intervention_date
                FROM 
                    intervention_table 
                WHERE 
                    intervention_date 
                IN 
                    (
                        SELECT 
                            MAX(intervention_date) 
                        FROM 
                            intervention_table 
                        GROUP BY 
                            student_id
                    )
            ) AS intercepts 
        ON 
            checkins.student_id = intercepts.student_id 
        WHERE 
            date_part('year',age(student_birthdate)) >=18 
        AND 
            lower(registration_type_description) !~* '.*temporary.*' 
        GROUP BY 
            checkins.student_id, students.student_enrolment_date, student_birthdate, just_pass, intercepts.intervention_type
    ) AS result 
WHERE 
    max IN 
        (
            SELECT 
                evaluation_date 
            FROM 
                students_points 
            ORDER BY 
                evaluation_date DESC LIMIT 1
        ) 
        OR 
    max ISNULL;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                      QUERY PLAN                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=74433.83..74433.84 rows=1 width=32) (actual time=0.081..0.081 rows=1 loops=1)
   Output: sum(CASE WHEN (result.just_pass = 1) THEN 1 ELSE 0 END), sum(CASE WHEN result.week_of_birthday THEN 1 ELSE 0 END), sum(CASE WHEN (result.fresh_grad = 1) THEN 1 ELSE 0 END), sum(CASE WHEN (((result.intervention_type)::integer = 2) OR ((result.intervention_type)::integer = 3)) THEN 1 ELSE 0 END)
   Buffers: shared hit=20
   ->  Subquery Scan on result  (cost=74412.92..74432.98 rows=34 width=15) (actual time=0.079..0.079 rows=0 loops=1)
         Output: result.student_id, result.intervention_type, result.max, result.just_pass, result.week_of_birthday, result.fresh_grad, students.student_enrolment_date, students.student_birthdate
         Filter: ((hashed SubPlan 1) OR (result.max IS NULL))
         Buffers: shared hit=20
         ->  GroupAggregate  (cost=74412.31..74431.52 rows=68 width=35) (actual time=0.079..0.079 rows=0 loops=1)
               Output: checkin_table.student_id, intervention_table.intervention_type, max(points.evaluation_date), points.just_pass, compute_week_of_birthday(students.student_birthdate, 4, 'US/Central'::text), CASE WHEN ((students.student_enrolment_date IS NOT NULL) AND (students.student_enrolment_date >= '2017-01-29'::date) AND (students.student_enrolment_date < '2016-11-30'::date)) THEN 1 ELSE 0 END, students.student_enrolment_date, students.student_birthdate
               Group Key: checkin_table.student_id, students.student_enrolment_date, students.student_birthdate, points.just_pass, intervention_table.intervention_type
               Buffers: shared hit=20
               ->  Sort  (cost=74412.31..74412.48 rows=68 width=30) (actual time=0.078..0.078 rows=0 loops=1)
                     Output: checkin_table.student_id, intervention_table.intervention_type, points.just_pass, students.student_enrolment_date, students.student_birthdate, points.evaluation_date
                     Sort Key: checkin_table.student_id, students.student_enrolment_date, students.student_birthdate, points.just_pass, intervention_table.intervention_type
                     Sort Method: quicksort  Memory: 25kB
                     Buffers: shared hit=20
                     ->  Nested Loop Left Join  (cost=70384.64..74410.24 rows=68 width=30) (actual time=0.035..0.035 rows=0 loops=1)
                           Output: checkin_table.student_id, intervention_table.intervention_type, points.just_pass, students.student_enrolment_date, students.student_birthdate, points.evaluation_date
                           Buffers: shared hit=6
                           ->  Nested Loop Left Join  (cost=70384.08..74151.91 rows=1 width=22) (actual time=0.035..0.035 rows=0 loops=1)
                                 Output: checkin_table.student_id, intervention_table.intervention_type, students.student_birthdate, students.student_enrolment_date
                                 Buffers: shared hit=6
                                 ->  Nested Loop  (cost=8.90..25.46 rows=1 width=16) (actual time=0.034..0.034 rows=0 loops=1)
                                       Output: checkin_table.student_id, students.student_birthdate, students.student_enrolment_date
                                       Buffers: shared hit=6
                                       ->  Group  (cost=8.46..8.47 rows=2 width=8) (actual time=0.034..0.034 rows=0 loops=1)
                                             Output: checkin_table.student_id
                                             Group Key: checkin_table.student_id
                                             Buffers: shared hit=6
                                             ->  Sort  (cost=8.46..8.47 rows=2 width=8) (actual time=0.033..0.033 rows=0 loops=1)
                                                   Output: checkin_table.student_id
                                                   Sort Key: checkin_table.student_id
                                                   Sort Method: quicksort  Memory: 25kB
                                                   Buffers: shared hit=6
                                                   ->  Append  (cost=0.00..8.45 rows=2 width=8) (actual time=0.027..0.027 rows=0 loops=1)
                                                         Buffers: shared hit=6
                                                         ->  Seq Scan on public.checkin_table  (cost=0.00..0.00 rows=1 width=8) (actual time=0.002..0.002 rows=0 loops=1)
                                                               Output: checkin_table.student_id
                                                               Filter: ((checkin_table.checkin_time > '2019-06-11 01:00:40+00'::timestamp with time zone) AND (checkin_table.checkin_time <= '2019-06-11 01:00:50+00'::timestamp with time zone) AND (checkin_table.house_id = 9001))
                                                         ->  Index Scan using checkins_y2019_m6_house_id_timestamp_idx on public.checkins_y2019_m6  (cost=0.43..8.45 rows=1 width=8) (actual time=0.024..0.024 rows=0 loops=1)
                                                               Output: checkins_y2019_m6.student_id
                                                               Index Cond: ((checkins_y2019_m6.house_id = 9001) AND (checkins_y2019_m6.checkin_time > '2019-06-11 01:00:40+00'::timestamp with time zone) AND (checkins_y2019_m6.checkin_time <= '2019-06-11 01:00:50+00'::timestamp with time zone))
                                                               Buffers: shared hit=6
                                       ->  Index Scan using students_student_id_idx on public.students  (cost=0.43..8.47 rows=1 width=16) (never executed)
                                             Output: students.student_type, students.house_id, students.registration_id, students.registration_status, students.registration_type_status, students.total_non_core_subjects, students.registration_source, students.total_core_subjects, students.registration_type_description, students.non_access_flag, students.address_1, students.address_2, students.city, students.state, students.zipcode, students.registration_created_date, students.registration_activation_date, students.registration_cancellation_request_date, students.registration_termination_date, students.cancellation_reason, students.monthly_dues, students.student_id, students.student_type, students.student_status, students.student_first_name, students.student_last_name, students.email_address, students.student_enrolment_date, students.student_birthdate, students.student_gender, students.insert_time, students.update_time
                                             Index Cond: (students.student_id = checkin_table.student_id)
                                             Filter: ((lower((students.registration_type_description)::text) !~* '.*temporary.*'::text) AND (date_part('year'::text, age((CURRENT_DATE)::timestamp with time zone, (students.student_birthdate)::timestamp with time zone)) >= '18'::double precision))
                                 ->  Nested Loop  (cost=70375.18..74126.43 rows=2 width=14) (never executed)
                                       Output: intervention_table.intervention_type, intervention_table.student_id
                                       Join Filter: (intervention_table.intervention_date = (max(intervention_table_1.intervention_date)))
                                       ->  HashAggregate  (cost=70374.75..70376.75 rows=200 width=8) (never executed)
                                             Output: (max(intervention_table_1.intervention_date))
                                             Group Key: max(intervention_table_1.intervention_date)
                                             ->  HashAggregate  (cost=57759.88..63366.49 rows=560661 width=16) (never executed)
                                                   Output: max(intervention_table_1.intervention_date), intervention_table_1.student_id
                                                   Group Key: intervention_table_1.student_id
                                                   ->  Seq Scan on public.intervention_table intervention_table_1  (cost=0.00..46349.25 rows=2282125 width=16) (never executed)
                                                         Output: intervention_table_1.record_id, intervention_table_1.student_id, intervention_table_1.intervention_type, intervention_table_1.intervention_date, intervention_table_1.house_id, intervention_table_1.teacher_id, intervention_table_1.expiration_date, intervention_table_1.point_at_intervention
                                       ->  Index Scan using intervention_table_student_id_idx on public.intervention_table  (cost=0.43..18.70 rows=4 width=22) (never executed)
                                             Output: intervention_table.record_id, intervention_table.student_id, intervention_table.intervention_type, intervention_table.intervention_date, intervention_table.house_id, intervention_table.teacher_id, intervention_table.expiration_date, intervention_table.point_at_intervention
                                             Index Cond: (checkin_table.student_id = intervention_table.student_id)
                           ->  Index Scan using students_latest_points_idx on public.students_points points  (cost=0.57..257.65 rows=68 width=16) (never executed)
                                 Output: points.record_id, points.student_id, points.registration_id, points.house_id, points.evaluation_date, points.just_pass, points.five_star, points.star1, points.star2, points.star3, points.star4, points.updatedate
                                 Index Cond: (checkin_table.student_id = points.student_id)
         SubPlan 1
           ->  Limit  (cost=0.57..0.61 rows=1 width=4) (never executed)
                 Output: students_points.evaluation_date
                 ->  Index Only Scan Backward using students_points_evaluation_date_idx on public.students_points  (cost=0.57..4984972.45 rows=111195272 width=4) (never executed)
                       Output: students_points.evaluation_date
                       Heap Fetches: 0
 Planning time: 23.993 ms
 Execution time: 17.648 ms
(72 rows) 
_

PS:テーブルの名前と属性は、プライバシー保護のために置き換えられています。最初は、_students_points_テーブルを年ごとに分割できるようですが、これは、私が指定できない理由のためにチームが開いているオプションではなく、以下に基づいて分割することは意味がありません。今年は、ほとんどの結合が_student_id_で行われ、_student_id_でパーティション化すると100万以上のパーティションが作成されるためです。

Jjanes comment に対処するように編集されました。

  1. _checkin_table_は空のようです-_checkin_table_は分割テーブルです。クエリは実際にパーティションをヒットします-実際にはデータがある_checkins_y2019_m6_。
  2. このクエリが原因であると思われた理由は何ですか?-ピーク時にPGBadgerを使用する場合、40のDB接続のうち30が待機状態。これらの接続のクエリを見ると、上記と同じクエリですが、_house_id_と_checkin_time_の組み合わせが異なります。また、RDSインサイト(上記の画像1)から、スクリーンショットの下部を見ると、Load By Waits(AAS)そして、棒グラフの2/3が明るい青色(IOWait)で、1/3が緑(CPU)であり、対応するクエリであることがわかります。添付のpgbadgerビューを見てください(クエリの詳細を編集しました)。このクエリは、最も時間がかかるクエリです。 Redacted_PgBadger_Long_Running_Query
  3. _pg_stat_statements_-はい、確認しました。これは、PG Badgerと一致するtotal_time descのトップクエリです。 pg_stat_statements
  4. _auto_explain_は実行可能に見えます。たった一つの質問-それは何らかの形でパフォーマンスを妨げますか?
  5. についてIOチャーンと最も遅いクエリ-私は同意しますが、行き止まりに到達し、アイデアが不足しています。一時ファイルに書き込むすべてのクエリを確認しているわけではないため、バッファが大量に消費され、IOWaitが発生する可能性があります。
1
gvatreya

「evaluation_date」の値は、GroupAggregateから行を除外するためにのみ必要です。これは行を返さないため、「evaluation_date」の値を見つけるサブプランを実行する必要はありません。

あなたはそのコストを心配するべきではありません。クエリのその部分が実行された場合でも、LIMITにより高速です。ノードの報告されたコスト見積もりは、それが完了するまで実行されるという仮定の下にあります。そのコストの比例配分は、その上のLIMITノードの仕事です。

テーブルcheckin_tableは空のようです。それはかなり奇妙ですね。

過剰なBufFileWriteを引き起こしているのは、このクエリではなく、少なくとも表示するクエリのインスタンスではありません。おそらく、空でないcheckin_tableで実行される他の機会があるでしょう。このクエリが犯人だと思ったのはなぜですか?いくつかの異なるツールについて言及しましたが、誤って解釈したLIMITedインデックススキャンの総コストの見積もりを除いて、特にこれらのツールが何を示しているかについては言及しませんでした。

pg_stat_statements を使用して、平均または全体でどのクエリが遅いかを確認できます。また、 "log_min_duration_statement"以上を使用すると、 auto_explain を使用して特定のクエリを確認できます実行は個別に遅くなります。

最も遅いクエリがこのすべてのIOチャーンを引き起こしているとは限りませんが、そのようなIOチャーンを引き起こしているものは、少なくとも遅いので、あなたの最善の策は最も遅いものから始めることです。

1
jjanes