Я пытаюсь диагностировать медленный запрос к материализованному представлению в Postgresql (10.12). Вкратце, у меня есть две таблицы activity
и task
плюс материализованное представление activity_status_view
, которое по сути является подмножеством строк activity
плюс несколько дополнительных столбцов, добавленных в них, которые описывают «статус» действия в системе.
Выполнение простого запроса, объединяющего две таблицы, выполняется быстро, но один и тот же запрос, объединяющий представление с таблицей task
, неоправданно медленен. Запрос, который строит материализованное представление, большой и уродливый, но, насколько я понимаю, он должен вести себя точно так же, как обычная таблица для чего-то подобного. Еще одно наблюдение заключается в том, что «обновление» рассматриваемого материализованного представления также очень быстро (менее секунды).
Я вычеркнул объяснение запроса + подробности таблицы (с удалением некоторых ненужных столбцов). Кто-нибудь может объяснить несоответствие? Может ли запрос, который строит материализованное представление, быть уместным здесь?
> EXPLAIN ANALYSE SELECT * FROM task t JOIN activity_status_view a ON t.args->>'activityId' = a.id::text LIMIT 1
+-----------------------------------------------------------------------------------------------------------------------------------------------+
| QUERY PLAN |
|-----------------------------------------------------------------------------------------------------------------------------------------------|
| Limit (cost=0.00..7.43 rows=1 width=861) (actual time=1866719.779..1866719.779 rows=0 loops=1) |
| -> Nested Loop (cost=0.00..187536570.93 rows=25235882 width=861) (actual time=1866719.777..1866719.777 rows=0 loops=1) |
| Join Filter: ((t.args ->> 'activityId'::text) = (a.id)::text) |
| Rows Removed by Join Filter: 5047176465 |
| -> Seq Scan on task t (cost=0.00..9197.05 rows=85905 width=766) (actual time=0.013..95.792 rows=85905 loops=1) |
| -> Materialize (cost=0.00..2706.30 rows=58753 width=95) (actual time=0.002..4.804 rows=58753 loops=85905) |
| -> Seq Scan on activity_status_view a (cost=0.00..1551.53 rows=58753 width=95) (actual time=0.008..5.978 rows=58753 loops=1) |
| Planning time: 0.231 ms |
| Execution time: 1866720.757 ms |
+-----------------------------------------------------------------------------------------------------------------------------------------------+
EXPLAIN
Time: 1866.740s (31 minutes), executed in: 1866.722s (31 minutes)
> EXPLAIN ANALYSE SELECT * FROM task t JOIN activity a ON t.args->>'activityId' = a.id::text LIMIT 1
+------------------------------------------------------------------------------------------------------------------------------------+
| QUERY PLAN |
|------------------------------------------------------------------------------------------------------------------------------------|
| Limit (cost=0.00..6.45 rows=1 width=819) (actual time=14.541..14.541 rows=1 loops=1) |
| -> Nested Loop (cost=0.00..293143952.80 rows=45420551 width=819) (actual time=14.540..14.540 rows=1 loops=1) |
| Join Filter: ((t.args ->> 'activityId'::text) = (a.id)::text) |
| Rows Removed by Join Filter: 14753 |
| -> Seq Scan on task t (cost=0.00..9197.05 rows=85905 width=766) (actual time=0.010..0.010 rows=1 loops=1) |
| -> Materialize (cost=0.00..3710.19 rows=105746 width=53) (actual time=0.007..5.481 rows=14754 loops=1) |
| -> Seq Scan on activity a (cost=0.00..2148.46 rows=105746 width=53) (actual time=0.005..1.488 rows=14754 loops=1) |
| Planning time: 0.151 ms |
| Execution time: 14.835 ms |
+------------------------------------------------------------------------------------------------------------------------------------+
EXPLAIN
Time: 0.036s
> SELECT count(*) FROM activity_status_view
+---------+
| count |
|---------|
| 58753 |
+---------+
SELECT 1
Time: 0.021s
> SELECT count(*) FROM activity
+---------+
| count |
|---------|
| 105746 |
+---------+
SELECT 1
Time: 0.036s
> \d activity
+--------------------------------------+-----------------------------+-------------+
| Column | Type | Modifiers |
|--------------------------------------+-----------------------------+-------------|
| id | uuid | not null |
| frame_range | int4range | not null |
+--------------------------------------+-----------------------------+-------------+
Indexes:
"pk_activity" PRIMARY KEY, btree (id)
Referenced by:
TABLE "activity_status_change" CONSTRAINT "fk_activity_status_change_activity_id_activity" FOREIGN KEY (activity_id) REFERENCES activity(id) ON DELETE CASCADE
> \d activity_status_view
+--------------------------------------+-----------------------------+-------------+
| Column | Type | Modifiers |
|--------------------------------------+-----------------------------+-------------|
| id | uuid | |
| frame_range | int4range | |
| video_activity_type_id | uuid | |
| status_change_id | uuid | |
| version | integer | |
| status | character varying | |
+--------------------------------------+-----------------------------+-------------+
Indexes:
"uq_activity_status_view_id" UNIQUE, btree (id)
> \d task
+--------------------------------+-----------------------------+-------------+
| Column | Type | Modifiers |
|--------------------------------+-----------------------------+-------------|
| id | uuid | not null |
| args | jsonb | not null |
+--------------------------------+-----------------------------+-------------+
Indexes:
"pk_task" PRIMARY KEY, btree (id)
TABLE "activity_status_change" CONSTRAINT "fk_activity_status_change_task_id_task" FOREIGN KEY (task_id) REFERENCES task(id) ON DELETE SET NULL
Следующее является одинаково медленным:
SELECT * FROM task t
JOIN activity a ON t.args->>'activityId' = a.id::text
WHERE a.id IN (SELECT id FROM activity_status_view)
LIMIT 1