Я использую Postgres 9.3 через Heroku.
У меня есть таблица «трафик» с 1M + записями, которая имеет много вставок и обновлений каждый день. Мне нужно выполнить операции SUM по всей этой таблице за разные промежутки времени, и эти вызовы могут занять до 40 секунд, и я хотел бы услышать предложения о том, как это улучшить.
У меня есть следующий индекс в этой таблице:
CREATE INDEX idx_traffic_partner_only ON traffic (dt_created) WHERE campaign_id IS NULL AND uuid_self <> uuid_partner;
Вот пример оператора SELECT:
SELECT SUM("clicks") AS clicks, SUM("impressions") AS impressions
FROM "traffic"
WHERE "uuid_self" != "uuid_partner"
AND "campaign_id" is NULL
AND "dt_created" >= 'Sun, 29 Mar 2015 00:00:00 +0000'
AND "dt_created" <= 'Mon, 27 Apr 2015 23:59:59 +0000'
И это ОБЪЯСНИТЬ АНАЛИЗ:
Aggregate (cost=21625.91..21625.92 rows=1 width=16) (actual time=41804.754..41804.754 rows=1 loops=1)
-> Index Scan using idx_traffic_partner_only on traffic (cost=0.09..20085.11 rows=308159 width=16) (actual time=1.409..41617.976 rows=302392 loops=1)
Index Cond: ((dt_created >= '2015-03-29'::date) AND (dt_created <= '2015-04-27'::date))
Total runtime: 41804.893 ms
http://explain.depesz.com/s/gGA
Этот вопрос очень похож на другой в SE, но тот, который использовал индекс по двум временным диапазонам столбцов, и планировщик индекса для этого запроса имел оценки, которые были далеко не верны. Основное предложение заключалось в том, чтобы создать отсортированный многостолбцовый индекс, но для одностолбечных индексов это не имеет большого эффекта. Другие предложения состояли в том, чтобы использовать индексы CLUSTER / pg_repack и GIST, но я еще не пробовал их, так как я хотел бы посмотреть, есть ли лучшее решение, использующее обычные индексы.
Оптимизация запросов по диапазону временных отметок (два столбца)
Для справки я попробовал следующие индексы, которые не использовались БД:
INDEX idx_traffic_2 ON traffic (campaign_id, uuid_self, uuid_partner, dt_created);
INDEX idx_traffic_3 ON traffic (dt_created);
INDEX idx_traffic_4 ON traffic (uuid_self);
INDEX idx_traffic_5 ON traffic (uuid_partner);
РЕДАКТИРОВАТЬ : Run EXPLAIN (АНАЛИЗ, VERBOSE, РАСХОДЫ, БУФЕРЫ), и это были результаты:
Aggregate (cost=20538.62..20538.62 rows=1 width=8) (actual time=526.778..526.778 rows=1 loops=1)
Output: sum(clicks), sum(impressions)
Buffers: shared hit=47783 read=29803 dirtied=4
I/O Timings: read=184.936
-> Index Scan using idx_traffic_partner_only on public.traffic (cost=0.09..20224.74 rows=313881 width=8) (actual time=0.049..431.501 rows=302405 loops=1)
Output: id, uuid_self, uuid_partner, impressions, clicks, dt_created... (other fields redacted)
Index Cond: ((traffic.dt_created >= '2015-03-29'::date) AND (traffic.dt_created <= '2015-04-27'::date))
Buffers: shared hit=47783 read=29803 dirtied=4
I/O Timings: read=184.936
Total runtime: 526.881 ms
http://explain.depesz.com/s/7Gu6
Определение таблицы:
CREATE TABLE traffic (
id serial,
uuid_self uuid not null,
uuid_partner uuid not null,
impressions integer NOT NULL DEFAULT 1,
clicks integer NOT NULL DEFAULT 0,
campaign_id integer,
dt_created DATE DEFAULT CURRENT_DATE NOT NULL,
dt_updated TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
)
id - это первичный ключ, а uuid_self, uuid_partner и campaign_id - все внешние ключи. Поле dt_updated обновляется функцией postgres.
traffic
. Также: почему второй EXPLAIN
показывает падение с 42 секунд до 0,5 секунд? Был ли первый запуск с холодным кешем?
id
? Любые другие ограничения? Я вижу два столбца, которые могут быть NULL. Какой процент значений NULL в каждом? Что вы получаете за это? SELECT count(*) AS ct, count(campaign_id)/ count(*) AS camp_pct, count(dt_updated)/count(*) AS upd_pct FROM traffic;
explain (buffers, analyze, verbose) ...
может пролить больше света.