2013-08-29 2 views
1

У меня есть следующий выбор, который на большой базе данных, медленно:Slow выберите - PostgreSQL

SELECT eventid 
FROM track_event 
WHERE inboundid IN (SELECT messageid FROM temp_message); 

Таблица temp_message мала (100 строк) и только один столбец (MessageId VARCHAR), с индекс btree в столбце.

В таблице track_event имеется 19 столбцов и почти 13 миллионов строк. В столбцах, используемых в этом запросе (eventid bigint и inboundid varchar), есть индексы btree.

Я не могу копировать/вставить объяснить план из большой базы данных, но вот план с меньшей базой данных (только 348 строк в track_event) с одной и той же схеме:

explain analyse SELECT eventid FROM track_event WHERE inboundid IN (SELECT messageid FROM temp_message); 
                  QUERY PLAN                
---------------------------------------------------------------------------------------------------------------------------------------- 
Nested Loop Semi Join (cost=0.00..60.78 rows=348 width=8) (actual time=0.033..3.186 rows=348 loops=1) 
-> Seq Scan on track_event (cost=0.00..8.48 rows=348 width=25) (actual time=0.012..0.860 rows=348 loops=1) 
-> Index Scan using temp_message_idx on temp_message (cost=0.00..0.48 rows=7 width=32) (actual time=0.005..0.005 rows=1 loops=348) 
     Index Cond: ((temp_message.messageid)::text = (track_event.inboundid)::text) 
Total runtime: 3.349 ms 
(5 rows) 

На большой базе данных , этот запрос занимает около 450 секунд. Может ли кто-нибудь увидеть очевидные ускорения? Я замечаю, что в плане объяснения есть Seq Scan на track_event - я думаю, что я хотел бы проиграть это, но не могу определить, какой индекс я мог бы использовать вместо этого.

редактирует

Postgres 9.0

Таблица track_event является частью очень большой сложной схеме, которую я не могу внести существенные изменения. Вот информация, в том числе новый индекс, который я только что добавил:

  Table "public.track_event" 
     Column  |   Type   | Modifiers 
--------------------+--------------------------+----------- 
eventid   | bigint     | not null 
messageid   | character varying  | not null 
inboundid   | character varying  | not null 
newid    | character varying  | 
parenteventid  | bigint     | 
pmmuser   | bigint     | 
eventdate   | timestamp with time zone | not null 
routeid   | integer     | 
eventtypeid  | integer     | not null 
adminid   | integer     | 
hostid    | integer     | 
reason    | character varying  | 
expiry    | integer     | 
encryptionendpoint | character varying  | 
encryptionerror | character varying  | 
encryptiontype  | character varying  | 
tlsused   | integer     | 
tlsrequested  | integer     | 
encryptionportal | integer     | 
Indexes: 
    "track_event_pk" PRIMARY KEY, btree (eventid) 
    "foo" btree (inboundid, eventid) 
    "px_event_inboundid" btree (inboundid) 
    "track_event_idx" btree (messageid, eventtypeid) 
Foreign-key constraints: 
    "track_event_parent_fk" FOREIGN KEY (parenteventid) REFERENCES track_event(eventid) 
    "track_event_pmi_route_fk" FOREIGN KEY (routeid) REFERENCES pmi_route(routeid) 
    "track_event_pmim_smtpaddress_fk" FOREIGN KEY (pmmuser) REFERENCES pmim_smtpaddress(smtpaddressid) 
    "track_event_track_adminuser_fk" FOREIGN KEY (adminid) REFERENCES track_adminuser(adminid) 
    "track_event_track_encryptionportal_fk" FOREIGN KEY (encryptionportal) REFERENCES track_encryptionportal(id) 
    "track_event_track_eventtype_fk" FOREIGN KEY (eventtypeid) REFERENCES track_eventtype(eventtypeid) 
    "track_event_track_host_fk" FOREIGN KEY (hostid) REFERENCES track_host(hostid) 
    "track_event_track_message_fk" FOREIGN KEY (inboundid) REFERENCES track_message(messageid) 
Referenced by: 
    TABLE "track_event" CONSTRAINT "track_event_parent_fk" FOREIGN KEY (parenteventid) REFERENCES track_event(eventid) 
    TABLE "track_eventaddress" CONSTRAINT "track_eventaddress_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid) 
    TABLE "track_eventattachment" CONSTRAINT "track_eventattachment_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid) 
    TABLE "track_eventrule" CONSTRAINT "track_eventrule_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid) 
    TABLE "track_eventthreatdescription" CONSTRAINT "track_eventthreatdescription_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid) 
    TABLE "track_eventthreattype" CONSTRAINT "track_eventthreattype_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid) 
    TABLE "track_quarantineevent" CONSTRAINT "track_quarantineevent_track_event_fk" FOREIGN KEY (eventid) REFERENCES track_event(eventid) 
+0

1) Есть ли причина, по которой mesaage_id является типом varchar? 2) возможно, добавьте суррогатный первичный ключ, используйте его как FK и добавьте уникальное ограничение в текстовый столбец? 3) возможно, предпочитает 'EXISTS (...)' IN (...) '? (хотя план уже показывает сканирование индекса) 0), пожалуйста, добавьте определения таблицы в вопрос. 0a) и переадресации (random_page_cost, work_mem,?) – joop

+1

BTW: листинг '((temp_message.messageid) :: text = (track_event.inboundid) :: текст) ' в индексе условие ** очень подозрительно ** Я не могу воспроизвести его здесь (pg9.3beta: я получаю только хэш-коды, даже для ключей varchar). Версия для Postgres? ** TABLE определения **? – joop

+0

Как сказал joop, добавьте свою версию Postgres. Никакой ответ не должен предоставляться без плана и версии. – Kuberchaun

ответ

1

Ваш запрос выполняет полное сканирование таблицы в большой таблице. Очевидная скорость - это добавить индекс на event_track(inboundid, eventid). Postgres должен иметь возможность использовать индекс по вашему запросу, как написано. Вы можете переписать запрос:

SELECT te.eventid 
FROM track_event te join 
    temp_message tm 
    on te.inboundid = tm.messageid; 

который определенно должен использовать индекс. (Вы, возможно, потребуется select distinct te.eventid, если есть дубликаты в temp_message таблице.)

EDIT:

Последняя попытка перезапись инвертировать запрос:

select (select eventid from track_event te WHERE tm.messageid = te.inboundid) as eventid 
from temp_message tm; 

Это должно заставить использование индекса , Если есть не-спичка, вы можете:

select eventid 
from (select (select eventid from track_event te WHERE tm.messageid = te.inboundid) as eventid 
     from temp_message tm 
    ) tm 
where eventid is not null; 
+0

Есть еще сканирование Seq на track_event – NickJ

+2

Может быть, статистика неверна или отсутствует. Или, что число ожидаемых строк меньше 10 процентов (IIRC), или константы настройки СУБД ошибочны. – joop

+0

Планы запросов могут отличаться в зависимости от размера таблиц. Вы должны проверить его на большом столе. – klin

1

Попробуйте эту технику:

SELECT eventid FROM track_event te WHERE inboundid IN (SELECT messageid FROM temp_message where messageid = te.inboundid); 

ИЛИ вы можете использовать следующий код для достижения лучших результатов

Select eventid From track_event te Where (Select count(*) from temp_message where messageid = te.inboundid) > 0 
1

Это зависит по количеству записей в базе данных (конкретные таблицы). Если есть небольшая база данных и характер базы данных является статической и очень редкой записи увеличения затем присоединиться использование лучше тогда В и где Потому что после того, как присоединиться к Тэм ведут себя в виде таблицы и в небольших таблицах присоединиться принимают микро секунд.Потому что, где и В имеют определенное время исполнения Того остается лучше в большой базе данных, если база данных велики, то Том получить быстрый результат, если база данных мало, то в случае использования в Постулатах Запрос занимает больше времени

Для малого База данных

SELECT t1.column_name,t1.column_name,t2.column_name,t2.column_name FROM tbl1 t1 
INNER JOIN tbl2 t2 
ON tbl1.column_name=tbl2.column_name; 

Для больших баз данных

SELECT column_name,column_name FROM tbl1 t1 WHERE tbl1.column_name IN (SELECT column_name FROM tbl2 t2 where t2.column_name = t1.column_name); 
0

Может быть, что NULL не индексируются, поэтому если track_event.inboundid и, возможно, temp_message.messageid может быть пустым, тогда он не может составить план доступа, который не включает проверку.

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

Какова статистика на дорожке/указателях track_event?

0

Проблема может быть результатом (неправильной) настройки.

Я могу воспроизвести поведение, отключив hashjoin и сортировку. При достаточно больших запросах подобное поведение, вероятно, можно было бы вызвать на больших входных наборах данных, как только будет достигнуто work_mem.

Приведенные ниже настройки воспроизводят план запроса операционного здесь (PG9.3beta)

-- SET work_mem = 64 ; 
-- SET enable_material = 0; -- only needed for pg9.3 ? 
SET enable_hashjoin = 0 ; 
SET enable_sort = 0 ; 

effective_cache_size и random_page_cost кажется, не имеют никакого влияния (пока).

BTW: вопрос говорит: (only 348 rows in track_event), что подразумевает, что seqscan будет превосходить все остальное. План ожидает 348 строк: нет избирательности, которую можно получить с помощью индекса. (основная таблица необходима для получения значения event_id из основной таблицы)

Итак, я предполагаю, что проблему (в производственной БД) можно было бы решить, установив для рабочих значений значение work_mem и effective_cache_size. Это, конечно, будет работать только в том случае, если индекс является выборочным; для запроса, который извлекает 100% строк, индекс вряд ли будет полезен.

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