почему Postgres предпочитает сканирование seq частичному индексу с явным условием где? - PullRequest
0 голосов
/ 22 февраля 2019

У меня простой запрос типа select * from xxx where col is not null limit 10.Я не знаю, почему Postgres предпочитает сканирование seq, которое намного медленнее, чем частичный индекс (я проанализировал таблицу).Как отладить такую ​​проблему?

В таблице более 4 миллионов строк.И около 350 000 строк удовлетворены pid is not null.

Я думаю, что может быть что-то не так с оценкой стоимости.Стоимость последующего сканирования ниже, чем индексное сканирование.Но как копаться в этом?

У меня есть предположение, но я не уверен в этом.Не пустые строки занимают около 10% всех строк.Это означает, что он может получить 10 строк, которые не равны нулю, при последующем сканировании 100 строк.И он считает, что стоимость последовательного сканирования 100 строк ниже, чем индексное сканирование 10 строк, а затем случайная выборка 10 полных строк.Это 1009

> \d data_import
+--------------------+--------------------------+----------------------------------------------------------------------------+
| Column             | Type                     | Modifiers                                                                  |
|--------------------+--------------------------+----------------------------------------------------------------------------|
| id                 | integer                  |  not null default nextval('data_import_id_seq'::regclass) |
| name               | character varying(64)    |                                                                            |
| market_activity_id | integer                  |  not null                                                                  |
| hmsr_id            | integer                  |  not null default (-1)                                                     |
| site_id            | integer                  |  not null default (-1)                                                     |
| hmpl_id            | integer                  |  not null default (-1)                                                     |
| hmmd_id            | integer                  |  not null default (-1)                                                     |
| hmci_id            | integer                  |  not null default (-1)                                                     |
| hmkw_id            | integer                  |  not null default (-1)                                                     |
| creator_id         | integer                  |                                                                            |
| created_at         | timestamp with time zone |                                                                            |
| updated_at         | timestamp with time zone |                                                                            |
| bias               | integer                  |                                                                            |
| pid                | character varying(128)   |  default NULL::character varying                                           |
+--------------------+--------------------------+----------------------------------------------------------------------------+
Indexes:
    "data_import_pkey" PRIMARY KEY, btree (id)
    "unique_hmxx" UNIQUE, btree (site_id, hmsr_id, hmpl_id, hmmd_id, hmci_id, hmkw_id) WHERE pid IS NULL
    "data_import_pid_idx" UNIQUE, btree (pid) WHERE pid IS NOT NULL
    "data_import_created_at_idx" btree (created_at)
    "data_import_hmsr_id" btree (hmsr_id)
    "data_import_updated_at_idx" btree (updated_at)



> set enable_seqscan to false;
apollon> explain (analyse, verbose)  select * from data_import where pid is not null limit 10
+-------------------------------------------------------------------------------------------------------------------------------------------------------------
| QUERY PLAN
|-------------------------------------------------------------------------------------------------------------------------------------------------------------
| Limit  (cost=0.42..5.68 rows=10 width=84) (actual time=0.059..0.142 rows=10 loops=1)
|   Output: id, name, market_activity_id, hmsr_id, site_id, hmpl_id, hmmd_id, hmci_id, hmkw_id, creator_id, created_at, updated_at, bias, pid
|   ->  Index Scan using data_import_pid_idx on public.data_import  (cost=0.42..184158.08 rows=350584 width=84) (actual time
|         Output: id, name, market_activity_id, hmsr_id, site_id, hmpl_id, hmmd_id, hmci_id, hmkw_id, creator_id, created_at, updated_at, bias, pid
|         Index Cond: (data_import.pid IS NOT NULL)
| Planning time: 0.126 ms
| Execution time: 0.177 ms
+-------------------------------------------------------------------------------------------------------------------------------------------------------------
EXPLAIN
Time: 0.054s

> set enable_seqscan to true;
> explain (analyse, verbose)  select * from data_import where pid is not null limit 10
+---------------------------------------------------------------------------------------------------------------------------------------------------+
| QUERY PLAN                                                                                                                                        |
|---------------------------------------------------------------------------------------------------------------------------------------------------|
| Limit  (cost=0.00..2.37 rows=10 width=84) (actual time=407.042..407.046 rows=10 loops=1)                                                          |
|   Output: id, name, market_activity_id, hmsr_id, site_id, hmpl_id, hmmd_id, hmci_id, hmkw_id, creator_id, created_at, updated_at, bias, pid       |
|   ->  Seq Scan on public.data_import  (cost=0.00..83016.60 rows=350584 width=84) (actual time=407.041..407.045 rows=10 loops=1)  |
|         Output: id, name, market_activity_id, hmsr_id, site_id, hmpl_id, hmmd_id, hmci_id, hmkw_id, creator_id, created_at, updated_at, bias, pid |
|         Filter: (data_import.pid IS NOT NULL)                                                                                    |
|         Rows Removed by Filter: 3672502                                                                                                           |
| Planning time: 0.116 ms                                                                                                                           |
| Execution time: 407.078 ms                                                                                                                        |
+---------------------------------------------------------------------------------------------------------------------------------------------------+
EXPLAIN
Time: 0.426s

1 Ответ

0 голосов
/ 22 февраля 2019

Ваша проблема:

Rows Removed by Filter: 3672502

. PostgreSQL знает распределение значений и как они соотносятся с физической разметкой таблицы, но не знает, что все строки в начале таблицы имеютNULL для pid.

Если бы значения NULL были равномерно распределены, последовательное сканирование быстро нашло бы 10 совпадений и остановилось, но на самом деле, оно должно прочитать 3672512 строк, чтобы найти 10 совпадающих.

Если вы добавите ORDER BY pid (даже если вам это не нужно) перед LIMIT, оптимизатор поступит правильно.

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