powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Подсчет строк по фильтру в большой таблице
22 сообщений из 22, страница 1 из 1
Подсчет строк по фильтру в большой таблице
    #39340274
Denys Den
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый вечер,
Имеется некий сервис который хранит статистику о сайтах и их пользователях. Общее число записей в таблице пользователей - 100 миллионов, у самого крупного сайта - около 10 миллионов пользователей. Выгялдит таблица примерно так:

CREATE TABLE client
(
id bigint NOT NULL,
...
country character varying(255),
ip character varying(255),
lang character varying(255),
os integer,
registrationdate timestamp(6) without time zone,
valid boolean,
site_id bigint,
...
CONSTRAINT client_pkey PRIMARY KEY (id),
CONSTRAINT site_fk FOREIGN KEY (site_id) REFERENCES site (id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION,
...
)

CREATE INDEX date_index ON client USING btree (site_id, valid, registrationdate);
CREATE INDEX site_index ON client USING btree (site_id, valid);

Нужно считать колличество валидных уникальных клиентов сайта у которых дата регистрации больше определенной даты (выборка от 3-5 миллионов записей). На данный момент делаем так:

SELECT count(distinct (id)) FROM client WHERE site_id IN (1540369841275273216) AND valid=true AND registrationDate>'20-Jul-12';
*сайтов может быть несколько по этому используется IN;

но даже с учетом индексов запрос выполняется 3-8 минут, что неприемлемо. Какой вариант подсчета записей лучше реализовать, что б можно было получить значение максимально быстро? Пока в голову приходит только создание в базе sequence для каждого сайта и тригера который будет инкриментить/декрементить соответствующий sequence при добавлении/удалении из таблицы клиентов. Тогда можно будет сразу получить значение из sequence, но насколько это скажется на производительности высоконагруженного сервера с огромной базой? Также при удалении 1000 клиентов одним запросом тригер выполнится 1000 раз, вместо того что б один раз сделать +1000 для последовательности. Возможно есть варианты получше?
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39340353
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Denys Den,

Вопросы по производительности полезно сопровождать планом исполнения запроса, желательно также с информацией о буферах. Короче: EXPLAIN (analyze, buffers) …
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39340438
Alexius
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Denys Den,

а зачем тут distinct в запросе если id - primary key? уберите - будет быстрее.

в общем случае если старые данные не обновляются - нужно делать агрегацию например каждый день/месяц и брать уже подсчитанные данные и объединять их с неагрегированными данными за последний день/месяц.
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39340886
Denys Den
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vyegorov, конечно, но первый пост получился и так достаточно большой и я написал что суть не в производительности запроса а в реализации механизма подсчета, хотя если можно заставить этот запрос выполняться быстрее 1 секунды, то вот:

Результат EXPLAIN:

Aggregate (cost=2458240.56..2458240.57 rows=1 width=8)
-> Bitmap Heap Scan on client (cost=248140.23..2434057.23 rows=9673333 width=8)
Recheck Cond: (site_id = '1549722331764490240'::bigint)
Filter: (valid AND (registrationdate > '2012-07-20 00:00:00'::timestamp without time zone))
-> Bitmap Index Scan on site_index (cost=0.00..245721.90 rows=9673333 width=0)
Index Cond: ((site_id = '1549722331764490240'::bigint) AND (valid = true))

EXPLAIN ( analyze on, buffers on ):

Aggregate (cost=2458240.56..2458240.57 rows=1 width=8) (actual time=139690.103..139690.103 rows=1 loops=1)
Buffers: shared hit=7 read=2079135 written=1, temp read=14664 written=14664
-> Bitmap Heap Scan on client (cost=248140.23..2434057.23 rows=9673333 width=8) (actual time=1124.318..129998.275 rows=10000000 loops=1)
Recheck Cond: (site_id = '1549722331764490240'::bigint)
Rows Removed by Index Recheck: 88209812
Filter: (valid AND (registrationdate > '2012-07-20 00:00:00'::timestamp without time zone))
Heap Blocks: exact=40592 lossy=2000225
Buffers: shared read=2079135 written=1
-> Bitmap Index Scan on site_index (cost=0.00..245721.90 rows=9673333 width=0) (actual time=1116.813..1116.813 rows=10000000 loops=1)
Index Cond: ((site_id = '1549722331764490240'::bigint) AND (valid = true))
Buffers: shared read=38318 written=1
Planning time: 110.237 ms
Execution time: 139690.545 ms
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39340899
Denys Den
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Alexius, действительно без distinct секунд на 30 стало быстрее, но все равно 2 с лишним минуты. По поводу агрегации неплохой вариант, количество новых клиентов за день достается за милисекунды, может лучше разово посчитать для всех сайтов текущее количество клиентов, а потом при каждом запросе считать количество с момента последнего подсчета и сумировать с предыдущим? Но проблема в том что старые данные обновляются и если старый клиент станет невалидным или его удалят из базы то у нас при этом подходе общее колличество не уменьшится, колличество будет только суммироваться, но не уменьшаться.
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39340970
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Denys Den,

какое значение `work_mem`? поднимите его для этого запроса, метров до 64

индекс `(site_id, registrationdate)` должен помочь

данные холодные уж очень. какое значение `shared_buffers`, `max_wal_size`, `checkpoint_timeout` и сколько памяти на сервере?
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39341020
Denys Den
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vyegorov, на сервере 32 гигабайта оперативной памяти, до 16-ти из них занято. Индекс `(site_id, registrationdate)` есть, как показывает EXPLAIN он не используется (точнее оптимизатор постгрес его использует в случаях где количество клиентов небольшое, для больших сайтов он использует индекс по site_id и затем на этой выборке применяет Filter по valid и registrationdate, возможно так действительно работает быстрее так как индекс тоже достаточно большой).

shared_buffers = 2048MB
max_wal_size, checkpoint_timeout и work_mem не установлены, используются стандартные значения, на сколько я понял:
wal_buffers = -1 (sets based on shared_buffers)
checkpoint_timeout = 5min
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39341022
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vyegorovDenys Den,

какое значение `work_mem`? поднимите его для этого запроса, метров до 64

индекс `(site_id, registrationdate)` должен помочь

данные холодные уж очень. какое значение `shared_buffers`, `max_wal_size`, `checkpoint_timeout` и сколько памяти на сервере?

либо снесите site_index кху йам
либо пересоберите статистику для date_index
а лучше всего -- сделайте и то и другое..
(там соотношенийе 1:10 -- вы в 10 раз больше читаете, чем надо)

и проверьте, что правильное приведение условия по времнени к таймтсп(6) ешё в строке запроса не (?) переключает индекс на нужный (с использованием условия еше в индекс скане, а не в фильтре).

(вообще бы прибивал нахер пользователей х.з. каких строк вместо единственно правильных литералов timestamp'yyyy-mm-dd hh...' )


ЗЫ
есть странная идея -- сунуть сам id в date_index последним полем --- а то ж это угробище не дотумкает, что там ИОС напрашивается (при свежой визибилити-меп)
зы2 валид должен быть или первым полем, или вообще условием на частичном индексе, как правило
ну и т.п.
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39341025
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
qwwq

ЗЫ
есть странная идея -- сунуть сам id в date_index последним полем --- а то ж это угробище не дотумкает, что там ИОС напрашивается (при свежой визибилити-меп)

или попробуйте ему помочь дотумкать :

Код: sql
1.
2.
SELECT --count(distinct (id)) 
count(1) FROM client WHERE
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39341121
Denys Den
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
qwwq,

>либо снесите site_index кху йам
>либо пересоберите статистику для date_index

Удалил site_index, перегенерил date_index, ничего особо не изменилось.

EXPLAIN:
"Aggregate (cost=2525259.89..2525259.90 rows=1 width=8)"
" -> Bitmap Heap Scan on client (cost=315159.56..2501076.56 rows=9673333 width=8)"
" Recheck Cond: ((site_id = '1549722331764490240'::bigint) AND (registrationdate > '2015-05-02 00:00:00'::timestamp without time zone))"
" Filter: valid"
" -> Bitmap Index Scan on date_index (cost=0.00..312741.23 rows=9673333 width=0)"
" Index Cond: ((site_id = '1549722331764490240'::bigint) AND (valid = true) AND (registrationdate > '2015-05-02 00:00:00'::timestamp without time zone))"

Не пойму зачем он делает Bitmap Heap Scan, наверное в нем вся загвоздка.

>или попробуйте ему помочь дотумкать :
>SELECT --count(distinct (id))
>count(1) FROM client WHERE

Не знаю как это работает и что означает, но запрос выполнился за 2 с.
EXPLAIN показал что используется только индекс скан без Bitmap Heap Scan:

"Aggregate (cost=433657.89..433657.90 rows=1 width=0)"
" -> Index Only Scan using date_index on client (cost=0.57..409474.56 rows=9673333 width=0)"
" Index Cond: ((site_id = '1549722331764490240'::bigint) AND (valid = true) AND (registrationdate > '2015-05-02 00:00:00'::timestamp without time zone))"
" Filter: valid"
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39341132
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Denys Den,

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

а означает -- пж не понимает, что ид нот нулл всегда -- и в плане всегда держит чтение самой записи.
а мы понимаем, что любая запись , удовлетворяющая where должна быть подсчитана. поэтому берем count (1) , а для такого запроса оптимайзер уже может применить ios -- т.е. забить на чтение самих записей (никаких полей не содержащихся в индексе в выводе не остаётся) , если в визибилити меп узел индекса помечен как живой. Т.е. экономия на 9 лямах (9673333) произвольных доступов.

а хип скан, скорее всего делает эти доступы более правильными кучками (читал но забыл). а делать их не надо вообще, если индекс свежий. вот отсюда и 2 сек.
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39341135
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
в мепе скорее всего страницы индекса помечены как полностью живые или грязные. ну да товарисчи счас подробно распишут. надеюсь.
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39341139
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Denys Den,

не совсем понял, почему вы считаете что "не изменилось"

у вас раньше всё уходило в песок
Код: sql
1.
Rows Removed by Index Recheck: 88209812


а читать эти записи теперь не обязательно (если только на страницах в нужными 10 лямами не лежит дополнительно ненужных 90 лямов)
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39341145
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Denys Den,

Не надо `EXPLAIN`, надо `EXPLAIN (analyze, buffers)`.
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39341183
Denys Den
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vyegorov, я его тоже уже приводил:

EXPLAIN ( analyze, buffers):

Aggregate (cost=2458240.56..2458240.57 rows=1 width=8) (actual time=139690.103..139690.103 rows=1 loops=1)
Buffers: shared hit=7 read=2079135 written=1, temp read=14664 written=14664
-> Bitmap Heap Scan on client (cost=248140.23..2434057.23 rows=9673333 width=8) (actual time=1124.318..129998.275 rows=10000000 loops=1)
Recheck Cond: (site_id = '1549722331764490240'::bigint)
Rows Removed by Index Recheck: 88209812
Filter: (valid AND (registrationdate > '2012-07-20 00:00:00'::timestamp without time zone))
Heap Blocks: exact=40592 lossy=2000225
Buffers: shared read=2079135 written=1
-> Bitmap Index Scan on site_index (cost=0.00..245721.90 rows=9673333 width=0) (actual time=1116.813..1116.813 rows=10000000 loops=1)
Index Cond: ((site_id = '1549722331764490240'::bigint) AND (valid = true))
Buffers: shared read=38318 written=1
Planning time: 110.237 ms
Execution time: 139690.545 ms
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39341201
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Denys Den,

Я попросил сделать индекс на `(site_id, registrationdate)`, без `valid`. Индексировать bool-колонки смысла нет, т.к. селективность очень маленькая, я бы убрал поле `valid` из всех индексов. Я предпочитаю добавлять такие колонки в WHERE условия:
Код: sql
1.
CREATE INDEX CONCURRENTLY i_client ON client(site_id, registrationdate) WHERE valid;


Это частичный индекс — если можно обойтись полным, то лучше таковой и использовать.
Также я просил увеличить `work_mem`.
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39341215
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vyegorov... Индексировать bool-колонки смысла нет, т.к. селективность очень маленькая, я бы убрал поле `valid` из всех индексов.
какбе это не совсем верное утверждение.
1. может быть распределение 1:100 и поиск на стороне 1.
хотя тут лучше частичный индекс , как вы и предлагаете

2. для 5 бул-полей индекс (b1,b2,b3,b4,b5) уже вполне себе селективен. а для 10 -- потрясающе селективен.

3. булев префикс даже если само булево условие не селективно, усекает количество доступов вдвое . Т.е. если все запросы будут содержать булево условие -- иметь булево поле в начале индекса не вредно. (да и составить туфтовое OR для условия не содержащего булево поле не так сложно -- будет битмап--OR. даже если оптимайзер сам этого не сделает)

но в любом случае хотелось бы посмотреть планы с аналайзами и буферами после дропа инд-а по сайту. они то уж точно должны быть другими, если не по времени выполнения, то по деталям. ан ТС уперся на своем "ничего ,, не поменялось".
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39341330
Alexius
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vyegorovDenys Den,

Я попросил сделать индекс на `(site_id, registrationdate)`, без `valid`. Индексировать bool-колонки смысла нет, т.к. селективность очень маленькая, я бы убрал поле `valid` из всех индексов.


смысл добавлять valid в индекс есть даже в случае частичного индекса, даже когда он зафиксирован. без этого ios работать не будет (это может в 9.6 починили, не проверял еще).
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39343437
Denys Den
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
qwwq... но в любом случае хотелось бы посмотреть планы с аналайзами и буферами после дропа инд-а по сайту. они то уж точно должны быть другими, если не по времени выполнения, то по деталям. ан ТС уперся на своем "ничего ,, не поменялось".

Вот результат EXPLAIN ( analyze on, buffers on ) после дропа индекса по сайту:

Aggregate (cost=2525210.64..2525210.65 rows=1 width=8) (actual time=141612.989..141612.989 rows=1 loops=1)
Buffers: shared read=2090089, temp read=14664 written=14664
-> Bitmap Heap Scan on ckient (cost=315127.23..2501029.72 rows=9672366 width=8) (actual time=3254.863..132744.122 rows=10000000 loops=1)
Recheck Cond: ((site_id = '1549722331764490240'::bigint) AND (registrationdate > '2012-07-20 00:00:00'::timestamp without time zone))
Rows Removed by Index Recheck: 88554710
Filter: valid
Heap Blocks: exact=32726 lossy=2008091
Buffers: shared read=2090082
-> Bitmap Index Scan on count_index (cost=0.00..312709.14 rows=9672366 width=0) (actual time=3217.411..3217.411 rows=10000000 loops=1)
Index Cond: ((site_id = '1549722331764490240'::bigint) AND (valid = true) AND (registrationdate > '2012-07-20 00:00:00'::timestamp without time zone))
Buffers: shared read=49265
Planning time: 0.250 ms
Execution time: 141613.199 ms
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39343458
Denys Den
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vyegorovТакже я просил увеличить `work_mem`.

Вот результаты после увеличения work_mem до 64 MB:

Aggregate (cost=2525210.64..2525210.65 rows=1 width=8) (actual time=142649.610..142649.611 rows=1 loops=1)
Buffers: shared hit=7 read=2090082, temp read=14664 written=14664
-> Bitmap Heap Scan on follower (cost=315127.23..2501029.72 rows=9672366 width=8) (actual time=4471.210..132165.383 rows=10000000 loops=1)
Recheck Cond: ((site_id = '1549722331764490240'::bigint) AND (registrationdate > '2012-07-20 00:00:00'::timestamp without time zone))
Rows Removed by Index Recheck: 56041728
Filter: valid
Heap Blocks: exact=769795 lossy=1271022
Buffers: shared read=2090082
-> Bitmap Index Scan on count_index (cost=0.00..312709.14 rows=9672366 width=0) (actual time=4201.615..4201.615 rows=10000000 loops=1)
Index Cond: ((site_id = '1549722331764490240'::bigint) AND (valid = true) AND (registrationdate > '2012-07-20 00:00:00'::timestamp without time zone))
Buffers: shared read=49265
Planning time: 233.452 ms
Execution time: 142650.789 ms
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39343564
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Denys Den,

Хм... можно поднять до 160, чтобы всё влезло, но погоды не сделает.

А покажите ваши настройки и инфу по таблице/индексам:
Код: sql
1.
2.
3.
4.
5.
select name,setting,context from pg_settings where source not in ('default','override');
select relname,pg_relation_size(oid),reltuples,null::text def from pg_class where oid='clients'::regclass
union all
select relname,pg_relation_size(r.oid),reltuples,pg_get_indexdef(r.oid) from pg_class r join pg_index i on i.indexrelid=r.oid
where i.indrelid='clients'::regclass;
...
Рейтинг: 0 / 0
Подсчет строк по фильтру в большой таблице
    #39344878
Denys Den
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
vyegorovDenys Den,

Хм... можно поднять до 160, чтобы всё влезло, но погоды не сделает.

А покажите ваши настройки и инфу по таблице/индексам:
Код: sql
1.
2.
3.
4.
5.
select name,setting,context from pg_settings where source not in ('default','override');
select relname,pg_relation_size(oid),reltuples,null::text def from pg_class where oid='clients'::regclass
union all
select relname,pg_relation_size(r.oid),reltuples,pg_get_indexdef(r.oid) from pg_class r join pg_index i on i.indexrelid=r.oid
where i.indrelid='clients'::regclass;



select name,setting,context from pg_settings where source not in ('default','override'):
"max_connections";"100";"postmaster"
"max_stack_depth";"2048";"superuser"
"shared_buffers";"262144";"postmaster"
"work_mem";"65536";"user"

select relname,pg_relation_size(oid),reltuples,null::text def from pg_class where oid='clients'::regclass
union all
select relname,pg_relation_size(r.oid),reltuples,pg_get_indexdef(r.oid) from pg_class r join pg_index i on i.indexrelid=r.oid
where i.indrelid='clients'::regclass;

"client";16718372864;1e+08;""
"client_pkey";2246197248;1e+08;"CREATE UNIQUE INDEX follower_pkey ON client USING btree (id)"
"regid_index";5905137664;1e+08;"CREATE INDEX regid_index ON client USING btree (regid)"
"welcome_index";2246197248;1e+08;"CREATE INDEX welcome_index ON client USING btree (wmdelivered)"
"date_index";7371243520;1e+08;"CREATE INDEX date_index ON client USING btree (site_id, valid, registrationdate, city, country, alias_id, browser)"
"full_index";5641027584;1e+08;"CREATE INDEX full_index ON client USING btree (site_id, valid, city, country, alias_id, browser)"
"date_index";4061192192;1e+08;"CREATE INDEX count_index ON client USING btree (site_id, valid, registrationdate)"
...
Рейтинг: 0 / 0
22 сообщений из 22, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Подсчет строк по фильтру в большой таблице
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]