|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
Имеется БД в которой более 60 млн. записей. Table "public.tr_email" Column | Type | Modifiers -------------+------------------------+---------------------------------------------------------- id | integer | not null default nextval('public.tr_email_id_seq'::text) category_id | integer | not null address | character varying(255) | not null is_active | boolean | not null default true Indexes: "tr_email_pkey" primary key, btree (id) "tr_email_address_key" unique, btree (address) "tr_emailndx1" btree (category_id, is_active) Foreign-key constraints: "$1" FOREIGN KEY (category_id) REFERENCES tr_email_category(id) ON DELETE CASCADE Почему простой select (приведён ниже) не использует индекс? EXPLAIN SELECT * FROM tr_email WHERE category_id = 1; QUERY PLAN ---------------------------------------------------------------------- Seq Scan on tr_email (cost=0.00..1327459.55 rows=12259563 width=33) Filter: (category_id = 1) (2 rows) VACUUM ANALYZE делал. Пробовал принудительно использовать индекс (SET enable_seqscan TO OFF) - время работы получается больше. В чём дело? ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 06:18 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
I. Попробуй сделать 1. ALTER TABLE tr_email ALTER category_id SET STATISTICS 500; 2. ANALYZE tr_email где "500" число порядка select count(distinct category_id) from tr_email II. Если SELECT count(*) FROM tr_email WHERE category_id = 1 ; составляет значительную часть от общего числа записей из tr_email, то seq_scan может быть действительно быстрее indexscan. Ш. Если ты уверен, что оптимизатор ведет себя неверно и тебе точно нужно использовать в этом запросе индекс, то сделай перед этим запросом Код: plaintext
P.S. PG какой версии ? ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 07:59 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
select count(distinct category_id) from tr_email Число категории невелико (максимум 25) Если SELECT count(*) FROM tr_email WHERE category_id = 1; составляет значительную часть от общего числа записей из tr_email, то seq_scan может быть действительно быстрее indexscan. Всего: 64 млн. записей, и 10 категориий. Распределение такое: 1) 12,2 млн 2) 1,3 млн 3) 23 млн 4) 2,3 млн 5) 13,6 млн 6) 43 тыс 7) 590 тыс 8) 10,2 млн 9) 900 тыс Когда я отключал seqscan, то оптимизатор показывал большее время выполнения запроса. PostgreSQL 7.4 ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 08:21 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
И ещё: Если категория содержит менее 10 млн. записей, то используется индекс. Если больше - то нет. Так и должно быть? Время выполнения около 30 минут!!! ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 08:34 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
Да, что-то медленно. Что говорят команды iostat, top во время выполненеия запроса? Сколько Мб физически таблица занимает? Винты медленные, какой сервер? Могу посоветовать поставить RAID. Создай N таблиц, где N - число категорий. tr_email_1, tr_email_2, tr_email_3 и т.д. И запросы соотв-м образом формировать. Это убогий вариант того, что в Oracle называется partioning. Индекс по полю categoty_id здесь вообще не нужен :) И еще, пересоздай таблицу с опцией WITHOUT OIDS. ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 08:54 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
Характеристики машины: Dual Xeon 2.4Ghz, 2GB Ram, mirrored 80GB HDDs Под Shared Memory я выделил 256 MB. Top показывает, что при выполнении selecta от процессора используется 10-30%, от памяти 13% (т.е. вся выделенная под Shared Memory) БД занимает 9GB (все таблицы кроме данной пустые) Идея с созданием таблиц для каждой категории не подходит, т.к. придётся очень много кода переписывать. ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 10:01 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
таблица охрененно напоминает базу адресов для спама. давить ублюдков. ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 10:13 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
если бы спам............. статистика ГТС ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 10:20 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
авторИдея с созданием таблиц для каждой категории не подходит, т.к. придётся очень много кода переписывать. а жаль, это бы здорово помогло... Попробуй сделать CLUSTER tr_emailndx1 ON tr_email . Над category_id у тебя один индекс по 2м полям. "tr_emailndx1" btree (category_id, is_active) Посмотри не станет ли лучше если будет отдельный индекс только по полю category. авторХарактеристики машины: Подключи еще один винт и перемести туда файл-индекс, а на старом месте сделай symlink. И подключи еще один винт, разместив там WAL, ...эээ, здесь хотел сказать временное табличное пространство, но в PG этого нет:), но думаю в PG как-то тоже можно заставить создавать временные файлы (кот-ые исп-ся например при больших сортировках) в указанном месте. Монтируй файловые системы с опцией noatime, в режиме soft updates. И еще, если у тебя запросы в основном идут к category_id=1, то можешь сделать условный индекс только для этой категории: CREATE INDEX .... WHERE category_id=1 и вообще говоря, можешь сделать несколько условных индексов для каждой категории CREATE INDEX tr_email_categ_1 . . . . . WHERE category_id=1 CREATE INDEX tr_email_categ_2 . . . . . WHERE category_id=2 надо смотреть, тоже должно помочь для выборок, но за счет замедления INSERT/UPDATE... ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 10:32 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
> напоминает базу адресов для спама. ну, думаю, если все заработает, aev мне сделает одну рассылку на Штаты? ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 10:34 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
Приведите пожалуйста результаты выполнения следующих команд: SET enable_seqscan TO on; EXPLAIN ANALYZE SELECT * FROM tr_email WHERE category_id = 1; SET enable_seqscan TO off; EXPLAIN ANALYZE SELECT * FROM tr_email WHERE category_id = 1; ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 10:42 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
Еще вопрос: что вы далее делаете с результатом запроса, состоящим из 12 миллионов строк? Если еще как-то фильтруете, то может быть сделать эту фильтрацию в самом запросе? Если вычисляете какие-то групповые функции, то может быть их вычислить в запросе? ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 10:49 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
SET enable_seqscan TO on; EXPLAIN ANALYZE SELECT * FROM tr_email WHERE category_id = 1; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------- Seq Scan on tr_email (cost=0.00..1327459.55 rows=12259563 width=33) (actual time=0.113..2717358.453 rows=12189810 loops=1) Filter: (category_id = 1) Total runtime: 2731638.130 ms SET enable_seqscan TO off; EXPLAIN ANALYZE SELECT * FROM tr_email WHERE category_id = 1; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------------- Index Scan using tr_emailndx1 on tr_email (cost=0.00..34687630.64 rows=12259563 width=33) (actual time=38.962..245449.002 rows=12189810 loops=1) Index Cond: (category_id = 1) Total runtime: 281099.570 ms Получается так что оптимизатор неправильно выбирает план запроса. Как сделать чтобы он использовал индекс (без принудительного enable_seqscan to off)? Еще вопрос: что вы далее делаете с результатом запроса, состоящим из 12 миллионов строк? Если еще как-то фильтруете, то может быть сделать эту фильтрацию в самом запросе? Если вычисляете какие-то групповые функции, то может быть их вычислить в запросе? Здесь должны производиться кое-какие подсчёты и сбор статистики, но это не важно - select подправится. Сейчас вопрос стотит так, почему запрпос по индексированному полю занимает так много времени?! ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 11:54 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
Получается так что оптимизатор неправильно выбирает план запроса. Почему же неправильно. Для этого запроса он поступил правильно, хотя бы потому что 2731638.130 ms < 281099.570 ms. Как сделать чтобы он использовал индекс (без принудительного enable_seqscan to off)? Для чего? Мне кажется, что этого делать не нужно, так как в этом запросе seq_scan имеет примерно одинаковое с index_scan-ом время. почему запрпос по индексированному полю занимает так много времени?! Потому что условию в WHERE удовлетворяют очень много строк. ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 12:05 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
Почему же неправильно. Для этого запроса он поступил правильно, хотя бы потому что 2731638.130 ms < 281099.570 ms. 2 731 638.130 ms > 281 099.570 ms Наверное порядком промазал.............. ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 12:07 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
а explain analyze на этотже запрос (с/без index_scana) что говори? ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 12:45 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
Я же привёл пример того что говорит explain analyze (см. чуть выше) ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 12:51 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
порядком промазал Точно. Прошу прощения за невнимательность. И текст моего предыдущего поста не принимать во внимание, так как он не имеет силы из-за этой моей ошибки. Нам тоже приходится бороться с постгресом на поле выбора execution планов. Однако того, чтобы оценка постгреса отличалась от реальности в 300 раз, наблюдать не доводилось. (1327459.55/2717358.453) / (34687630.64/245449.002) = 0.0034566945089819 ~= 1/300. Попробуйте изменить значения Planner Cost Constants (пункт 16.4.4.2. в документации), в особенности, если мне не изменяет память, играет роль random_page_cost. В гугле и мэйл-листах postgresql.org есть обсуждения random_page_cost. Попробуйте CLUSTER-изовать таблицу по индексу tr_emailndx1. Однако, эту операцию придется выполнять регулярно a-la vacuum analyze. :-( Если успех не достигнут, посмотрите на статистику собранную vacuumanalyze-ом об этой таблице и в сорцы планировщика постгреса. :-( Возможно, корень вашей проблемы в том же, о чем я написал в рассылку после двухдневного копания в сорцах: корреляция внутри дисковой страницы вместо глобальной? ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 13:17 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
попробуй вот так WHERE category_id = '1' или так WHERE category_id = 1::integer я тоже наступил на эти грабли похоже, что Postgre преобразование типов выполняет не в ту сторону (category_id преобразуется в тип константы 1, а не наоборот, как хотелось бы) по крайней мере после такого синтаксиса у меня все начало летать (в таблице с несколькими млн записей вместо 30 секунд запрос выполняется доли секунды) ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 13:58 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
Разогреваю тему: имеем # \d acct Table "public.acct" Column | Type | Modifiers ---------------------+--------------------------------+---------------------------------------------------------- acctid | bigint | not null default nextval('public.acct_acctid_seq'::text) ... Indexes: acct_pkey primary key btree (acctid), ... в чем разница между запросами? # explain select * from acct where acctid = 35000000; QUERY PLAN ----------------------------------------------------------- Seq Scan on acct (cost=0.00..155269.20 rows=1 width=442) Filter: (acctid = 35000000) # explain select * from acct where acctid = '35000000'; QUERY PLAN ------------------------------------------------------------------------ Index Scan using acct_pkey on acct (cost=0.00..3.49 rows=1 width=442) Index Cond: (acctid = 35000000::bigint) # explain select * from acct where acctid = 3500000::bigint; QUERY PLAN ------------------------------------------------------------------------ Index Scan using acct_pkey on acct (cost=0.00..3.49 rows=1 width=442) Index Cond: (acctid = 3500000::bigint) ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 17:33 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
YuSha пальцем в ж... товарищ же в первом сообщении привёл схему, у него поле integer, а не bigint. ... |
|||
:
Нравится:
Не нравится:
|
|||
03.06.2004, 19:18 |
|
Индексы на больших объёмах
|
|||
---|---|---|---|
#18+
честно говоря очень странно.... по индексу просматривается 43.4 тысяч записей в секунду (12 миллионов за 281 сек), без индекса 23.4 тысяч записей в секунду (64 миллиона записей за 2 731 сек) - при просмотре по индексу просматриваются только нужные записи, иначе вся таблица. Теория же учит - при последовательном чтении перемещений головок гораздо меньше, чем при переборе по индексу - поэтому должно быть наоборот.... соответственно планировщик ведёт себя неадекватно такой реальности, его можно немного "подправить" уменьшив random_page_cost (кстати а какая у вас дисковая подсистема?), но в любом случае надо искать объяснение такому странному поведению... вариант 0: не учтено влияние дискового кэша операционной системы (первый проход загружает данные в кэш, второй же быстро их оттуда достает). решение - запустить каждый запрос (без seq_scan/с seq_scan) по 3 раза подряд, первый результат проигнорировать, между вторым и третьим взять среднее. маловероятно, т.к. объем базы хоть и не на порядок, но все-таки существенно превосходит размер ram вариант 1: postgesql очень неэффективно обрабатывает условие "category_id = 1". можно попробовать в частности 1::integer (хотя очень сильно сомневаюсь) больше пока вариантов причин такого аномального поведения не видется в любом случае мне кажется стоит провести дополнительные эксперименты и попробовать сообщить в pgsql-performance - думаю помогут (а если проблемы в самом pgsql - может и подправят к 7.5) ... |
|||
:
Нравится:
Не нравится:
|
|||
04.06.2004, 01:40 |
|
|
start [/forum/topic.php?fid=53&fpage=356&tid=2007834]: |
0ms |
get settings: |
12ms |
get forum list: |
13ms |
check forum access: |
3ms |
check topic access: |
3ms |
track hit: |
36ms |
get topic data: |
11ms |
get forum data: |
2ms |
get page messages: |
59ms |
get tp. blocked users: |
2ms |
others: | 314ms |
total: | 455ms |
0 / 0 |