Почему оптимизатор БД не использует сканирование только по индексу даже после добавления индекса? - PullRequest
0 голосов
/ 19 января 2019

Я запускаю команду EXPLAIN с SQL и получаю план.

explain analyze SELECT "users2_device2"."id", "users2_device2"."device_token", "users2_device2"."os_type"
FROM "users2_device2"
WHERE (
    "users2_device2"."user_id" IN (

        SELECT V0."user_id"
        FROM "users2_pushsetting2" V0
        WHERE (V0."user_id" IN (

            SELECT U0."from_user_id"
            FROM "users2_followingusers" U0
            WHERE (
                U0."to_user_id" = 1700019
                AND U0."favorite" = true
            )
        ) AND V0."live" = true)

    ) AND "users2_device2"."status" = 0
);

.

+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| QUERY PLAN                                                                                                                                                                                                                             |
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Hash Semi Join  (cost=36089.60..70812.80 rows=99600 width=6) (actual time=1306.602..1601.587 rows=100000 loops=1)                                                                                                                      |
|   Output: users2_device2.id, users2_device2.device_token, users2_device2.os_type                                                                                                                                                       |
|   Hash Cond: (users2_device2.user_id = v0.user_id)                                                                                                                                                                                     |
|   ->  Seq Scan on public.users2_device2  (cost=0.00..20834.12 rows=1000010 width=10) (actual time=0.006..382.735 rows=1000010 loops=1)                                                                                                 |
|         Output: users2_device2.id, users2_device2.device_token, users2_device2.os_type, users2_device2.user_id                                                                                                                         |
|         Filter: (users2_device2.status = 0)                                                                                                                                                                                            |
|   ->  Hash  (cost=34454.60..34454.60 rows=99600 width=8) (actual time=803.194..803.194 rows=100000 loops=1)                                                                                                                            |
|         Output: v0.user_id, u0.from_user_id                                                                                                                                                                                            |
|         Buckets: 131072  Batches: 2  Memory Usage: 2984kB                                                                                                                                                                              |
|         ->  Hash Semi Join  (cost=5163.43..34454.60 rows=99600 width=8) (actual time=504.350..769.445 rows=100000 loops=1)                                                                                                             |
|               Output: v0.user_id, u0.from_user_id                                                                                                                                                                                      |
|               Hash Cond: (v0.user_id = u0.from_user_id)                                                                                                                                                                                |
|               ->  Seq Scan on public.users2_pushsetting2 v0  (cost=0.00..17354.10 rows=1000010 width=4) (actual time=0.002..318.386 rows=1000010 loops=1)                                                                              |
|                     Output: v0.user_id                                                                                                                                                                                                 |
|                     Filter: v0.live                                                                                                                                                                                                    |
|               ->  Hash  (cost=3528.43..3528.43 rows=99600 width=4) (actual time=62.920..62.920 rows=100000 loops=1)                                                                                                                    |
|                     Output: u0.from_user_id                                                                                                                                                                                            |
|                     Buckets: 131072  Batches: 2  Memory Usage: 2788kB                                                                                                                                                                  |
|                     ->  Index Only Scan using users2_followingusers_to_user_id_favorite_from_ba2688fe_uniq on public.users2_followingusers u0  (cost=0.42..3528.43 rows=99600 width=4) (actual time=0.011..30.535 rows=100000 loops=1) |
|                           Output: u0.from_user_id                                                                                                                                                                                      |
|                           Index Cond: ((u0.to_user_id = 1700019) AND (u0.favorite = true))                                                                                                                                             |
|                           Filter: u0.favorite                                                                                                                                                                                          |
|                           Heap Fetches: 0                                                                                                                                                                                              |
| Planning time: 0.420 ms                                                                                                                                                                                                                |
| Execution time: 1621.003 ms                                                                                                                                                                                                            |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

Обратите внимание на Seq Scan on public.users2_pushsetting2.

Я думал, что если я добавлю индекс для (live, user_id), он будет ускорен при сканировании только по индексу.

Тем не менее, он замедляет мое приложение, хотя и использует сканирование только по индексу.

+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| QUERY PLAN
|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Nested Loop Semi Join  (cost=0.90..64437.19 rows=99589 width=6) (actual time=3127.140..4772.221 rows=100000 loops=1)
|   ->  Seq Scan on users2_device2  (cost=0.00..20833.25 rows=999940 width=10) (actual time=0.007..383.861 rows=1000010 loops=1)
|         Filter: (status = 0)
|   ->  Hash Semi Join  (cost=0.90..0.93 rows=1 width=8) (actual time=0.004..0.004 rows=0 loops=1000010)
|         Hash Cond: (v0.user_id = u0.from_user_id)
|         ->  Index Only Scan using users2_pushsetting2_user_id_live_e89f5671_uniq on users2_pushsetting2 v0  (cost=0.42..0.46 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=100
|               Index Cond: ((user_id = users2_device2.user_id) AND (live = true))
|               Filter: live
|               Heap Fetches: 0
|         ->  Hash  (cost=0.46..0.46 rows=1 width=4) (actual time=0.002..0.002 rows=0 loops=1000010)
|               Buckets: 1024  Batches: 1  Memory Usage: 8kB
|               ->  Index Only Scan using users2_followingusers_to_user_id_favorite_from_ba2688fe_uniq on users2_followingusers u0  (cost=0.42..0.46 rows=1 width=4) (actual time=0.002..
|                     Index Cond: ((to_user_id = 1700019) AND (favorite = true) AND (from_user_id = users2_device2.user_id))
|                     Filter: favorite
|                     Heap Fetches: 0
| Planning time: 0.459 ms
| Execution time: 4793.639 ms
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Похоже, что условие (from_user_id = users2_device2.user_id) перемещено в корневой узел, а Hash Semi Join удалено. (Условия в оптимизаторе)

Таким образом, я стараюсь, чтобы Оптимизатор не выравнивал состояние, добавляя `OFFSET 0, как показано ниже.

explain analyze SELECT "users2_device2"."id", "users2_device2"."device_token", "users2_device2"."os_type"
FROM "users2_device2"
WHERE (
    "users2_device2"."user_id" IN (

        SELECT V0."user_id"
        FROM "users2_pushsetting2" V0
        WHERE (V0."user_id" IN (

            SELECT U0."from_user_id"
            FROM "users2_followingusers" U0
            WHERE (
                U0."to_user_id" = 1700019
                AND U0."favorite" = true
            ) OFFSET 0
        ) AND V0."live" = true)

    ) AND "users2_device2"."status" = 0
);

Однако, это восходит к первому плану, не использующему сканирование только по индексу.

Что мне не хватает?

...