2013-09-16 3 views
0

Один из наших запросов PostgreSQL начал медленно (~ 15 секунд), поэтому мы посмотрели на перенос в базу данных Graph. Ранние тесты показывают значительно более высокие скорости, поэтому УДИВИТЕЛЬНЫЕ.Оптимизация медленного запроса postgresql для нескольких таблиц

Вот проблема: нам по-прежнему необходимо сохранить резервную копию данных в Postgres для нужд без аналитики. База данных Graph предназначена только для аналитики, и мы предпочли бы, чтобы она оставалась вторичным хранилищем данных. Поскольку во время этой миграции наша бизнес-логика изменилась довольно сильно, две существующие таблицы превратились в 4 - и текущая «резервная копия» выбирает в Postgres, отнимает от 1 до 6 минут для запуска.

Я пробовал несколько способов оптимизировать это, и лучше всего, кажется, превратить это в два запроса. Если кто-то может предложить очевидные ошибки здесь, я хотел бы услышать предложение. Я попытался включить левое/правое/внутреннее соединение с небольшой разницей в планировщике запросов. Порядок соединения влияет на разницу; Я думаю, что я просто не понимаю это правильно.

Я подробно рассмотрю.

Цель: Получить последние 10 вложения, отправленные к данному лицу

Структура базы данных:

CREATE TABLE message ( 
    id SERIAL PRIMARY KEY NOT NULL , 
    body_raw TEXT 
    ); 
CREATE TABLE attachments ( 
    id SERIAL PRIMARY KEY NOT NULL , 
    body_raw TEXT 
    ); 
CREATE TABLE message_2_attachments ( 
    message_id INT NOT NULL REFERENCES message(id) , 
    attachment_id INT NOT NULL REFERENCES attachments(id) 
    ); 

CREATE TABLE mailings ( 
    id SERIAL PRIMARY KEY NOT NULL , 
    event_timestamp TIMESTAMP not null , 
    recipient_id INT NOT NULL , 
    message_id INT NOT NULL REFERENCES message(id) 
    ); 

Sidenote: причина, почему тариф абстрагируется от сообщения в том, что тариф часто имеет более один получатель/и/одно сообщение может выходить нескольким получателям

Этот запрос занимает около 5 минут на относительно небольшом наборе данных (время планировщика запросов представляет собой комментарий выше каждого элемента):

-- 159374.75 
EXPLAIN ANALYZE SELECT attachments.* 
FROM attachments 
JOIN message_2_attachments ON attachments.id = message_2_attachments.attachment_id 
JOIN message ON message_2_attachments.message_id = message.id 
JOIN mailings ON mailings.message_id = message.id 
WHERE mailings.recipient_id = 1 
ORDER BY mailings.event_timestamp desc limit 10 ; 

Расщепление его на 2-запросов только занимает 1/8 времени:

-- 19123.22 
EXPLAIN ANALYZE SELECT message_2_attachments.attachment_id 
FROM mailings 
JOIN message ON mailings.message_id = message.id 
JOIN message_2_attachments ON message.id = message_2_attachments.message_id 
JOIN attachments ON message_2_attachments.attachment_id = attachments.id 
WHERE mailings.recipient_id = 1 
ORDER BY mailings.event_timestamp desc limit 10 ; 

-- 1.089 
EXPLAIN ANALYZE SELECT * FROM attachments WHERE id IN (results of above query) 

Я попытался переписывания запросов несколько раз - разные порядки соединения, различные типы объединений и т. д. Я не могу представить, что это почти так же эффективно в одном запросе, как и в двух случаях.

ОБНОВЛЕНО Github имеет лучшее форматирование, так что полный выход объяснить здесь - https://gist.github.com/jvanasco/bc1dd38ca06e52c9a090

+1

Вы также можете опубликовать вывод EXPLAIN? –

+0

Спасибо. Я добавил его в github gist. –

ответ

2

вставлен в выходе вашего объяснения здесь: http://explain.depesz.com/s/hqPT

Как вы можете видеть,:

Hash Join (cost=96588.85..158413.71 rows=44473 width=3201) (actual time=22590.630..30761.213 rows=44292 loops=1) 
       Hash Cond: (message_2_attachment.attachment_id = attachment.id) 

занимает много времени. Я попытался бы добавить индексы к внешним ключам также с помощью:

CREATE INDEX idx_message_2_attachments_attachment_id ON "message_2_attachments" USING btree (attachment_id); 
CREATE INDEX idx_message_2_attachments_message_id ON "message_2_attachments" USING btree (message_id);` 
CREATE INDEX idx_mailings_message_id ON "mailings" USING btree (message_id); 
+0

Спасибо. Я не думал, что индексы будут иметь большое влияние, потому что сама структура таблицы настолько стандартизирована. Добавление этих индексов не имело большого значения, но это сделало огромную разницу «CREATE INDEX» idx_mailings_message_speedy «ON» рассылки «ИСПОЛЬЗОВАНИЕ btree (event_timestamp);» –

+1

Как выглядит объяснение сейчас? Я смотрю «после». –

+0

Я вытащу его позже, но запросы 2: 30 теперь уже 100 мс. индекс event_timestamp снизил его до 200 мс; ваши 3 индекса упали до 100 мс. без индекса event_timestamp, я думаю, что это было около 20 секунд. –

0

В таблице соединений отсутствует первичный ключ. Кроме того, желательно, чтобы добавить индекс обратной на этом ПК:

CREATE TABLE message_2_attachments (
    message_id INT NOT NULL REFERENCES message(id) , 
    attachment_id INT NOT NULL REFERENCES attachments(id) 
     , PRIMARY KEY (message_id,attachment_id) -- <<== here 
    ); 

CREATE UNIQUE INDEX ON message_2_attachments(attachment_id,message_id); -- <<== here 

Для таблицы почтовых отправлений, ситуация не столь однозначна. Он выглядит как некоторая комбинация {event_timestamp, recipient_id, message_id} может функционировать как ключ-кандидат. Поле id просто функционирует как суррогат.

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