ダッシュボードを実行しているすべてのデバイス(通常、ピーク時に500以上のデバイス)から5〜7秒ごとに実行されるクエリ(クエリプランに付属)があります。このクエリは、最初は待機状態_IO:BufFileWrite
_で時間を費やしているように見えます。
AWS Aurora Performance Insightsダッシュボードから、問題のクエリが_IO: BUfFileWrite
_待機状態でより多くの時間を費やしていることがわかります(グラフの水色)
Postgres設定/詳細:
work_mem = 80MB
_1週間前、アプリサーバーで多くのエラーが発生し始めました。
_
Failed to get database connection errors
_
および/または
_
Java.sql.SQLTimeoutException: Timeout after 30000ms of waiting for a connection
_
少しデバッグし、PGAdminの助けを借りて、ほとんどの接続が_IO: BufFileWrite
_で待機していることがわかり、デフォルト4MB_work_mem
_はクエリには不十分でした。
PgBadgerの助けを借りて、一時ファイルの平均サイズが70MBであることを確認したため、_work_mem
_を80MBに更新しました。
これは間違いなく助けになり、DB接続の問題が少なくなり始めましたが、完全に解消されませんでした。
Postgres Visualize Analyzer と Explain 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
を実行しましたが、目に見えるパフォーマンスの向上はありません。
以下についてサポートが必要です
never executed
_部分はどういう意味ですか? -私はウェブ上の文献をチェックしましたが、Postgresエンジン以外の満足のいく説明では、それが関連性がないとは考えられず、0行を返しました。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 に対処するように編集されました。
checkin_table
_は空のようです-_checkin_table
_は分割テーブルです。クエリは実際にパーティションをヒットします-実際にはデータがある_checkins_y2019_m6
_。house_id
_と_checkin_time
_の組み合わせが異なります。また、RDSインサイト(上記の画像1)から、スクリーンショットの下部を見ると、Load By Waits(AAS)そして、棒グラフの2/3が明るい青色(IOWait)で、1/3が緑(CPU)であり、対応するクエリであることがわかります。添付のpgbadgerビューを見てください(クエリの詳細を編集しました)。このクエリは、最も時間がかかるクエリです。 pg_stat_statements
_-はい、確認しました。これは、PG Badgerと一致するtotal_time descのトップクエリです。 auto_explain
_は実行可能に見えます。たった一つの質問-それは何らかの形でパフォーマンスを妨げますか?「evaluation_date」の値は、GroupAggregateから行を除外するためにのみ必要です。これは行を返さないため、「evaluation_date」の値を見つけるサブプランを実行する必要はありません。
あなたはそのコストを心配するべきではありません。クエリのその部分が実行された場合でも、LIMITにより高速です。ノードの報告されたコスト見積もりは、それが完了するまで実行されるという仮定の下にあります。そのコストの比例配分は、その上のLIMITノードの仕事です。
テーブルcheckin_tableは空のようです。それはかなり奇妙ですね。
過剰なBufFileWriteを引き起こしているのは、このクエリではなく、少なくとも表示するクエリのインスタンスではありません。おそらく、空でないcheckin_tableで実行される他の機会があるでしょう。このクエリが犯人だと思ったのはなぜですか?いくつかの異なるツールについて言及しましたが、誤って解釈したLIMITedインデックススキャンの総コストの見積もりを除いて、特にこれらのツールが何を示しているかについては言及しませんでした。
pg_stat_statements を使用して、平均または全体でどのクエリが遅いかを確認できます。また、 "log_min_duration_statement"以上を使用すると、 auto_explain を使用して特定のクエリを確認できます実行は個別に遅くなります。
最も遅いクエリがこのすべてのIOチャーンを引き起こしているとは限りませんが、そのようなIOチャーンを引き起こしているものは、少なくとも遅いので、あなたの最善の策は最も遅いものから始めることです。