У меня есть следующие таблицы:Как увеличить скорость моего оператора выбора Postgres?
CREATE TABLE views (
view_id bigint NOT NULL,
usr_id bigint,
ip inet,
referer_id bigint,
country_id integer,
validated smallint,
completed smallint,
value numeric
);
ALTER TABLE ONLY views
ADD CONSTRAINT "Views_pkey" PRIMARY KEY (view_id);
CREATE TABLE country (
country_id integer NOT NULL,
country character varying(2)
);
ALTER TABLE ONLY country
ADD CONSTRAINT country_pkey PRIMARY KEY (country_id);
CREATE TABLE file_id_view_id (
file_id bigint,
view_id bigint,
created_ts timestamp without time zone
);
CREATE TABLE file_owner (
file_id bigint NOT NULL,
owner_id bigint
);
ALTER TABLE ONLY file_owner
ADD CONSTRAINT owner_table_pkey PRIMARY KEY (file_id);
CREATE TABLE referer (
referer_id bigint NOT NULL,
referer character varying(255)
);
ALTER TABLE ONLY referer
ADD CONSTRAINT referer_pkey PRIMARY KEY (referer_id);
views
и file_id_view_id
таблицы имеют примерно 340M строк каждый. Каждый час они оба увеличиваются на 600K строк.
В таблице file_owner
имеет 75K строк и ежечасно увеличится на строк.
Таблица country
имеет ряды и редко меняются.
Таблица referer
имеет ряды и редко меняются.
Моя цель состоит в том, чтобы быть в состоянии выполнить запрос, такие как:
SELECT Count(ft.*) AS total_views,
(Count(ft.*) - SUM(ft.valid)) AS invalid_views,
SUM(ft.valid) AS valid_views,
SUM(ft.values) AS VALUES,
ft.day AS day,
(CASE
WHEN r.referer IS NULL THEN 'Unknown'
ELSE r.referer
END) AS referer,
(CASE
WHEN c.country IS NULL THEN 'Unknown'
ELSE c.country
END) AS country
FROM country c
right join (referer r
right join (SELECT v.validated AS valid,
v.value AS VALUES,
vf.day AS day,
vf.view_id AS view_id,
v.referer_id AS referer_id,
v.country_id AS country_id
FROM VIEWS v,
(SELECT view_id,
fivi.created_ts :: timestamp :: DATE AS
day
FROM file_id_view_id fivi
join (SELECT file_id
FROM file_owner
WHERE owner_id = 75
GROUP BY file_id) fo
ON (fo.file_id = fivi.file_id)
WHERE (fivi.created_ts BETWEEN
'2015-11-01' AND '2015-12-01')
GROUP BY view_id,
day) vf
WHERE v.view_id = vf.view_id) ft
ON (ft.referer_id = r.referer_id))
ON (ft.country_id = c.country_id)
GROUP BY day,
referer,
country;
Для производства:
GroupAggregate (cost=38893491.99..40443007.61 rows=182295955 width=52) (actual time=183725.696..205882.889 rows=172 loops=1)
Group Key: ((fivi.created_ts)::date), r.referer, c.country
-> Sort (cost=38893491.99..38984639.97 rows=182295955 width=52) (actual time=183725.655..200899.098 rows=8390217 loops=1)
Sort Key: ((fivi.created_ts)::date), r.referer, c.country
Sort Method: external merge Disk: 420192kB
-> Hash Left Join (cost=16340128.88..24989809.75 rows=182295955 width=52) (actual time=23399.900..104337.332 rows=8390217 loops=1)
Hash Cond: (v.country_id = c.country_id)
-> Hash Left Join (cost=16340125.36..24800637.72 rows=182295955 width=49) (actual time=23399.782..102534.655 rows=8390217 loops=1)
Hash Cond: (v.referer_id = r.referer_id)
-> Merge Join (cost=16340033.52..24051874.62 rows=182295955 width=29) (actual time=23397.410..99955.000 rows=8390217 loops=1)
Merge Cond: (fivi.view_id = v.view_id)
-> Group (cost=16340033.41..16716038.36 rows=182295955 width=16) (actual time=23397.298..30454.444 rows=8390217 loops=1)
Group Key: fivi.view_id, ((fivi.created_ts)::date)
-> Sort (cost=16340033.41..16434985.73 rows=189904653 width=16) (actual time=23397.294..28165.729 rows=8390217 loops=1)
Sort Key: fivi.view_id, ((fivi.created_ts)::date)
Sort Method: external merge Disk: 180392kB
-> Nested Loop (cost=6530.43..8799350.01 rows=189904653 width=16) (actual time=63.123..15131.956 rows=8390217 loops=1)
-> HashAggregate (cost=6530.31..6659.62 rows=43104 width=8) (actual time=62.983..90.331 rows=43887 loops=1)
Group Key: file_owner.file_id
-> Bitmap Heap Scan on file_owner (cost=342.90..6508.76 rows=43104 width=8) (actual time=5.407..50.779 rows=43887 loops=1)
Recheck Cond: (owner_id = 75)
Heap Blocks: exact=5904
-> Bitmap Index Scan on owner_id_index (cost=0.00..340.74 rows=43104 width=0) (actual time=4.327..4.327 rows=45576 loops=1)
Index Cond: (owner_id = 75)
-> Index Scan using file_id_view_id_indexing on file_id_view_id fivi (cost=0.11..188.56 rows=4406 width=24) (actual time=0.122..0.306 rows=191 loops=43887)
Index Cond: (file_id = file_owner.file_id)
Filter: ((created_ts >= '2015-11-01 00:00:00'::timestamp without time zone) AND (created_ts <= '2015-12-01 00:00:00'::timestamp without time zone))
Rows Removed by Filter: 184
-> Index Scan using "Views_pkey" on views v (cost=0.11..5981433.17 rows=338958763 width=25) (actual time=0.088..46804.757 rows=213018702 loops=1)
-> Hash (cost=68.77..68.77 rows=6591 width=28) (actual time=2.344..2.344 rows=6495 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 410kB
-> Seq Scan on referer r (cost=0.00..68.77 rows=6591 width=28) (actual time=0.006..1.156 rows=6495 loops=1)
-> Hash (cost=2.70..2.70 rows=233 width=7) (actual time=0.078..0.078 rows=233 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 10kB
-> Seq Scan on country c (cost=0.00..2.70 rows=233 width=7) (actual time=0.005..0.042 rows=233 loops=1)
Planning time: 1.015 ms
Execution time: 206034.660 ms
(37 rows)
:
total_views | invalid_views | valid_views | values | day | referer | country
------------+---------------+-------------+--------+------------+-----------------+---------
При выполнении такого запроса с EXPLAIN ANALYZE
производится следующая
План на explain.depesz.com: http://explain.depesz.com/s/OiN
206s Время работы.
Некоторые вещи, чтобы отметить,
Postgresql Версия 9,4
Я настроил конфиг следующим образом:
- shared_buffers = 30GB
- work_mem = 32MB
- random_page_cost = 2.0
- cpu_tuple_cost = 0,0030
- cpu_index_tuple_cost = 0,0010
- cpu_operator_cost = 0.0005
- effective_cache_size = 52GB
В настоящее время существуют следующие показатели:
- CREATE INDEX country_index ON стране ИСПОЛЬЗОВАНИЕ ВТКЕЕ (страна);
- CREATE INDEX created_ts_index ON file_id_view_id ИСПОЛЬЗОВАНИЕ btree (created_ts);
- CREATE INDEX file_id_created_ts_index ON file_id_view_id ИСПОЛЬЗОВАНИЕ btree (created_ts, file_id);
- CREATE INDEX file_id_view_id_indexing ON file_id_view_id ИСПОЛЬЗОВАНИЕ btree (file_id);
- CREATE INDEX owner_id_file_id_index ON file_owner ИСПОЛЬЗОВАНИЕ btree (file_id, owner_id);
- CREATE INDEX owner_id_index ON file_owner ИСПОЛЬЗОВАНИЕ btree (owner_id);
- CREATE INDEX referer_index ON referer ИСПОЛЬЗОВАНИЕ btree (referer);
предыдущий запрос был использованием владельца идентификатор который был определена консервативно, некоторые запросы могут привести к 1/3 таблицы file_id_view_id соединены с видом.
Изменение структуры данных Последнее Курорт. На данном этапе такое изменение должно быть вызвано серьезными проблемами.
db может считаться прочитанным только в случае необходимости, записываемые данные выполняются почасово, и после каждой записи предоставляется Postgres. В настоящий момент в течение 600K почасовая запись db возвращается в 1100-х годах (это связано с другими причинами наряду со стоимостью вставки). Существует множество возможностей для добавления дополнительных индексов, если это увеличит скорость чтения, причем скорость чтения является приоритетом.
Спецификации аппаратного обеспечения являются:
CPU: http://ark.intel.com/products/83356/Intel-Xeon-Processor-E5-2630-v3-20M-Cache-2_40-GHz
Оперативная память: 128GB
ХРАНЕНИЕ: 1.5TB PCIE SSD
Как я могу оптимизировать либо свою базу данных или запрос так что я могу получить информацию, которая мне нужна из db, в разумные сроки?
Что можно сделать для оптимизации моего текущего дизайна?
Я считаю, что Postgres и аппаратное обеспечение, на котором он работает, имеют возможность выполнять намного лучше, чем сейчас.
UPDATE
Я попытался:
- Анализ таблицы, не влияет на производительность.
- Увеличьте work_mem, это привело к увеличению скорости до 116 с.
- В зависимости от планировщика запросов Postgres, избегая подвыборов, это отрицательно влияет на производительность.
- Отдельный поиск db перед рукой, это, по-видимому, не оказывает положительного/отрицательного эффекта.
Есть ли у кого-нибудь опыт перестройки столов? Возможно ли это? Будут ли дни, часы (оценка, конечно)?
Я рассматриваю возможность де-нормализации базы данных, так как в этом методе это будет только ссылка. Мое единственное беспокойство в том, что: если бы 100M строк вызывались из таблицы с индексированным owner_id, это было бы достаточно быстро или мне бы пришлось столкнуться с теми же проблемами производительности? Не хотелось бы идти в одну сторону, а потом отступать.
Другое решение, которое я рассматриваю, - это предложение @ ivan.panasuik, группировать данные за весь день в другую таблицу, поскольку как только день прошел, эта информация является постоянной и ее не нужно менять или обновлять. Однако я не уверен, как реализовать это гладко - должны ли я запускать запросы через данные, в то время как вставки находятся на удержании и как можно быстрее улавливают дни? С этого момента есть триггерный набор?
Оценки на самом деле не такие точные.Вы анализировали вовлеченные таблицы? У вас также есть два довольно больших типа, которые выполняются на диске. Вы можете попробовать увеличить work_mem резко _ для этого запроса_, например. 'set work_mem = '512MB'' или даже' set work_mem =' 1GB'' –
У меня было впечатление, что Postgres автоматически проанализирует таблицы, должен ли я также вручную это делать? Когда вы говорите _that query_, вы имеете в виду, что существует определенный способ установить work_mem для одного запроса? –
Это _should_ автоматически делает это, но иногда (например, после начальной загрузки) оно не срабатывает достаточно быстро. Операции, которые я показывал при запуске _before_, ваш запрос изменит «work_mem» для текущего сеанса: http://www.postgresql.org/docs/current/static/sql-set.html –