Это правильный способ создания частичного индекса в Postgres? - PullRequest
0 голосов
/ 16 октября 2018

У нас есть таблица с 4 миллионами записей, и для конкретного часто используемого сценария использования нас интересуют только записи с определенным типом продаж salesforce «Standard», которые составляют всего около 10 000 из 4 миллионов.Другими типами пользователей, которые могут существовать, являются «PowerPartner», «CSPLitePortal», «CustomerSuccess», «PowerCustomerSuccess» и «CsnOnly».

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

Итак, я планирую создать этот частичный индекс для ускорения запросов к записям с пользовательским типом «Стандарт» и предотвращения тайм-аута запроса из Интернета:

CREATE INDEX user_type_idx ON user_table(userType)
WHERE userType NOT IN
   ('PowerPartner', 'CSPLitePortal', 'CustomerSuccess', 'PowerCustomerSuccess', 'CsnOnly');

Запрос на поиск будет

select * from user_table where userType='Standard';

Не могли бы вы подтвердить, что это правильный способ создания частичного индекса?Это было бы очень полезно.

Ответы [ 2 ]

0 голосов
/ 16 октября 2018

Postgres может использовать это, но делает это немного (несколько) менее эффективно, чем индекс, указывающий where user_type = 'Standard'.

Я создал небольшую тестовую таблицу с 4 миллионами строк, из которых 10.000 имеюттип_пользователя 'Standard'.Другие значения были распределены случайным образом с помощью следующего сценария:

create table user_table
(
  id serial primary key,
  some_date date not null,
  user_type text not null,
  some_ts timestamp not null, 
  some_number integer not null, 
  some_data text, 
  some_flag boolean
);

insert into user_table (some_date, user_type, some_ts, some_number, some_data, some_flag)
select current_date, 
       case (random() * 4 + 1)::int
         when 1 then 'PowerPartner'
         when 2 then 'CSPLitePortal'
         when 3 then 'CustomerSuccess'
         when 4 then 'PowerCustomerSuccess'
         when 5 then 'CsnOnly'
       end, 
       clock_timestamp(),
       42, 
       rpad(md5(random()::text), (random() * 200 + 1)::int, md5(random()::text)), 
       (random() + 1)::int = 1
from generate_series(1,4e6 - 10000) as t(i)
union all 
select current_date, 
       'Standard',
       clock_timestamp(),
       42, 
       rpad(md5(random()::text), (random() * 200 + 1)::int, md5(random()::text)), 
       (random() + 1)::int = 1
from generate_series(1,10000) as t(i);

(я создаю таблицы, в которых больше, чем несколько столбцов, поскольку выбор планировщика также определяется размером и шириной таблиц)

Первый тест с использованием индекса с NOT IN:

create index ix_not_in on user_table(user_type) 
where user_type not in ('PowerPartner', 'CSPLitePortal', 'CustomerSuccess', 'PowerCustomerSuccess', 'CsnOnly');
explain (analyze true, verbose true, buffers true) 
select *
from user_table
where user_type = 'Standard'

Результат:

QUERY PLAN                                                                                                                      
--------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on stuff.user_table  (cost=139.68..14631.83 rows=11598 width=139) (actual time=1.035..2.171 rows=10000 loops=1)
  Output: id, some_date, user_type, some_ts, some_number, some_data, some_flag                                                  
  Recheck Cond: (user_table.user_type = 'Standard'::text)                                                                       
  Buffers: shared hit=262                                                                                                       
  ->  Bitmap Index Scan on ix_not_in  (cost=0.00..136.79 rows=11598 width=0) (actual time=1.007..1.007 rows=10000 loops=1)      
        Index Cond: (user_table.user_type = 'Standard'::text)                                                                   
        Buffers: shared hit=40                                                                                                  
Total runtime: 2.506 ms                                                                                                         

(Выше приведено типичное время выполнения после запускапримерно 10 раз, чтобы устранить проблемы с кэшированием)

Как вы можете видеть, планировщик использует сканирование индекса растрового изображения, которое представляет собой сканирование с потерями, которое требует дополнительного шага для фильтрации ложных срабатываний.

При использовании следующего индекса:

create index ix_standard on user_table(id) 
where user_type = 'Standard';

Это приводит к следующему плану:

QUERY PLAN                                                                                                                              
----------------------------------------------------------------------------------------------------------------------------------------
Index Scan using ix_standard on stuff.user_table  (cost=0.29..443.16 rows=10267 width=139) (actual time=0.011..1.498 rows=10000 loops=1)
  Output: id, some_date, user_type, some_ts, some_number, some_data, some_flag                                                          
  Buffers: shared hit=313                                                                                                               
Total runtime: 1.815 ms                                                                                                                 

Вывод:

Ваш индекс используется, ноиндекс только для интересующего вас типа немного более эффективен.

Время выполнения не сильно отличается.Я выполнял каждое объяснение примерно 10 раз, и среднее значение для индекса ix_standard было немного ниже 2 мс, а среднее значение для индекса ix_not_in было немного выше 2 мс, так что разница в производительности не была реальной.

Но в целом индексное сканирование будет масштабироваться лучше при увеличении размеров таблицы, чем это делает растровое сканирование индекса.Это в основном связано с «перепроверкой условия» - особенно если недостаточно work_mem для хранения растрового изображения в памяти (для больших таблиц).

0 голосов
/ 16 октября 2018

Чтобы использовать индекс, в запросе должно использоваться условие WHERE.

PostgreSQL имеет некоторые возможности делать выводы, но он не сможет вывести этоuserType = 'Standard' эквивалентно условию в индексе.

Используйте EXPLAIN, чтобы узнать, можно ли использовать ваш индекс.

...