2014-10-06 8 views
1

Вот эта таблица и ее индексы. Эта таблица в производстве содержит ~ 50 миллионов строк.PostgreSQL игнорирует мой составной индекс

CREATE TABLE "public"."audit_page_loads" (
    "id" int4 NOT NULL DEFAULT nextval('audit_page_loads_id_seq'::regclass), 
    "dt" timestamp(6) NULL, 
    "ip" varchar(255) COLLATE "default", 
    "method" varchar(255) COLLATE "default", 
    "action" varchar(255) COLLATE "default", 
    "elapsed" numeric(8,2) DEFAULT 0, 
    "views" numeric(8,2) DEFAULT 0, 
    "db" numeric(8,2) DEFAULT 0 
) 
WITH (OIDS=FALSE); 
ALTER TABLE "public"."audit_page_loads" ADD PRIMARY KEY ("id") NOT DEFERRABLE INITIALLY IMMEDIATE; 

CREATE INDEX "index_audit_page_loads_on_action" ON "public"."audit_page_loads" USING btree("action" COLLATE "default" ASC NULLS LAST); 
CREATE INDEX "index_audit_page_loads_on_action_and_dt" ON "public"."audit_page_loads" USING btree("action" COLLATE "default" ASC NULLS LAST, dt ASC NULLS LAST); 

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

SELECT action, avg(elapsed), count(*) 
FROM audit_page_loads 
WHERE action != 'UsersController#login' and dt >= '2014-09-01' 
GROUP BY action 

Limit (cost=1685321.43..1685321.68 rows=20 width=32) (actual time=15900.954..15900.968 rows=20 loops=1) 
    -> HashAggregate (cost=1685321.43..1685321.80 rows=30 width=32) (actual time=15900.952..15900.965 rows=20 loops=1) 
     -> Seq Scan on audit_page_loads (cost=0.00..1646329.70 rows=5198897 width=32) (actual time=7.075..11826.963 rows=5820401 loops=1) 
       Filter: (((action)::text <> 'UsersController#login'::text) AND (dt >= '2014-09-01 00:00:00'::timestamp without time zone)) 
       Rows Removed by Filter: 52614815 
Total runtime: 15901.013 ms 

При добавлении набор enable_seqscan = ложь;, чтобы выбрать, результаты мгновенные. .13 секунды, и я вижу в объяснении, что используется индекс index_audit_page_loads_on_action_and_dt. Зачем мне это нужно? И если я правильно понял, в том числе, что set enable_seqscan = false; нецелесообразно. Может ли кто-нибудь помочь мне здесь?

PostgreSQL 9.3.5 на x86_64-неизвестно-Linux-гну, составленный GCC (Ubuntu/Линаро 4.6.3-1ubuntu5) 4.6.3, 64-битный

+0

Измените свой вопрос и вставьте результаты 'explain analysis SELECT action ...'. –

ответ

2

Предложение WHERE не sargable. Но может быть эквивалентное выражение, которое равно sargable.

Я построил ваш стол и загрузил около миллиона строк случайных данных.

explain analyze 
select action, avg(elapsed), count(*) 
from audit_page_loads 
where action < 'UsersController#login' and dt >= '2014-09-01' 
    or action > 'UsersController#login' and dt >= '2014-09-01' 
group by action; 
 
"HashAggregate (cost=75.45..75.46 rows=1 width=24) (actual time=0.379..0.379 rows=1 loops=1)" 
" -> Bitmap Heap Scan on audit_page_loads (cost=9.20..75.32 rows=17 width=24) (actual time=0.264..0.276 rows=90 loops=1)" 
"  Recheck Cond: ((((action)::text = '2014-09-01 00:00:00'::timestamp without time zone)) OR (((action)::text > 'UsersController#login'::text) AND (dt >= '2014-09-01 00:00:00'::timestamp without time zone)))" 
"  -> BitmapOr (cost=9.20..9.20 rows=17 width=0) (actual time=0.259..0.259 rows=0 loops=1)" 
"    -> Bitmap Index Scan on audit_page_loads_action_dt_idx (cost=0.00..4.59 rows=8 width=0) (actual time=0.191..0.191 rows=90 loops=1)" 
"     Index Cond: (((action)::text = '2014-09-01 00:00:00'::timestamp without time zone))" 
"    -> Bitmap Index Scan on audit_page_loads_action_dt_idx (cost=0.00..4.59 rows=8 width=0) (actual time=0.067..0.067 rows=0 loops=1)" 
"     Index Cond: (((action)::text > 'UsersController#login'::text) AND (dt >= '2014-09-01 00:00:00'::timestamp without time zone))" 
"Total runtime: 0.431 ms" 

На моей коробке, это уменьшило время работы примерно от 186 мс до 0,4 мс. YMMV; Я уверен, что мои данные не очень похожи на ваши.

В общем случае, можно ли заменить неосновного оператора эквивалентным предикатом с разделителями в предложении WHERE в зависимости от типа данных, порядка сортировки и чувствительности к регистру. Проверьте свои данные.

2

Индексы action не может быть используется здесь, поскольку вы используете сравнение != для столбца action. Невозможно использовать индекс, если вы ищете значения вне его.

Вместо этого, вы можете попробовать следующее:

CREATE INDEX i_1 ON audit_page_loads(dt); 

Если предикат action != 'UsersController#login' используется довольно часто, вы можете попробовать создать частичный индекс:

CREATE INDEX i_1 ON audit_page_loads(dt) WHERE action != 'UsersController#login'; 

Согласно Rows Removed by Filter: 52614815 записи из вашего плана , этот показатель должен быть неплохим.

Как вы используете поиск диапазона на dt столбца в запросе, это не представляется возможным оптимизировать также GROUP BY использовать индексы, так что вы будете иметь HashAgg узел. Но с индексом время должно быть намного лучше.

Смежные вопросы