powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Медленный COUNT, OFFSET. Таблица не помещается в RAM
25 сообщений из 26, страница 1 из 2
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063169
Zim-a
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Добрый день.
У меня есть таблица djangobb форума.
В полях body и body_html хранится текст постов (он может быть очень большой).

Сайт написан на Django с использованием батареек типа haystack (для полнотекстового поиска используется Elasticsearch)
Elasticsearch делит с postgersql 2 Гб оперативной памяти.
В таблице постов где-то 1,5 млн. записей суммарно. Весит она 1,7 Гб (из-за текстовых полей, без текстовых полей где-то 130 МБ) и соответственно в память полностью не помещается.
Django-haystack синхронизирует с Elasticsearch данные по кусочкам, каждый раз делая запрос вида:
Код: plsql
1.
2.
3.
4.
SELECT [список колонок, можно считать что *] 
FROM djangobb_forum_post 
WHERE djangobb_forum_post.created < [now timestamp]
OFFSET 1000000 LIMIT 1000


Такой запрос из-за того, что данные каждый раз читаются с диска выполняется долго (где-то 11-23 секунд), не считая запросов с небольшим OFFSET

Кроме того, тупят темы с большим числом постов (несколько десятков тысяч), но гораздо меньше (одна-две секунды).
Django для пагинации каждый раз делает запрос
Код: plsql
1.
SELECT COUNT * FROM djangobb_forum_post 



Делал VACUUM FULL ANALYZE;
Делал переиндексацию.

Пробовал создать копию таблицы, но без текстовых полей (body и body_html) - и те же выборки оттуда выполняются за 200-300 мс.
Что гораздо лучше.
Пробовал колонкам выставлять Column storage в EXTERNAL
Я с БД мало работал, и плохо понимаю как хранятся данные, но у меня ощущение что даже при запросах COUNT - postgresql пытается считываться все данные (в т. ч. и эти текстовые поля).
Нельзя ли как-то настроить чтобы они как бы были в этой же таблице (но физически лежали в другом месте) и их не трогали при просчёте OFFSET и COUNT?

Спасибо.


CREATE таблицы:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
-- Table: djangobb_forum_post

-- DROP TABLE djangobb_forum_post;

CREATE TABLE djangobb_forum_post
(
  id serial NOT NULL,
  topic_id integer NOT NULL,
  user_id integer NOT NULL,
  created timestamp with time zone NOT NULL,
  updated timestamp with time zone,
  updated_by_id integer,
  markup character varying(15) NOT NULL,
  body text NOT NULL,
  body_html text NOT NULL,
  user_ip inet,
  CONSTRAINT djangobb_forum_post_pkey PRIMARY KEY (id),
  CONSTRAINT djangobb_f_topic_id_6140e6c4bb8ab5af_fk_djangobb_forum_topic_id FOREIGN KEY (topic_id)
      REFERENCES djangobb_forum_topic (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED,
  CONSTRAINT djangobb_forum_post_user_id_3d941fcc88e24fc_fk_common_user_id FOREIGN KEY (user_id)
      REFERENCES common_user (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED,
  CONSTRAINT djangobb_forum_updated_by_id_6725245b78bc092c_fk_common_user_id FOREIGN KEY (updated_by_id)
      REFERENCES common_user (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED
)
WITH (
  OIDS=FALSE
);
ALTER TABLE djangobb_forum_post
  OWNER TO warhold_u;

-- Index: djangobb_forum_post_19b4d727

-- DROP INDEX djangobb_forum_post_19b4d727;

CREATE INDEX djangobb_forum_post_19b4d727
  ON djangobb_forum_post
  USING btree
  (topic_id);

-- Index: djangobb_forum_post_9ccf0ba6

-- DROP INDEX djangobb_forum_post_9ccf0ba6;

CREATE INDEX djangobb_forum_post_9ccf0ba6
  ON djangobb_forum_post
  USING btree
  (updated_by_id);

-- Index: djangobb_forum_post_created_7f4b3140f52c01f_uniq

-- DROP INDEX djangobb_forum_post_created_7f4b3140f52c01f_uniq;

CREATE INDEX djangobb_forum_post_created_7f4b3140f52c01f_uniq
  ON djangobb_forum_post
  USING btree
  (created);

-- Index: djangobb_forum_post_e8701ad4

-- DROP INDEX djangobb_forum_post_e8701ad4;

CREATE INDEX djangobb_forum_post_e8701ad4
  ON djangobb_forum_post
  USING btree
  (user_id);

-- Index: post_sount_cpeedup

-- DROP INDEX post_sount_cpeedup;

CREATE INDEX post_sount_cpeedup
  ON djangobb_forum_post
  USING btree
  (id, topic_id);

...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063218
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-a,

`OFFSET` тяжелая вещь, от нее нужно избавляться — особенно со столь большими значениями. `COUNT()` же в Postgres'е делает SeqScan из-за особенностей версионного движка.

Чтобы лучше понять что у вас происходит, нужно показать вывод `EXPLAIN (analyze, buffers) ... SELECT ...` для ваших проблемных запросов.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063257
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-aПробовал колонкам выставлять Column storage в EXTERNAL

RTFMNote that SET STORAGE doesn't itself change anything in the table, it just sets the strategy to be pursued during future table updates.



ну и ведите актуальное матвью канутов. если вы их не по каждому фильтру отдельно лудите. чтобы фуллсканом не читать. а джанго засуньте, откуда нашли. если он не исправится.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063287
p2.
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-a...
Код: plsql
1.
2.
3.
4.
SELECT [список колонок, можно считать что *] 
FROM djangobb_forum_post 
WHERE djangobb_forum_post.created < [now timestamp]
OFFSET 1000000 LIMIT 1000

...
Код: plsql
1.
SELECT COUNT * FROM djangobb_forum_post 

поаккуратней с синтаксисом. Где ордер бы? Где скобки?
Может ли created быть больше now? В совокупности с OFFSET 1000000 напрашивается вывод о некорректной настройке поисковика для получения очередной пачки изменений.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063613
Alexius
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-a,

тут либо добавлять память (shared_buffers надеюсь выставлен нормально) либо извращаться с запросами.

первый запрос можно немного ускорить для широких таблиц если например предварительно выбрать только id по заданному условию, используя index only scan (потребуется индекс на (created, id)), а потом уже выбрать 1000 записей по полученным id. увеличение размеров пачек тоже должно помочь. если created уникальное поле, то можно и сразу по нему выбирать.

второй запрос можно ускорить если не считать точное число, а оценку (я так полагаю что там условие какое-то есть). например, можно парсить вывод explain или самому из pg_stats вычислять (если простое условие). тут конечно надо autovacuum_analyze_threshold еще подкрутить и возможно stats_target.
ну или кэшировать результат.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063656
Zim-a
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Это на самом деле тестовый форум - просто провожу так сказать нагрузочное тестирование.

qwwq, Django - это фреймворк на питоне и форуму писал не я. Мне просто надо как-то что-то оптимизировать.
Я там менять ничего не могу. Тем более принцип работы пагинации. Сначала выбирается общее количество, потом разбивается на количество страниц, потом уже оффсетом выбирается нужные посты для нужной страницы.
В любой момент некоторые посты могут быть удалены. И соответственно посты сдвинутся.
Ну это так себе проблема, лишь несколько тем большие.

Оригинальную таблицу пришлось урезать. Вот план из таблицы на 100 000 строк
p2., фреймворк выполняет следующие запросы (из дебага):
Код: plsql
1.
2.
3.
SELECT COUNT('*') AS "__count"
FROM "djangobb_forum_post"
WHERE "djangobb_forum_post"."topic_id" = 1


Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
SELECT "djangobb_forum_post"."id",
       "djangobb_forum_post"."topic_id",
       "djangobb_forum_post"."user_id",
       "djangobb_forum_post"."created",
       "djangobb_forum_post"."updated",
       "djangobb_forum_post"."updated_by_id",
       "djangobb_forum_post"."markup",
       "djangobb_forum_post"."body",
       "djangobb_forum_post"."body_html",
       "djangobb_forum_post"."user_ip",
       "djangobb_forum_topic"."id",
       "djangobb_forum_topic"."forum_id",
       "djangobb_forum_topic"."name",
       "djangobb_forum_topic"."created",
       "djangobb_forum_topic"."updated",
       "djangobb_forum_topic"."user_id",
       "djangobb_forum_topic"."views",
       "djangobb_forum_topic"."sticky",
       "djangobb_forum_topic"."closed",
       "djangobb_forum_topic"."post_count",
       "djangobb_forum_topic"."last_post_id",
       "djangobb_forum_forum"."id",
       "djangobb_forum_forum"."category_id",
       "djangobb_forum_forum"."name",
       "djangobb_forum_forum"."position",
       "djangobb_forum_forum"."description",
       "djangobb_forum_forum"."updated",
       "djangobb_forum_forum"."post_count",
       "djangobb_forum_forum"."topic_count",
       "djangobb_forum_forum"."last_post_id",
       "djangobb_forum_forum"."forum_logo",
       "djangobb_forum_category"."id",
       "djangobb_forum_category"."name",
       "djangobb_forum_category"."position",
       "common_user"."id",
       "common_user"."password",
       "common_user"."last_login",
       "common_user"."is_superuser",
       "common_user"."username",
       "common_user"."first_name",
       "common_user"."last_name",
       "common_user"."email",
       "common_user"."is_staff",
       "common_user"."is_active",
       "common_user"."date_joined",
       "common_user"."avatar",
       "common_user"."country",
       "common_user"."city",
       T6."id",
       T6."password",
       T6."last_login",
       T6."is_superuser",
       T6."username",
       T6."first_name",
       T6."last_name",
       T6."email",
       T6."is_staff",
       T6."is_active",
       T6."date_joined",
       T6."avatar",
       T6."country",
       T6."city"
FROM "djangobb_forum_post"
INNER JOIN "djangobb_forum_topic" ON ("djangobb_forum_post"."topic_id" = "djangobb_forum_topic"."id")
INNER JOIN "djangobb_forum_forum" ON ("djangobb_forum_topic"."forum_id" = "djangobb_forum_forum"."id")
INNER JOIN "djangobb_forum_category" ON ("djangobb_forum_forum"."category_id" = "djangobb_forum_category"."id")
INNER JOIN "common_user" ON ("djangobb_forum_topic"."user_id" = "common_user"."id")
INNER JOIN "common_user" T6 ON ("djangobb_forum_post"."user_id" = T6."id")
WHERE "djangobb_forum_post"."topic_id" = 1
ORDER BY "djangobb_forum_post"."created" ASC LIMIT 10
OFFSET 100000


Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
"Limit  (cost=19002.22..19002.22 rows=1 width=3502) (actual time=338.397..338.397 rows=1 loops=1)"
"  Output: djangobb_forum_post.id, djangobb_forum_post.topic_id, djangobb_forum_post.user_id, djangobb_forum_post.created, djangobb_forum_post.updated, djangobb_forum_post.updated_by_id, djangobb_forum_post.markup, djangobb_forum_post.body, djangobb_forum_p (...)"
"  Buffers: shared hit=22848"
"  ->  Nested Loop  (cost=0.29..19002.22 rows=100000 width=3502) (actual time=0.100..328.335 rows=100001 loops=1)"
"        Output: djangobb_forum_post.id, djangobb_forum_post.topic_id, djangobb_forum_post.user_id, djangobb_forum_post.created, djangobb_forum_post.updated, djangobb_forum_post.updated_by_id, djangobb_forum_post.markup, djangobb_forum_post.body, djangobb_f (...)"
"        Join Filter: (djangobb_forum_post.user_id = t6.id)"
"        Buffers: shared hit=22848"
"        ->  Nested Loop  (cost=0.29..17501.21 rows=100000 width=2334) (actual time=0.081..213.849 rows=100001 loops=1)"
"              Output: djangobb_forum_post.id, djangobb_forum_post.topic_id, djangobb_forum_post.user_id, djangobb_forum_post.created, djangobb_forum_post.updated, djangobb_forum_post.updated_by_id, djangobb_forum_post.markup, djangobb_forum_post.body, djan (...)"
"              Buffers: shared hit=22847"
"              ->  Index Scan using djangobb_forum_post_created_7f4b3140f52c01f_uniq on public.djangobb_forum_post  (cost=0.29..16247.03 rows=100000 width=1053) (actual time=0.027..69.759 rows=100001 loops=1)"
"                    Output: djangobb_forum_post.id, djangobb_forum_post.topic_id, djangobb_forum_post.user_id, djangobb_forum_post.created, djangobb_forum_post.updated, djangobb_forum_post.updated_by_id, djangobb_forum_post.markup, djangobb_forum_post.body (...)"
"                    Filter: (djangobb_forum_post.topic_id = 1)"
"                    Buffers: shared hit=22843"
"              ->  Materialize  (cost=0.00..4.19 rows=1 width=1281) (actual time=0.000..0.000 rows=1 loops=100001)"
"                    Output: djangobb_forum_topic.id, djangobb_forum_topic.forum_id, djangobb_forum_topic.name, djangobb_forum_topic.created, djangobb_forum_topic.updated, djangobb_forum_topic.user_id, djangobb_forum_topic.views, djangobb_forum_topic.sticky (...)"
"                    Buffers: shared hit=4"
"                    ->  Nested Loop  (cost=0.00..4.18 rows=1 width=1281) (actual time=0.043..0.048 rows=1 loops=1)"
"                          Output: djangobb_forum_topic.id, djangobb_forum_topic.forum_id, djangobb_forum_topic.name, djangobb_forum_topic.created, djangobb_forum_topic.updated, djangobb_forum_topic.user_id, djangobb_forum_topic.views, djangobb_forum_topic. (...)"
"                          Join Filter: (djangobb_forum_topic.user_id = common_user.id)"
"                          Buffers: shared hit=4"
"                          ->  Nested Loop  (cost=0.00..3.16 rows=1 width=113) (actual time=0.032..0.036 rows=1 loops=1)"
"                                Output: djangobb_forum_topic.id, djangobb_forum_topic.forum_id, djangobb_forum_topic.name, djangobb_forum_topic.created, djangobb_forum_topic.updated, djangobb_forum_topic.user_id, djangobb_forum_topic.views, djangobb_forum_ (...)"
"                                Join Filter: (djangobb_forum_forum.category_id = djangobb_forum_category.id)"
"                                Rows Removed by Join Filter: 2"
"                                Buffers: shared hit=3"
"                                ->  Nested Loop  (cost=0.00..2.09 rows=1 width=90) (actual time=0.022..0.024 rows=1 loops=1)"
"                                      Output: djangobb_forum_topic.id, djangobb_forum_topic.forum_id, djangobb_forum_topic.name, djangobb_forum_topic.created, djangobb_forum_topic.updated, djangobb_forum_topic.user_id, djangobb_forum_topic.views, djangobb_ (...)"
"                                      Join Filter: (djangobb_forum_topic.forum_id = djangobb_forum_forum.id)"
"                                      Rows Removed by Join Filter: 2"
"                                      Buffers: shared hit=2"
"                                      ->  Seq Scan on public.djangobb_forum_topic  (cost=0.00..1.02 rows=1 width=47) (actual time=0.011..0.012 rows=1 loops=1)"
"                                            Output: djangobb_forum_topic.id, djangobb_forum_topic.forum_id, djangobb_forum_topic.name, djangobb_forum_topic.created, djangobb_forum_topic.updated, djangobb_forum_topic.user_id, djangobb_forum_topic.views, dja (...)"
"                                            Filter: (djangobb_forum_topic.id = 1)"
"                                            Rows Removed by Filter: 1"
"                                            Buffers: shared hit=1"
"                                      ->  Seq Scan on public.djangobb_forum_forum  (cost=0.00..1.03 rows=3 width=43) (actual time=0.003..0.004 rows=3 loops=1)"
"                                            Output: djangobb_forum_forum.id, djangobb_forum_forum.category_id, djangobb_forum_forum.name, djangobb_forum_forum."position", djangobb_forum_forum.description, djangobb_forum_forum.updated, djangobb_forum_forum. (...)"
"                                            Buffers: shared hit=1"
"                                ->  Seq Scan on public.djangobb_forum_category  (cost=0.00..1.03 rows=3 width=23) (actual time=0.002..0.003 rows=3 loops=1)"
"                                      Output: djangobb_forum_category.id, djangobb_forum_category.name, djangobb_forum_category."position""
"                                      Buffers: shared hit=1"
"                          ->  Seq Scan on public.common_user  (cost=0.00..1.01 rows=1 width=1168) (actual time=0.003..0.004 rows=1 loops=1)"
"                                Output: common_user.id, common_user.password, common_user.last_login, common_user.is_superuser, common_user.username, common_user.first_name, common_user.last_name, common_user.email, common_user.is_staff, common_user.is_act (...)"
"                                Buffers: shared hit=1"
"        ->  Materialize  (cost=0.00..1.01 rows=1 width=1168) (actual time=0.000..0.000 rows=1 loops=100001)"
"              Output: t6.id, t6.password, t6.last_login, t6.is_superuser, t6.username, t6.first_name, t6.last_name, t6.email, t6.is_staff, t6.is_active, t6.date_joined, t6.avatar, t6.country, t6.city"
"              Buffers: shared hit=1"
"              ->  Seq Scan on public.common_user t6  (cost=0.00..1.01 rows=1 width=1168) (actual time=0.003..0.004 rows=1 loops=1)"
"                    Output: t6.id, t6.password, t6.last_login, t6.is_superuser, t6.username, t6.first_name, t6.last_name, t6.email, t6.is_staff, t6.is_active, t6.date_joined, t6.avatar, t6.country, t6.city"
"                    Buffers: shared hit=1"
"Total runtime: 338.690 ms"



Старую таблицу я скопировать отдельно, но план такой же каким я его видел и раньше:

Код: plsql
1.
select * from djangobb_forum_post_backup OFFSET 1000000 LIMIT 10


Код: plsql
1.
2.
3.
4.
5.
6.
7.
"Limit  (cost=142359.49..142360.91 rows=10 width=1064) (actual time=11447.579..11447.584 rows=10 loops=1)"
"  Output: id, topic_id, user_id, created, updated, updated_by_id, markup, body, body_html, user_ip"
"  Buffers: shared read=132315"
"  ->  Seq Scan on public.djangobb_forum_post_backup  (cost=0.00..187026.63 rows=1313763 width=1064) (actual time=4.212..11322.402 rows=1000010 loops=1)"
"        Output: id, topic_id, user_id, created, updated, updated_by_id, markup, body, body_html, user_ip"
"        Buffers: shared read=132315"
"Total runtime: 11447.626 ms"
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063678
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-aЭто на самом деле тестовый форум - просто провожу так сказать нагрузочное тестирование.

qwwq, Django - это фреймворк на питонеКО не заметил, что оно тут не первое развесистой джангой помахивает
Zim-aи форуму писал не я. Мне просто надо как-то что-то оптимизировать. -- выкинуть джангу, или научиться таки ей пользоваться, перепиливая во все дыры, если оно не собирается без этого нормально работать

Zim-aЯ там менять ничего не могу. Тем более принцип работы пагинации. Сначала выбирается общее количество, потом разбивается на количество страниц, потом уже оффсетом выбирается нужные посты для нужной страницы.
В любой момент некоторые посты могут быть удалены. И соответственно посты сдвинутся.
Ну это так себе проблема, лишь несколько тем большие.


ещё раз -- пагинация у вас одна сквозь всё, или по всяким фильтрам -- своя ?

а то ж если одна -- то это даже не задача, а гулькин чих. вот только джангу похерить -- и решать.
а вот если по любым фильтрам - своя -- то тут уже никуда не денесся -- придется фуллсканить на миллионных оффсетах. или другую модельку "постраниции" строить. печалька.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063722
Zim-a
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
qwwqещё раз -- пагинация у вас одна сквозь всё, или по всяким фильтрам -- своя ?Для индексации - практически сквозь всё.
WHERE created < [здесь NOW timestamp]

Для страниц форума - дополнительная фильтрация по topic_id

В Django есть стандартный компонент paginator.
Ну я немного посмотрел исходники тоже же phpbb - там тоже такая же беда. Поэтому на рутрекере например темы закрываются при достижении 100 страниц.

Кстати, а какой другой способ сделать пагинацию вы можете предложить? Кроме LIMIT OFFSET?
Хранить дополнительные данные в других таблицах?

Почему вот этот трюк, который работает в MySQL не работает в Postgresl? Почему всё равно просматривается вся таблица, а не только индекс по id?
http://stackoverflow.com/a/4502426/4007475

Код: plsql
1.
2.
3.
4.
5.
SELECT  id
FROM    djangobb_forum_post_backup 
ORDER BY
	id
OFFSET 1000000 LIMIT 30


Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
"Limit  (cost=327022.52..327022.59 rows=30 width=4) (actual time=10226.353..10226.369 rows=30 loops=1)"
"  Output: id"
"  Buffers: shared hit=302 read=173587, temp read=1780 written=2248"
"  ->  Sort  (cost=324522.52..327806.92 rows=1313763 width=4) (actual time=9898.538..10136.370 rows=1000030 loops=1)"
"        Output: id"
"        Sort Key: djangobb_forum_post_backup.id"
"        Sort Method: external merge  Disk: 17976kB"
"        Buffers: shared hit=302 read=173587, temp read=1780 written=2248"
"        ->  Seq Scan on public.djangobb_forum_post_backup  (cost=0.00..187026.63 rows=1313763 width=4) (actual time=0.021..8394.666 rows=1313804 loops=1)"
"              Output: id"
"              Buffers: shared hit=302 read=173587"
"Total runtime: 10232.355 ms"
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063746
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-aКстати, а какой другой способ сделать пагинацию вы можете предложить? Кроме LIMIT OFFSET?
Хранить дополнительные данные в других таблицах?
а что в этом плохого ?
у вас закрытое множество под--пагинируемого.
храните массивы айдишников начала страниц. при удалении -- производите перевычисление (инкрементальное) правых от точки удаления узлов. -- там будет limit 1 без offset, столько лупов -- сколько страниц.


на худой конец открытого множества возможных фильтров -- например откройте курсор (или FOR LOOP) и сразу всё нарежьте. в пределах сессии отдавайте из этой нарезки. можете опять таки подумать, как сдвиги вычислять по событиям удаления.
Zim-aПочему вот этот трюк, который работает в MySQL не работает в Postgresl? Почему всё равно просматривается вся таблица, а не только индекс по id?патамушта

индекс в пг не хранит видимости. много раз перетёрто.




чтобы не сортило на диске -- можете work_mem добавить, но это мало повлияет на время.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063747
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-a,

А индекс `ON djangobb_forum_post_backup(id)` есть? И статистики актуальные?
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063748
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vyegorovZim-a,

А индекс `ON djangobb_forum_post_backup(id)` есть? И статистики актуальные?вы ещё версию пж узнайте. IOS не сразу появился.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063816
Zim-a
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: plsql
1.
2.
3.
4.
5.
6.
7.
  CONSTRAINT post_backup_pk PRIMARY KEY (id)


CREATE INDEX post_backup_pk_index
  ON djangobb_forum_post_backup
  USING btree
  (id);



Статистики актуальные.
Делал VACUUM ANALYZE FULL;

И через pgAdmin REINDEX таблицы.

Строка версии PostgreSQL 9.3.9 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4, 64-bit
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063926
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-a,

Приведите план для

Код: sql
1.
2.
3.
ORDER BY 	id OFFSET 100000 LIMIT 30
--и
ORDER BY 	id OFFSET 10000 LIMIT 30


-- где-то там начнётся выгода от IOS , с т.з. планировщика.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39063954
Zim-a
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Выборки:
Віборка по ID, OFFSET 100 000
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
SELECT  id
FROM    djangobb_forum_post_backup 
ORDER BY
	id
OFFSET 100000 LIMIT 30

"Limit  (cost=255593.04..255669.71 rows=30 width=4) (actual time=70.220..70.237 rows=30 loops=1)"
"  Output: id"
"  Buffers: shared hit=26077"
"  ->  Index Only Scan using post_backup_pk_index on public.djangobb_forum_post_backup  (cost=0.43..3357881.55 rows=1313763 width=4) (actual time=0.031..59.148 rows=100030 loops=1)"
"        Output: id"
"        Heap Fetches: 100030"
"        Buffers: shared hit=26077"
"Total runtime: 70.275 ms"


Выборка * OFFSET 100 000
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
SELECT  *
FROM    djangobb_forum_post_backup 
ORDER BY
	id
OFFSET 100000 LIMIT 30
"Limit  (cost=255593.04..255669.71 rows=30 width=1064) (actual time=54.520..54.536 rows=30 loops=1)"
"  Output: id, topic_id, user_id, created, updated, updated_by_id, markup, body, body_html, user_ip"
"  Buffers: shared hit=26077"
"  ->  Index Scan using post_backup_pk_index on public.djangobb_forum_post_backup  (cost=0.43..3357881.55 rows=1313763 width=1064) (actual time=0.027..45.677 rows=100030 loops=1)"
"        Output: id, topic_id, user_id, created, updated, updated_by_id, markup, body, body_html, user_ip"
"        Buffers: shared hit=26077"
"Total runtime: 54.575 ms"


Выборка по ID, OFFSET 10 000
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
SELECT  ID
FROM    djangobb_forum_post_backup 
ORDER BY
	id
OFFSET 10000 LIMIT 30
"Limit  (cost=25559.69..25636.37 rows=30 width=4) (actual time=7.187..7.207 rows=30 loops=1)"
"  Output: id"
"  Buffers: shared hit=2478"
"  ->  Index Only Scan using post_backup_pk_index on public.djangobb_forum_post_backup  (cost=0.43..3357881.55 rows=1313763 width=4) (actual time=0.037..6.218 rows=10030 loops=1)"
"        Output: id"
"        Heap Fetches: 10030"
"        Buffers: shared hit=2478"
"Total runtime: 7.236 ms"


Выборка *, OFFSET 10 000
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
SELECT  *
FROM    djangobb_forum_post_backup 
ORDER BY
	id
OFFSET 10000 LIMIT 30
"Limit  (cost=25559.69..25636.37 rows=30 width=1064) (actual time=5.827..5.849 rows=30 loops=1)"
"  Output: id, topic_id, user_id, created, updated, updated_by_id, markup, body, body_html, user_ip"
"  Buffers: shared hit=2478"
"  ->  Index Scan using post_backup_pk_index on public.djangobb_forum_post_backup  (cost=0.43..3357881.55 rows=1313763 width=1064) (actual time=0.026..5.016 rows=10030 loops=1)"
"        Output: id, topic_id, user_id, created, updated, updated_by_id, markup, body, body_html, user_ip"
"        Buffers: shared hit=2478"
"Total runtime: 5.883 ms"

...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39064025
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-a,

кажется,
Код: sql
1.
Heap Fetches: 100030

-- как бы говорит нам, что visibility map не актуальна.

олл, поправьте меня, если я не прав.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39064244
Ivan Durak
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
может создать табличку из 1 поля id 1:1 к исходной. И её мучать оффсетами раз авторбез текстовых полей (body и body_html) - и те же выборки оттуда выполняются за 200-300 мс.
А потом уже выбранные несколько id - лукапить по индексу из основной таблицы.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39064373
Zim-a
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ivan Durakможет создать табличку из 1 поля id 1:1 к исходной. И её мучать оффсетами раз авторбез текстовых полей (body и body_html) - и те же выборки оттуда выполняются за 200-300 мс.
А потом уже выбранные несколько id - лукапить по индексу из основной таблицы.
А просто попросить postgres чтобы он большие текстовые поля отдельно ложил и подтягивал их только по мере необходимости нельзя?
Column storage EXTERNAL не для этого разве?
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39064382
Фотография vyegorov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-a,

А он так и делает, из коробки.

Размер страницы 8Кб и запись не может превышать этот размер. Так что любое значение больше примерно 2Кб будет автоматом тостироваться. А вот меньшие значения вполне себе могут и в основной таблице лежать.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39064420
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-a А просто попросить postgres чтобы он большие текстовые поля отдельно ложил и подтягивал их только по мере необходимости нельзя?
Column storage EXTERNAL не для этого разве? так он это начнет делать только *после* того , как вы это скажете, и зальёте данные ПОСЛЕ этого слова. До того у вас ширина записи так и останется "несколько шире" тех считанных байт + системный префикс,

и это шире будет примерно до 2К/на поле, как нам тут подбрасывают.

т.е. ПОСЛЕ того, как вы вот это сказали, вам надо обновить все записи, чтобы в новых версиях оно в external|extended легло, после чего отвакуумится, отреиндекситься и попробовать ещё раз.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39064661
Zim-a
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Я понял.Создал новую таблицу.
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
-- Table: djangobb_forum_post_backup_2

-- DROP TABLE djangobb_forum_post_backup_2;

CREATE TABLE djangobb_forum_post_backup_2
(
  id serial NOT NULL,
  topic_id integer NOT NULL,
  user_id integer NOT NULL,
  created timestamp with time zone NOT NULL,
  updated timestamp with time zone,
  updated_by_id integer,
  markup character varying(15) NOT NULL,
  body text NOT NULL,
  body_html text NOT NULL,
  user_ip inet,
  CONSTRAINT post_backup_2_pk PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE djangobb_forum_post_backup_2
  OWNER TO my_u;
ALTER TABLE djangobb_forum_post_backup_2 ALTER COLUMN body SET STORAGE EXTERNAL;
ALTER TABLE djangobb_forum_post_backup_2 ALTER COLUMN body_html SET STORAGE EXTERNAL;


-- Index: post_backup_2_pk_index

-- DROP INDEX post_backup_2_pk_index;

CREATE INDEX post_backup_2_pk_index
  ON djangobb_forum_post_backup_2
  USING btree
  (id);



Вставил туда все данные
Код: plsql
1.
INSERT INTO djangobb_forum_post_backup_2 SELECT * FROM djangobb_forum_post_backup 



Сделал VACUUM ANALYZE FREEZE
Код: plsql
1.
2.
3.
4.
5.
6.
7.
INFO:  vacuuming "public.djangobb_forum_post_backup_2"
INFO:  "djangobb_forum_post_backup_2": found 0 removable, 1313804 nonremovable row versions in 173809 pages
DETAIL:  0 dead row versions cannot be removed yet.
CPU 12.54s/0.00u sec elapsed 22.50 sec.
INFO:  analyzing "public.djangobb_forum_post_backup_2"
INFO:  "djangobb_forum_post_backup_2": scanned 30000 of 173809 pages, containing 227316 live rows and 0 dead rows; 30000 rows in sample, 1314353 estimated total rows
Суммарное время выполнения запроса: 85029 ms.


И REINDEX.

Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
SELECT COUNT (id) from djangobb_forum_post_backup_2 

"Aggregate  (cost=190231.55..190231.56 rows=1 width=4) (actual time=14927.407..14927.407 rows=1 loops=1)"
"  Output: count(id)"
"  Buffers: shared hit=192 read=173617"
"  ->  Seq Scan on public.djangobb_forum_post_backup_2  (cost=0.00..186947.04 rows=1313804 width=4) (actual time=3.028..14608.890 rows=1313804 loops=1)"
"        Output: id, topic_id, user_id, created, updated, updated_by_id, markup, body, body_html, user_ip"
"        Buffers: shared hit=192 read=173617"
"Total runtime: 14928.792 ms"


Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
SELECT id from djangobb_forum_post_backup_2 order by id OFFSET 1000000 LIMIT 10

"Limit  (cost=326947.03..326947.05 rows=10 width=4) (actual time=18406.933..18406.937 rows=10 loops=1)"
"  Output: id"
"  Buffers: shared hit=256 read=173553, temp read=1780 written=2248"
"  ->  Sort  (cost=324447.03..327731.54 rows=1313804 width=4) (actual time=18062.429..18311.726 rows=1000010 loops=1)"
"        Output: id"
"        Sort Key: djangobb_forum_post_backup_2.id"
"        Sort Method: external merge  Disk: 17976kB"
"        Buffers: shared hit=256 read=173553, temp read=1780 written=2248"
"        ->  Seq Scan on public.djangobb_forum_post_backup_2  (cost=0.00..186947.04 rows=1313804 width=4) (actual time=1.207..16254.702 rows=1313804 loops=1)"
"              Output: id"
"              Buffers: shared hit=256 read=173553"
"Total runtime: 18413.005 ms"
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39064756
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-a,

страничек осталось стока же. сканить -- стока де. вот и времена не поменялись.

покажите план select count(1) из узкой таблички, которую руками обрезали. и vacuum verbose её же -- будем на странички посмотреть
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39064853
Zim-a
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Узкая таблица:
Код: plsql
1.
2.
3.
4.
5.
6.
7.
INFO:  vacuuming "public.djangobb_forum_post_test"
INFO:  "djangobb_forum_post_test": found 0 removable, 1313804 nonremovable row versions in 9661 pages
DETAIL:  0 dead row versions cannot be removed yet.
CPU 0.78s/0.00u sec elapsed 0.99 sec.
INFO:  analyzing "public.djangobb_forum_post_test"
INFO:  "djangobb_forum_post_test": scanned 9661 of 9661 pages, containing 1313804 live rows and 0 dead rows; 30000 rows in sample, 1313804 estimated total rows
Суммарное время выполнения запроса: 1521 ms.


Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
SELECT COUNT(1) FROM djangobb_forum_post_test
"Aggregate  (cost=26083.55..26083.56 rows=1 width=0) (actual time=436.812..436.812 rows=1 loops=1)"
"  Output: count(1)"
"  Buffers: shared hit=32 read=9629"
"  ->  Seq Scan on public.djangobb_forum_post_test  (cost=0.00..22799.04 rows=1313804 width=0) (actual time=0.026..230.793 rows=1313804 loops=1)"
"        Output: id, topic_id, user_id, created, updated, updated_by_id, markup, user_ip"
"        Buffers: shared hit=32 read=9629"
"Total runtime: 436.867 ms"


Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
SELECT COUNT(*) FROM djangobb_forum_post_test
"Aggregate  (cost=26083.55..26083.56 rows=1 width=0) (actual time=456.394..456.394 rows=1 loops=1)"
"  Output: count(*)"
"  Buffers: shared hit=64 read=9597"
"  ->  Seq Scan on public.djangobb_forum_post_test  (cost=0.00..22799.04 rows=1313804 width=0) (actual time=0.030..253.311 rows=1313804 loops=1)"
"        Output: id, topic_id, user_id, created, updated, updated_by_id, markup, user_ip"
"        Buffers: shared hit=64 read=9597"
"Total runtime: 456.437 ms"


Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
SELECT id FROM djangobb_forum_post_test ORDER BY id OFFSET 1000000 LIMIT 10
"Limit  (cost=162799.03..162799.05 rows=10 width=4) (actual time=1890.742..1890.746 rows=10 loops=1)"
"  Output: id"
"  Buffers: shared hit=128 read=9533, temp read=1946 written=2249"
"  ->  Sort  (cost=160299.03..163583.54 rows=1313804 width=4) (actual time=1536.116..1799.977 rows=1000010 loops=1)"
"        Output: id"
"        Sort Key: djangobb_forum_post_test.id"
"        Sort Method: external merge  Disk: 17984kB"
"        Buffers: shared hit=128 read=9533, temp read=1946 written=2249"
"        ->  Seq Scan on public.djangobb_forum_post_test  (cost=0.00..22799.04 rows=1313804 width=4) (actual time=0.031..305.366 rows=1313804 loops=1)"
"              Output: id"
"              Buffers: shared hit=128 read=9533"
"Total runtime: 1895.523 ms"


[SRC PLSQL][/SRC]
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39065039
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-a,

173809 pages <=> 9661 pages

14928.792 ms <=> 436.867 ms

во втором случае видимо затраты на агрегирование уже вкладываются ощутимо

почему external не заужает таблику -- хз. Может быть заполнение считается из расчетной ширины записи без учета STORAGE ... EXTERNAL. Попробуйте ещё FILLFACTOR на табличку погонять туда -- сюда. (тоже затратно для тестера). Хотя лучше у знающих людей спросить. Максим наверное должен знать.



и вот это: "Sort Method: external merge Disk: 17984kB" говорит о том, что оффсету на миллион без IOS надо добавить work_mem до 18--20 MB даже в узком случае. Для сорта.
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39065107
Zim-a
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В конфиге postgresql.conf стояло
work_mem = 32MB
...
Рейтинг: 0 / 0
Медленный COUNT, OFFSET. Таблица не помещается в RAM
    #39065125
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Zim-aВ конфиге postgresql.conf стояло
work_mem = 32MB

посмотрите SHOW work_mem;
-- он м.б. переопределен для БД (ALTER database set work_mem TO ..) или для роли (ALTER ROLE ...)
...
Рейтинг: 0 / 0
25 сообщений из 26, страница 1 из 2
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Медленный COUNT, OFFSET. Таблица не помещается в RAM
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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