Redshift: Nested Loop Join в плане запроса - PullRequest
0 голосов
/ 23 января 2019

У меня есть таблица фактов t_session, которая выглядит упрощенно так:

+------------+----------+-------------+
| start_hm   | end_hm   | device_id   |
|------------+----------+-------------|
| 0          | 10       | 111         |
| 2          | 10       | 112         |
| 12         | 20       | 113         |
| 60         | 90       | 111         |
| 60         | 90       | 112         |

У меня также есть таблица измерений dim_time, которая содержит 1440 записей с часами 0-23 и минутами 0-59за каждый час.Таким образом, он содержит все комбинации часов-минут за день.tk это диапазон чисел 0-1439

+------+--------+----------+
| tk   | hour   | minute   |
|------+--------+----------|
| 0    | 0      | 0        |
| 1    | 0      | 1        |
| 2    | 0      | 2        |
............................
| 60   | 1      | 0        |
| 61   | 1      | 1        |
| 62   | 1      | 2        |
............................
| 120  | 2      | 0        |
| 121  | 2      | 1        |
| 122  | 2      | 2        |
............................

Я хочу посчитать количество активных device_id для каждой минуты.В реальном приложении есть еще одна таблица dim_date и полдюжины других отношений, но давайте будем простыми в этом вопросе.

Устройство активно во временном интервале между start_hm и end_hmstart_hm, и end_hm имеют значения от 0 до 1439.

select count(distinct device_id)
from t_session
join dim_time on tk between start_hm and end_hm
group by tk
order by tk;

Этот запрос выполняется медленно, как в аду.Когда я смотрю на план выполнения, он жалуется на вложенный цикл.

+--------------------------------------------------------------------------------------------------------------------------------+
| QUERY PLAN                                                                                                                     |
|--------------------------------------------------------------------------------------------------------------------------------|
| XN Limit  (cost=1000002000820.94..1000002000820.97 rows=10 width=8)                                                            |
|   ->  XN Merge  (cost=1000002000820.94..1000002000821.44 rows=200 width=8)                                                     |
|         Merge Key: tk                                                                                                          |
|         ->  XN Network  (cost=1000002000820.94..1000002000821.44 rows=200 width=8)                                             |
|               Send to leader                                                                                                   |
|               ->  XN Sort  (cost=1000002000820.94..1000002000821.44 rows=200 width=8)                                          |
|                     Sort Key: tk                                                                                               |
|                     ->  XN HashAggregate  (cost=2000812.80..2000813.30 rows=200 width=8)                                       |
|                           ->  XN Subquery Scan volt_dt_0  (cost=2000764.80..2000796.80 rows=3200 width=8)                      |
|                                 ->  XN HashAggregate  (cost=2000764.80..2000764.80 rows=3200 width=8)                          |
|                                       ->  XN Nested Loop DS_BCAST_INNER  (cost=0.00..2000748.80 rows=3200 width=8)             |
|                                             Join Filter: (("outer".tk <= "inner".end_hm) AND ("outer".tk >= "inner".start_hm)) |
|                                             ->  XN Seq Scan on dim_time  (cost=0.00..28.80 rows=2880 width=4)                  |
|                                             ->  XN Seq Scan on t_session  (cost=0.00..0.10 rows=10 width=12)                   |
| ----- Nested Loop Join in the query plan - review the join predicates to avoid Cartesian products -----                        |
+--------------------------------------------------------------------------------------------------------------------------------+

Я понимаю, откуда берется вложенный цикл.Для каждой записи в t_session необходимо выполнить цикл по dim_time.

Можно ли изменить мой запрос, чтобы избежать вложенного цикла и повысить производительность?

ОБНОВЛЕНИЕ: тот же запросработает на Postgres невероятно быстро, и план выполнения не содержит декартово произведение.

+--------------------------------------------------------------------------------------------------------------+
| QUERY PLAN                                                                                                   |
|--------------------------------------------------------------------------------------------------------------|
| Limit  (cost=85822.07..85839.17 rows=10 width=12)                                                            |
|   ->  GroupAggregate  (cost=85822.07..88284.47 rows=1440 width=12)                                           |
|         Group Key: dim_time.tk                                                                               |
|         ->  Sort  (cost=85822.07..86638.07 rows=326400 width=8)                                              |
|               Sort Key: dim_time.tk                                                                          |
|               ->  Nested Loop  (cost=0.00..51467.40 rows=326400 width=8)                                     |
|                     Join Filter: ((dim_time.tk >= t_session.start_hm) AND (dim_time.tk <= t_session.end_hm)) |
|                     ->  Seq Scan on t_session  (cost=0.00..30.40 rows=2040 width=12)                         |
|                     ->  Materialize  (cost=0.00..32.60 rows=1440 width=4)                                    |
|                           ->  Seq Scan on dim_time  (cost=0.00..25.40 rows=1440 width=4)                     |
+--------------------------------------------------------------------------------------------------------------+

UPDATE2:

Таблица t_session имеет столбец device_id в виде DISTKEY и столбец start_date(не показано в упрощенном примере) как SORTKEY: сеансы естественным образом сортируются по start_date.

Таблица dim_time имеет tk как SORTKEY и DISTSTYLE ALL.

Время выполнения 40000 сеансов в день составляет 5-6 минут в Redshift.И пару секунд на Postgres.

В кластере Redshift есть два узла dc2.large

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