Форум программистов, компьютерный форум, киберфорум
PostgreSQL
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.86/35: Рейтинг темы: голосов - 35, средняя оценка - 4.86
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
1

Как можно увеличить скорость подсчета строк в таблице

19.06.2013, 00:52. Показов 6354. Ответов 15
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Доброй ночи.
Есть таблица на 9500000 строк как можно увеличить скорость подсчета строк в таблице если по одному параметру может выбрать 8999999 строк.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
19.06.2013, 00:52
Ответы с готовыми решениями:

Как увеличить скорость вычислений 500 000 строк?
Здравствуйте. Вопрос к людям с опытом. Как ускорить вычисления 500 000 строк. Задача посчитать,...

Как увеличить скорость добавления строк на листе Excel
Всем доброго времени суток ! Программно добавить строки на листе Excel можно таким кодом: ...

Можно ли увеличить скорость ответа БД
Привет всем у меня возник такой вопрос как можно увеличить скорость ответа бд, иногда когда долго...

Можно ли увеличить скорость парсинга в HtmlAgilityPack?
Привет всем, я написал код для парсинга одного сайт, код рабочий, но срабатывает довольно долго(от...

15
1251 / 967 / 382
Регистрация: 02.09.2012
Сообщений: 2,989
19.06.2013, 16:57 2
построить план запроса (EXPLAIN)... от него уже плясать
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
19.06.2013, 21:33  [ТС] 3
Цитата Сообщение от grgdvo Посмотреть сообщение
построить план запроса (EXPLAIN)... от него уже плясать
не совсем понятно что он показывает, есть где то расшифровка?

Добавлено через 39 минут
Вот план, можно перевести

"Hash Join (cost=2.27..676918.63 rows=9020883 width=1166) (actual time=0.051..31650.458 rows=8999999 loops=1)"
" Hash Cond: (f.screen_version_id = sv.id)"
" -> Nested Loop (cost=1.11..552880.33 rows=9020883 width=654) (actual time=0.030..28141.769 rows=8999999 loops=1)"
" -> Seq Scan on tbl_release r (cost=0.00..1.25 rows=1 width=8) (actual time=0.007..0.009 rows=1 loops=1)"
" Filter: (id = 15)"
" Rows Removed by Filter: 19"
" -> Hash Join (cost=1.11..462670.25 rows=9020883 width=654) (actual time=0.018..26458.778 rows=8999999 loops=1)"
" Hash Cond: (f.type_film_id = tf.id)"
" -> Seq Scan on tbl_films f (cost=0.00..338632.00 rows=9020883 width=142) (actual time=0.005..22186.697 rows=8999999 loops=1)"
" Filter: (release_id = 15)"
" Rows Removed by Filter: 500002"
" -> Hash (cost=1.05..1.05 rows=5 width=520) (actual time=0.006..0.006 rows=5 loops=1)"
" Buckets: 1024 Batches: 1 Memory Usage: 1kB"
" -> Seq Scan on tbl_type_films tf (cost=0.00..1.05 rows=5 width=520) (actual time=0.002..0.003 rows=5 loops=1)"
" -> Hash (cost=1.07..1.07 rows=7 width=520) (actual time=0.014..0.014 rows=7 loops=1)"
" Buckets: 1024 Batches: 1 Memory Usage: 1kB"
" -> Seq Scan on tbl_screen_version sv (cost=0.00..1.07 rows=7 width=520) (actual time=0.007..0.009 rows=7 loops=1)"
"Total runtime: 32230.258 ms"
0
1251 / 967 / 382
Регистрация: 02.09.2012
Сообщений: 2,989
22.06.2013, 16:04 4
Не мог я сразу ответить, если еще актуально!

Отступы потеряли! Это важно для понимания структуры плана.

Простые рекомендации, если лень читать доки.
1. Обращайте внимание на "actual time" и стрелочками помечены операции, которые исполнитель запроса делает для получения результатов вашего запроса.
2. Seq Scan - последовательный поиск по таблице! ПЛОХО! Проверьте индексы по полю, которое ниже строчкой стоит под Filter. Индекс выгоднее использовать.
3. Не понятно, откуда взялся Nested Loop (вложенный цикл, то есть перебор записей). У вас что таблицы в запросе не связаны? Это ПЛОХО!

Остальное уже мало времени занимает, пока пропустим
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
23.06.2013, 14:35  [ТС] 5
Цитата Сообщение от grgdvo Посмотреть сообщение
. Не понятно, откуда взялся Nested Loop (вложенный цикл, то есть перебор записей). У вас что таблицы в запросе не связаны? Это ПЛОХО!
Почему это плохо?
0
1251 / 967 / 382
Регистрация: 02.09.2012
Сообщений: 2,989
23.06.2013, 17:49 6
Nested Loop: Базе приходится перелапачивать n*m итераций для объединения двух таблиц с n и m записями. Nested Loop хорошо для маленьких таблиц - быстрее выполняется, чем Hash Join, так как не нужно строить хэши для объединения, но для больших таблиц - это фактически гарантировано тормоза.
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
24.06.2013, 18:20  [ТС] 7
Вот новый, если можно про комментировать
"Aggregate (cost=342640.87..342640.88 rows=1 width=4) (actual time=407536.830..407536.830 rows=1 loops=1)"
" -> Bitmap Heap Scan on tbl_films f (cost=67247.68..335103.51 rows=3014946 width=4) (actual time=404609.514..407143.771 rows=2999999 loops=1)"

" Recheck Cond: (release_id = 7)"

" Rows Removed by Index Recheck: 119"

" -> Bitmap Index Scan on index_tbl_films_release_id (cost=0.00..66493.95 rows=3014946 width=0) (actual time=404607.541..404607.541 rows=3000000 loops=1)"

" Index Cond: (release_id = 7)"
"Total runtime: 407537.071 ms"
0
1251 / 967 / 382
Регистрация: 02.09.2012
Сообщений: 2,989
26.06.2013, 07:17 8
Bitmap Index имеет ограниченные условия применения (в основном для данных, которые очень редко меняются, ибо существенные накладные расходы на создание и поддержание целостности индекса)
См. например здесь и здесь

Попробуйти заменить на обычный b-tree индекс у соответствующих таблиц.
И покажите сам запрос. Не понимаю почему делается heap scan и index scan. Не могу представить, как это в запросе может выглядеть.
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
26.06.2013, 23:43  [ТС] 9
Oracle 11 SQL
1
2
3
SELECT COUNT(f.id) AS _count  
FROM tbl_films f  
WHERE release_id = 7;
Oracle 11 SQL
1
2
3
SELECT COUNT(f.id) AS _count  
FROM tbl_films f  
WHERE release_id = 7 AND screen_version_id=7;
Индексы такие создавал
Oracle 11 SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE INDEX index_tbl_relations_film_id ON tbl_relations USING btree(film_id);
CREATE INDEX index_tbl_relations_genre_id ON tbl_relations USING btree(genre_id);
CREATE INDEX index_tbl_genres_id ON tbl_genres USING btree(id);
CREATE INDEX index_tbl_films_id ON tbl_films USING btree(id);
CREATE INDEX index_tbl_films_disk ON tbl_films USING btree(disk);
CREATE INDEX index_tbl_films_all_genres ON tbl_films USING btree(all_genres text_pattern_ops);
CREATE INDEX index_tbl_films_film_name ON tbl_films USING btree(film_name text_pattern_ops);
CREATE INDEX index_tbl_films_release_id ON tbl_films USING btree(release_id);
CREATE INDEX index_tbl_films_screen_version_id ON tbl_films USING btree(screen_version_id);
CREATE INDEX index_tbl_films_type_film_id ON tbl_films USING btree(type_film_id);
CREATE INDEX index_tbl_video_id ON tbl_video USING btree(id);
CREATE INDEX index_tbl_video_film_id ON tbl_video USING btree(film_id);
CREATE INDEX index_tbl_gallery_id ON tbl_gallery USING btree(id);
CREATE INDEX index_tbl_gallery_film_id ON tbl_gallery USING btree(film_id);
0
1251 / 967 / 382
Регистрация: 02.09.2012
Сообщений: 2,989
01.07.2013, 23:32 10
Был в отъезде... неудобно отвечать было... опять же, если еще актуально

1. Не знаю какая версия у вас postgres, но можете прочитать этот мануал
2. Сделать VACUUM tbl_film, как там написано + включить indexscanonly, если соотв. версия postgres.
3. Я провел эксперименты над таблицей в 9 млн. записей. Указанными выше способами довел выполнение запроса с 17000 ms (seq scan без индексов), до 0,170 ms (индексы, плюс кеширование уже помогало быстро выбирать результаты на запрос).

SQL
1
SELECT COUNT(*) FROM tbl_film WHERE film_releaseid = 7;
Его план без индексов (здесь и далее уже прошла VACUUM'изация таблицы, поэтому так быстро, вообще изначально я получал результат больше 10 с, на "чистой" базе, когда только сгенерированы были данные в 9000000 строк запрос выполнялся 17 с)
Код
"Aggregate  (cost=235677.39..235677.40 rows=1 width=0) (actual time=3387.151..3387.152 rows=1 loops=1)"
"  ->  Seq Scan on tbl_film  (cost=0.00..233215.00 rows=984956 width=0) (actual time=0.039..2439.000 rows=1001693 loops=1)"
"        Filter: (film_releaseid = 7)"
"        Rows Removed by Filter: 8004307"
"Total runtime: 3387.195 ms"
Создал индекс (b-tree)
Код
"Aggregate  (cost=153852.99..153853.00 rows=1 width=0) (actual time=4076.851..4076.852 rows=1 loops=1)"
"  ->  Bitmap Heap Scan on tbl_film  (cost=18438.65..151390.60 rows=984956 width=0) (actual time=144.397..3146.587 rows=1001693 loops=1)"
"        Recheck Cond: (film_releaseid = 7)"
"        Rows Removed by Index Recheck: 7424552"
"        ->  Bitmap Index Scan on index_film_releaseid  (cost=0.00..18192.41 rows=984956 width=0) (actual time=142.885..142.885 rows=1001693 loops=1)"
"              Index Cond: (film_releaseid = 7)"
"Total runtime: 4077.050 ms"
Как видно будет еще хуже, потому что операция по индексу за два действия идет, вместо последовательного скана за одно.

Включаем, все что написано в 1 и 2
Код
"Aggregate  (cost=31396.39..31396.40 rows=1 width=0) (actual time=2000.319..2000.320 rows=1 loops=1)"
"  ->  Index Only Scan using index_film_releaseid on tbl_film  (cost=0.00..28934.00 rows=984956 width=0) (actual time=0.056..1220.871 rows=1001693 loops=1)"
"        Index Cond: (film_releaseid = 7)"
"        Heap Fetches: 277"
"Total runtime: 2000.362 ms"
Проверяем сам запрос в конце:
Как можно увеличить скорость подсчета строк в таблице


Вообщем мораль, если нет частых (и массивных) изменений данных, то включить сканирование по индексу, вакуумизировать, также не создавать излишних (которые не участвуют в оптимизации запросов) индексов - это тоже отнимает время

Все результаты приведены для домашнего ноута (Core2Duo, 2,5Ghz, postgresql 9.2.4, поставлен в Gentoo из portage без каких-либо извратов)
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
09.07.2013, 00:35  [ТС] 11
Цитата Сообщение от grgdvo Посмотреть сообщение
1. Не знаю какая версия у вас postgres, но можете прочитать этот мануал
2. Сделать VACUUM tbl_film, как там написано + включить indexscanonly, если соотв. версия postgres.
3. Я провел эксперименты над таблицей в 9 млн. записей. Указанными выше способами довел выполнение запроса с 17000 ms (seq scan без индексов), до 0,170 ms (индексы, плюс кеширование уже помогало быстро выбирать результаты на запрос).
1. С английским проблема.
2. vacuum сделал, а где включить indexscanonly (версия 9.2.4.1).
3. Как лучше сделать индексы по каждому поисковому полю или один составной индекс? Как можно настроить кеширование запросов?
0
1251 / 967 / 382
Регистрация: 02.09.2012
Сообщений: 2,989
09.07.2013, 16:50 12
Цитата Сообщение от rave82 Посмотреть сообщение
1. С английским проблема.
Это проблема. Боюсь на русском такой информации еще не найти. Ну автопереводчики вам в помощь, хотя от них бывает мало толку. В разделе index-only scans (я бы перевел как "Поиск только с использование индексов") кратко написано
1. Сделать обычный индекс (без указания алгоритма) для таблицы по тем полям, которые участвуют в where
2. Сделать вакуумизацию
3. Включить "поиск только с использованием индексов"

Цитата Сообщение от rave82 Посмотреть сообщение
2. vacuum сделал, а где включить indexscanonly (версия 9.2.4.1).
Да вообще-то indexonlyscan уже должна быть включена по умолчанию (как пишут в документации). Но попробуйте через pgadmin или через psql дать команду SET enable_indexscanonly to ON. Это должно настроить планировщик выполнения запросов на использование соответствующего индекса.
Цитата Сообщение от rave82 Посмотреть сообщение
3. Как лучше сделать индексы по каждому поисковому полю или один составной индекс? Как можно настроить кеширование запросов?
Лучше создавать такие индексы, которые соответствуют выражению WHERE. То есть, если отбираем записи только по releaseid, то и индекс нужен только по этому полю, если по releaseid и screenid, то и индекс по этим двум полям. Опять же правило, чем больше индексов, тем больше времени тратиться на их перестроение при изменении данных (INSERT, DELETE, UPDATE). Обязательно сверяйте использование индексов с планом. Если видите, что планировщик выполнения запросов построил какой-то неверный план выполнения, значит где-то ошибка.

Кэширование как-таковое уже включено. То есть если дважды выполнить один и тот же запрос, то результаты для второго запроса результаты появятся гораздо быстрее. Для многопользовательского режима это может быть актуально. Если пользователи часто запрашивают одни и те же данные, не нужно по многу раз перевыполнять один и тотже запрос. Резульаты возмуться из кеша, если данные не изменялись конечно. Упралвять кеширование можно через параметры управления сервером. Об этом написана целая глава 18 в документации. На русском даже не знаю, есть или нет. Спрашивайте, если потребуется, попробуем разобраться.
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
09.07.2013, 18:32  [ТС] 13
Цитата Сообщение от grgdvo Посмотреть сообщение
Лучше создавать такие индексы, которые соответствуют выражению WHERE. То есть, если отбираем записи только по releaseid, то и индекс нужен только по этому полю, если по releaseid и screenid, то и индекс по этим двум полям.
то есть если у меня например пять поисковых полей то мне нужно создать на каждое поле индекс, а затем перебирать возможные комбинации фильтра? А если требуется возможность динамического добавления и удаления фильтра?
0
1251 / 967 / 382
Регистрация: 02.09.2012
Сообщений: 2,989
09.07.2013, 23:29 14
С динамическим фильтром может быть проблема: или гибкость или скорость. Обычно одно другое взаимоисключает. Но лучше всего протестировать! Если производительность устроит, значит индексы оставить как есть. Если нет, то тогда придется как-то менять модель данных, чтобы справиться с большим объемом данных и одновременно обеспечивать соотв. уровень гибкости фильтров при выборке.
0
23 / 23 / 11
Регистрация: 05.02.2010
Сообщений: 314
10.07.2013, 14:33  [ТС] 15
Для хранения списка жанров к фильму есть таблица связи в ней есть поле film_id и genre_id. При выборке например по "году выпуска" и жанру "ужасы" можно как то склеить поле из таблицы tbl_films.id с таблицей tbl_relations.film_id чтоб быстро получить количество строк соответствующему условию. Сейчас выполняется за 4,5с., а если отдельно выполнить запрос для получения количества строк только по жанру то запрос выполняется за 0,4с.
0
1251 / 967 / 382
Регистрация: 02.09.2012
Сообщений: 2,989
01.08.2013, 18:33 16
Гм-гм... времени правда прошло три недели, но не мог я раньше ответить.

При оптимизации запросов пока (IMHO) лучше не придумали подхода, чем смотреть, что показывает план запроса - в нем мы видим, как СУБД выполняет запрос (как она "думает" и совпадает ли ее "думанье" с нашим).

Мне сложно точно воспроизвести ситуацию, потому что я не знаю ни вашей модели данных, ни ваших данных, но попробую показать на простом примере. У меня остались предыдущие данные (9 млн. записей), я к ним приделал таблицу genre. Вот так:

SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CREATE TABLE tbl_film
(
  film_id INTEGER NOT NULL,
  film_genre INTEGER NOT NULL,
  film_releaseid INTEGER,
  film_screenid INTEGER NOT NULL,
  film_name CHARACTER VARYING(128) NOT NULL,
  CONSTRAINT constrain_film_pk PRIMARY KEY (film_id),
  CONSTRAINT constrain_genre_fk FOREIGN KEY (film_genre)
      REFERENCES tbl_genre (genre_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);
 
CREATE TABLE tbl_genre
(
  genre_id INTEGER NOT NULL,
  genre_name CHARACTER VARYING(128),
  CONSTRAINT constraint_genre_pk PRIMARY KEY (genre_id)
);
Хочу выполнить запрос:

SQL
1
2
3
SELECT COUNT(*)
FROM tbl_genre tg, tbl_film tf
WHERE tf.film_genre = tg.genre_id AND genre_id=8;
Посчитать количество фильмов ужасов (у меня это id=8 в tbl_genre).

Запускаю анализ плана:

Bash
1
2
3
4
5
6
"Aggregate  (cost=238905.04..238905.05 rows=1 width=0)"
"  ->  Nested Loop  (cost=0.00..237767.28 rows=455103 width=0)"
"        ->  Seq Scan on tbl_genre tg  (cost=0.00..1.25 rows=1 width=4)"
"              Filter: (genre_id = 8)"
"        ->  Seq Scan on tbl_film tf  (cost=0.00..233215.00 rows=455103 width=4)"
"              Filter: (film_genre = 8)"
Получаю проблему. Два последовательных скана, цикл соединения (от него не избавиться - это соединение таблиц), ну и агрегат в виде подсчета количества. Смотрим на cost. Видим у tbl_film оценка времени просто сумасшедшая: 233 тыс. единиц. Вложенный цикл и агрегат добавляют какие-то слезы к общей оценке, поэтому на них мы уже не обращаем внимание. Также не обращаем внимание на таблицы жанров. Ибо там всего у меня 20 записей и дешевле обойтись без индекса вообще. Итак видно, что запрос будет тормозить.
Проверяем, что это так и есть:

Как можно увеличить скорость подсчета строк в таблице


Видим 1,4 с. Много!

Создаем индекс для условия genre_id=8

SQL
1
2
3
4
CREATE INDEX index_film_genre
  ON tbl_film
  USING btree
  (film_genre);
Проверяем, что от создания индекса есть какой-то толк. Анализируем план запроса. Получаем.

Bash
1
2
3
4
5
6
"Aggregate  (cost=19062.59..19062.60 rows=1 width=0)"
"  ->  Nested Loop  (cost=0.00..17924.83 rows=455103 width=0)"
"        ->  Seq Scan on tbl_genre tg  (cost=0.00..1.25 rows=1 width=4)"
"              Filter: (genre_id = 8)"
"        ->  Index Only Scan using index_film_genre on tbl_film tf  (cost=0.00..13372.55 rows=455103 width=4)"
"              Index Cond: (film_genre = 8)"
Видим, что сработал наш индекс! Также помним, что включали до этого Index Scan Only (для postgresql 9.2). Получили оценочное ускорение раз в 10. Проверяем во сколько же ускорились на самом деле.

Как можно увеличить скорость подсчета строк в таблице


Получилось где-то раз в 10 быстрее.

Надеюсь алгоритм действий понятен. Много зависит от организации модели данных и запросов, которые строятся по этой модели. Будут вопросы, присылайте.
0
01.08.2013, 18:33
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
01.08.2013, 18:33
Помогаю со студенческими работами здесь

Максимальная скорость беспроводного соединения: 54 Мбит/с - можно ли увеличить ее?
Здравствуйте.У меня старый wi-fi роутер. У него Макс. скорость беспроводного соединения: 54 Мбит/с....

Можно ли увеличить скорость на мамке Shuttle AB52 USB1.1 до USB2.0?
Добрый вечер!Скажите пожалуйста можно ли как нибудь увеличить скорость на мамке Shuttle AB52 USB1.1...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru