Медленный запрос при соединении с материализованным представлением - PullRequest
0 голосов
/ 05 марта 2020

Я пытаюсь диагностировать медленный запрос к материализованному представлению в 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

1 Ответ

1 голос
/ 05 марта 2020

activity_status_view, который по сути является подмножеством строк действия

В этом подмножестве выбранных строк исключены все те, которые фактически соответствуют условию соединения. Таким образом, LIMIT никогда не срабатывает и позволяет завершить запрос рано. Принимая во внимание этот факт, лучшим планом было бы присоединение ha sh или использование индекса на activity_status_view.id или на task.args ->> 'activityId' (вы могли бы попытаться создать последний из них), но если статистика далеко он не может этого понять.

Поскольку распределение значений - >> 'activityId' не видно планировщику (находящемуся внутри JSONB), он не может знать, как часто эти значения могут пересекаться. Создание индекса выражения может помочь ему в этом разобраться (запустите ANALYZE для таблицы после создания индекса), поэтому он может использовать статистику из индекса для решения проблемы планирования, фактически не используя индекс в плане.

Это просто совпадение, что запрос MV исключает все совпадающие строки, или это по замыслу?

Вы VACUUM ANALYZE материализовали представление после его обновления?

Это маловероятно что это как-то связано с тем фактом, что это материализованное представление. Если бы вы сделали это автономной таблицей (CREATE TABLE независимо от того, как выбирается ...) или обычным нематериализованным представлением, у вас, вероятно, возникла бы та же проблема.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...