Почему в Postgresql запрос MAX с фильтром равенства для одного другого столбца так медленен? - PullRequest
1 голос
/ 29 марта 2019

Я столкнулся с проблемой в PostgreSQL (версия 9.6.10), когда индексы не работают для ускорения запроса MAX с помощью простого фильтра равенства для другого столбца. Логически кажется, что простой многоколонный индекс на (A, B DESC) должен сделать запрос очень быстрым.

Я не могу понять, почему я не могу заставить запрос работать, независимо от того, какие индексы определены.

Определение таблицы имеет следующее: - первичный ключ foo VARCHAR PRIMARY KEY (не используется в запросе) - Поле UUID, которое НЕ ПУСТО (NULL), называется bar UUID - Столбец sequential_id, который был создан как BIGSERIAL UNIQUE тип

Вот как именно выглядят соответствующие столбцы (имена изменены для конфиденциальности):

                                             Table "public.foo"
        Column        |           Type           |                                   Modifiers
----------------------+--------------------------+--------------------------------------------------------------------------------
 foo_uid            | character varying        | not null
 bar_uid            | uuid                     | not null
 sequential_id      | bigint                   | not null default nextval('foo_sequential_id_seq'::regclass)
Indexes:
    "foo_pkey" PRIMARY KEY, btree (foo_uid)
    "foo_bar_uid_sequential_id_idx", btree (bar_uid, sequential_id DESC)
    "foo_sequential_id_key" UNIQUE CONSTRAINT, btree (sequential_id)

Несмотря на наличие индекса, указанного выше на (bar_uid, sequential_id DESC), следующий запрос требует сканирования индекса и занимает 100-300 мс с несколькими миллионами строк в базе данных.

Запрос (получите максимум sequential_id для данного bar_uid):

SELECT MAX(sequential_id) 
FROM foo 
WHERE bar_uid = 'fa61424d-389f-4e75-ba2d-b77e6bb8491f';

Результат EXPLAIN ANALYZE не использует правильный индекс. Кроме того, по какой-то причине он проверяет, если sequential_id IS NOT NULL, даже если он объявлен как not null.

                                                                                             QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Result  (cost=0.75..0.76 rows=1 width=8) (actual time=321.110..321.110 rows=1 loops=1)
   InitPlan 1 (returns $0)
     ->  Limit  (cost=0.43..0.75 rows=1 width=8) (actual time=321.106..321.106 rows=1 loops=1)
           ->  Index Scan Backward using foo_sequential_id_key on foo  (cost=0.43..98936.43 rows=308401 width=8) (actual time=321.106..321.106 rows=1 loops=1)
                 Index Cond: (sequential_id IS NOT NULL)
                 Filter: (bar_uid = 'fa61424d-389f-4e75-ba2d-b77e6bb8491f'::uuid)
                 Rows Removed by Filter: 920761
 Planning time: 0.196 ms
 Execution time: 321.127 ms
(9 rows)

Я могу добавить, казалось бы, ненужный GROUP BY к этому запросу, и это немного ускоряет его, но он все еще очень медленный для запроса, который должен быть почти мгновенным с определенными индексами:

SELECT MAX(sequential_id) 
FROM foo 
WHERE bar_uid = 'fa61424d-389f-4e75-ba2d-b77e6bb8491f'
GROUP BY bar_uid;

Результат EXPLAIN (ANALYZE, BUFFERS):

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 GroupAggregate  (cost=8510.54..65953.61 rows=6 width=24) (actual time=234.529..234.530 rows=1 loops=1)
   Group Key: bar_uid
   Buffers: shared hit=1 read=11909
   ->  Bitmap Heap Scan on foo  (cost=8510.54..64411.55 rows=308401 width=24) (actual time=65.259..201.969 rows=309023 loops=1)
         Recheck Cond: (bar_uid = 'fa61424d-389f-4e75-ba2d-b77e6bb8491f'::uuid)
         Heap Blocks: exact=10385
         Buffers: shared hit=1 read=11909
         ->  Bitmap Index Scan on foo_bar_uid_sequential_id_idx  (cost=0.00..8433.43 rows=308401 width=0) (actual time=63.549..63.549 rows=309023 loops=1)
               Index Cond: (bar_uid = 'fa61424d-389f-4e75-ba2d-b77e6bb8491f'::uuid)
               Buffers: shared read=1525
 Planning time: 3.067 ms
 Execution time: 234.589 ms
(12 rows)

Кто-нибудь знает, что блокирует этот запрос порядка 10 миллисекунд? По логике это должно быть мгновенно с правильным определенным индексом. Требуется только время, чтобы перейти по ссылкам к значению листа в B-дереве.

Кто-то спросил:

Что вы получаете за SELECT * FROM pg_stats ГДЕ tablename = 'foo' и attname = 'bar_uid';?

 schemaname |       tablename        |   attname   | inherited | null_frac | avg_width | n_distinct |                                                                                                        most_common_vals                                                                                                         |                    most_common_freqs                     | histogram_bounds | correlation | most_common_elems | most_common_elem_freqs | elem_count_histogram
------------+------------------------+-------------+-----------+-----------+-----------+------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------+------------------+-------------+-------------------+------------------------+----------------------
 public     | foo                    | bar_uir     | f         |         0 |        16 |          6 | {fa61424d-389f-4e75-ba2d-b77e6bb8491f,5c5dcae9-1b7e-4413-99a1-62fde2b89c32,50b1e842-fc32-4c2c-b00f-4a17c3c1c5fa,7ff1999c-c0ea-b700-343f-9a737f6ad659,f667b353-e199-4890-9ffd-4940ea11fe2c,b24ce968-29fd-4587-ba1f-227036ee3135} | {0.203733,0.203167,0.201567,0.195867,0.1952,0.000466667} |                  |   -0.158093 |                   |                        |
(1 row)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...