powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Разное время выполнения почти одинаковых запросов
19 сообщений из 19, страница 1 из 1
Разное время выполнения почти одинаковых запросов
    #34028098
RussianSpy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Доброго времени суток. Столкнулся с проблемой, с которой бьюсь уже две недели и все решения которые приходили на ум не приносят результата. Итак. Есть две таблицы.
В первой хранятся записи об изменении денег юзера:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
CREATE TABLE pays(
  PlayID bigserial UNIQUE PRIMARY KEY,
  SessionID bigint NOT NULL,
  GameID int NOT NULL,
  BalanceChange int NOT NULL,
  BonusChange int NOT NULL,
  PayTime timestamp DEFAULT Now(),
  PayType int DEFAULT  0 ,
  CashInID int DEFAULT  0 ,
  CashOutID int DEFAULT  0 ,
  BonusID int DEFAULT  0 
);
Во второй хранятся сессии юзеров:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
CREATE TABLE sessions(
  SessionID bigserial UNIQUE PRIMARY KEY,
  UserID int NOT NULL,
  Key1 int NOT NULL,
  Key2 int NOT NULL,
  IsClosed bool DEFAULT FALSE NOT NULL,
  LoginTime timestamp NOT NULL DEFAULT Now(),
  LastActionTime timestamp NOT NULL DEFAULT Now(),
  LogoutTime timestamp,
  SessionIP int,
  hhash char( 36 ),
  Owner int NOT NULL
);

В таблице pays дополнительно создан индекс по полю SessionID (тип индекса BTree).

Теперь задача следующая: зная UserID подсчитать все его сессии.
Используем такой запрос:
Код: plaintext
SELECT COUNT(*) as cnt FROM pays p, sessions s WHERE s.userid= 2  AND s.sessionid=p.sessionid

Далее начинается шаманство: для некоторых UserID запрос выполняется в доли секунды, для других от 1 до 2.5 секунд. Делаем EXPLAIN ANALYZE для двух разных случаев:
Быстрое выполнение
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
Aggregate  (cost= 3704 . 35 .. 3704 . 36  rows= 1  width= 0 ) (actual time= 13 . 077 .. 13 . 078  rows= 1  loops= 1 )

  ->  Nested Loop  (cost= 2 . 44 .. 3662 . 40  rows= 16779  width= 0 ) (actual time= 0 . 207 .. 10 . 630  rows= 2558  loops= 1 )

        ->  Bitmap Heap Scan on sessions s  (cost= 2 . 44 .. 118 . 31  rows= 125  width= 8 ) (actual time= 0 . 138 .. 0 . 413  rows= 135  loops= 1 )

              Recheck Cond: (userid =  2 )

              ->  Bitmap Index Scan on "SessUserIDIndx"  (cost= 0 . 00 .. 2 . 44  rows= 125  width= 0 ) (actual time= 0 . 094 .. 0 . 094  rows= 154  loops= 1 )

                    Index Cond: (userid =  2 )

        ->  Index Scan using "sessionIDIndx2" on pays p  (cost= 0 . 00 .. 20 . 49  rows= 629  width= 8 ) (actual time= 0 . 007 .. 0 . 040  rows= 19  loops= 135 )

              Index Cond: ("outer".sessionid = p.sessionid)

Total runtime:  13 . 261  ms
По результату запроса возвращает: 2558

Медленное выполнение:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
Aggregate  (cost= 34796 . 13 .. 34796 . 14  rows= 1  width= 0 ) (actual time= 2882 . 673 .. 2882 . 674  rows= 1  loops= 1 )

  ->  Merge Join  (cost= 0 . 00 .. 33875 . 65  rows= 368193  width= 0 ) (actual time= 0 . 304 .. 2882 . 012  rows= 643  loops= 1 )

        Merge Cond: ("outer".sessionid = "inner".sessionid)

        ->  Index Scan using sessions_pkey on sessions s  (cost= 0 . 00 .. 239 . 22  rows= 2743  width= 8 ) (actual time= 0 . 189 .. 11 . 249  rows= 2795  loops= 1 )

              Filter: (userid =  1 )

        ->  Index Scan using "sessionIDIndx2" on pays p  (cost= 0 . 00 .. 27371 . 85  rows= 1030350  width= 8 ) (actual time= 0 . 031 .. 1887 . 719  rows= 1026261  loops= 1 )

Total runtime:  2882 . 937  ms
По результату запроса возвращает: 937

Принципиальное различие между пользователями с UserID 2 и 1 состоит в том, что у юзера UserID=2 (быстрого) записей в таблице сессий 136, а у юзера UserID=1(медленного) записей 2786. Но тем не менее разница во времени выолнения запросов составляет 2 порядка.

Конфигурация машины: AMD Athlon 2800+, 512 Mb RAM, SATA RAID 0+1
ПО: FreeBSD 6.0, PostgreSQL 8.1.2
Размеры таблиц: Sessions - 7606 записей (1 613 Кбайт), Pays - 1 030 350 записей (196 222 Кбайт)

В чем может быть проблема столь большой разницы в выполнении запросов? Что можно сделать чтобы ускорить медленные запросы? Куда копать?
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028175
Фотография Кувалдин Роман
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В одном случае у тебя условию удовлетворяют 629 записей, а в другом - свыше миллиона. Вот тебе и разница во времени.


=====================================
Страну, в которой все ходят на бровях,
на колени не поставишь...
=====================================
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028186
RussianSpy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
И как лечить? Как заставить сервер БД выполнять запрос быстрее?
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028247
ilejn
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
RussianSpyИ как лечить? Как заставить сервер БД выполнять запрос быстрее?

Почему не показано, какие индексы использованы? Это ж самое главное в данном случае!
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028254
Фотография Кувалдин Роман
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Гм. Если ему для выполнения условия надо перебрать миллион записей - то ты ему не поможешь.


=====================================
Страну, в которой все ходят на бровях,
на колени не поставишь...
=====================================
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028294
Фотография Кувалдин Роман
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ilejn RussianSpyИ как лечить? Как заставить сервер БД выполнять запрос быстрее?

Почему не показано, какие индексы использованы? Это ж самое главное в данном случае!

Все там показано. В первом случае:
Код: plaintext
Index Scan using "sessionIDIndx2" on pays p  (cost= 0 . 00 .. 20 . 49  rows= 629  width= 8 ) (actual time= 0 . 007 .. 0 . 040  rows= 19  loops= 135 )
Во втором случае:
Код: plaintext
Index Scan using "sessionIDIndx2" on pays p  (cost= 0 . 00 .. 27371 . 85  rows= 1030350  width= 8 ) (actual time= 0 . 031 .. 1887 . 719  rows= 1026261  loops= 1 )

Остальное несущественно.
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028303
RussianSpy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ilejnПочему не показано, какие индексы использованы? Это ж самое главное в данном случае!
Про индексы написано у меня.
В таблице pays дополнительно создан индекс по полю SessionID (тип индекса BTree).

Кувалдин Роман Гм. Если ему для выполнения условия надо перебрать миллион записей - то ты ему не поможешь.
почему же в таком случае один запрос выполняется 13 мсек, а второй почти в 200 раз медленнее?
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028322
Фотография Кувалдин Роман
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Попробуй вот такой запрос:
Код: plaintext
SELECT COUNT(PlayID) as cnt FROM pays p WHERE p.sessionid in (select distinct sessionid from sessions s where s.userid= 2 )

Ну и план покажи.


=====================================
Страну, в которой все ходят на бровях,
на колени не поставишь...
=====================================
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028419
Фотография Кувалдин Роман
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А потом вот этот:
Код: plaintext
SELECT COUNT(PlayID) as cnt FROM sessions s INNER JOIN pays p on (p.sessionid=s.sessionid) WHERE s.userid= 2 
И тоже план покажи.

=====================================
Страну, в которой все ходят на бровях,
на колени не поставишь...
=====================================
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028420
RussianSpy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Кувалдин РоманПопробуй вот такой запрос:
Код: plaintext
SELECT COUNT(PlayID) as cnt FROM pays p WHERE p.sessionid in (select distinct sessionid from sessions s where s.userid= 2 )

Ну и план покажи.

Запрос возвратил 2619 (это тестер работает - потому количество записей подрастает)
Вот план:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
Aggregate  (cost= 3893 . 72 .. 3893 . 73  rows= 1  width= 8 ) (actual time= 14 . 189 .. 14 . 190  rows= 1  loops= 1 )

  ->  Nested Loop  (cost= 122 . 66 .. 3694 . 71  rows= 79603  width= 8 ) (actual time= 0 . 884 .. 11 . 665  rows= 2619  loops= 1 )

        ->  Unique  (cost= 122 . 66 .. 123 . 29  rows= 125  width= 8 ) (actual time= 0 . 808 .. 1 . 203  rows= 135  loops= 1 )

              ->  Sort  (cost= 122 . 66 .. 122 . 97  rows= 125  width= 8 ) (actual time= 0 . 806 .. 0 . 927  rows= 135  loops= 1 )

                    Sort Key: s.sessionid

                    ->  Bitmap Heap Scan on sessions s  (cost= 2 . 44 .. 118 . 31  rows= 125  width= 8 ) (actual time= 0 . 141 .. 0 . 505  rows= 135  loops= 1 )

                          Recheck Cond: (userid =  2 )

                          ->  Bitmap Index Scan on "SessUserIDIndx"  (cost= 0 . 00 .. 2 . 44  rows= 125  width= 0 ) (actual time= 0 . 117 .. 0 . 117  rows= 204  loops= 1 )

                                Index Cond: (userid =  2 )

        ->  Index Scan using "sessionIDIndx2" on pays p  (cost= 0 . 00 .. 20 . 60  rows= 637  width= 16 ) (actual time= 0 . 007 .. 0 . 041  rows= 19  loops= 135 )

              Index Cond: (p.sessionid = "outer".sessionid)

Total runtime:  14 . 388  ms

Собственно случай с UserID=2 - это случай быстрого выполнения.
Вот тоже самое для UserID=1 - медленный случай
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
Aggregate  (cost= 54143 . 10 .. 54143 . 11  rows= 1  width= 8 ) (actual time= 2735 . 282 .. 2735 . 283  rows= 1  loops= 1 )

  ->  Hash Join  (cost= 280 . 37 .. 51567 . 14  rows= 1030383  width= 8 ) (actual time= 19 . 780 .. 2734 . 622  rows= 643  loops= 1 )

        Hash Cond: ("outer".sessionid = "inner".sessionid)

        ->  Seq Scan on pays p  (cost= 0 . 00 .. 23514 . 83  rows= 1030383  width= 16 ) (actual time= 0 . 026 .. 1672 . 179  rows= 1030440  loops= 1 )

        ->  Hash  (cost= 273 . 51 .. 273 . 51  rows= 2743  width= 8 ) (actual time= 19 . 585 .. 19 . 585  rows= 2795  loops= 1 )

              ->  Unique  (cost= 0 . 00 .. 246 . 08  rows= 2743  width= 8 ) (actual time= 0 . 116 .. 15 . 714  rows= 2795  loops= 1 )

                    ->  Index Scan using sessions_pkey on sessions s  (cost= 0 . 00 .. 239 . 22  rows= 2743  width= 8 ) (actual time= 0 . 112 .. 9 . 992  rows= 2795  loops= 1 )

                          Filter: (userid =  1 )

Total runtime:  2735 . 558  ms
Приведу кое-какие данные по разным UserID:

Код: plaintext
1.
2.
UserID | Кол-во записей в Sessions | Кол-во записей в pays
    1                2786                     643
    2                137                      2619
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028482
LeXa NalBat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Попробуйте "vacuum analyze", "set enable merge_join to off".

Планы разные из-за того что постгрес предполагает, что при условии userid=2 после join-а получится 16779 строк, а для userid=1 - 368193 строк.

PS. Кувалдин РоманВ одном случае у тебя условию удовлетворяют 629 записей, а в другом - свыше миллиона.Не так. Миллион строк во втором плане - без ограничения по sessionid.

Кувалдин РоманГм. Если ему для выполнения условия надо перебрать миллион записей - то ты ему не поможешь.Не обязательно перебирать миллион записей.

RussianSpyпочему же в таком случае один запрос выполняется 13 мсек, а второй почти в 200 раз медленнее?Потому что постгрес выбрал разные планы запросов.
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028496
RussianSpy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Делал VACUUM ANALYZE, VACUUM FULL ANALYZE
Делал REINDEX... Не помогает... Постгрес в двух почти одинаковых случаях выбирает разные планы... Вот это мне лично непонятно. Как вообще можно влиять на это?
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028499
Фотография Кувалдин Роман
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LeXa NalBat Кувалдин РоманГм. Если ему для выполнения условия надо перебрать миллион записей - то ты ему не поможешь.Не обязательно перебирать миллион записей.

Возвращаемся к спору в соседнем топике: как в версионнике, не перебирая таблицы, узнать, сколько строк входят в данное условие...
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028508
Фотография Кувалдин Роман
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
RussianSpyДелал VACUUM ANALYZE, VACUUM FULL ANALYZE
Делал REINDEX... Не помогает... Постгрес в двух почти одинаковых случаях выбирает разные планы... Вот это мне лично непонятно. Как вообще можно влиять на это?

Стоимость каждого плана.
Есть у меня сильное подозрение, что nested loops для 2786 записей не выполняется в силу конструктивных ограничений. Кто-нибудь помнит, какова глубина рекурсии в plpgsql-функциях?
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028559
LeXa NalBat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
RussianSpyКак вообще можно влиять на это?добиваться предпочтительного с вашей точки зрения плана можно с помощью:

1. vacuum analyze

2. set enable_...

3. тюнить константы влияющие на планировщик: random_page_cost и др. (см. доку)

4. искать ляпы в сборщике статистики, планировщике и писать баг-репорты

Кувалдин РоманЕсть у меня сильное подозрение, что nested loops для 2786 записей не выполняется в силу конструктивных ограничений.Nested Loop возможен для 2786 записей.

Кувалдин Роман LeXa NalBat Кувалдин РоманГм. Если ему для выполнения условия надо перебрать миллион записей - то ты ему не поможешь.Не обязательно перебирать миллион записей.Возвращаемся к спору в соседнем топике: как в версионнике, не перебирая таблицы, узнать, сколько строк входят в данное условие...Не вижу связи между темой версионник-блокировочник и данной. Чтобы не захламлять этот топик, можно создать рядом, я попробую ответить на ваши вопросы.
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028633
RussianSpy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
LeXa NalBatдобиваться предпочтительного с вашей точки зрения плана можно с помощью:

1. vacuum analyze

2. set enable_...

Вот с такими условиями скорость выполнения запроса стала нормальной:

Код: plaintext
1.
2.
3.
set enable_hashjoin =off;
set enable_mergejoin =off;
EXPLAIN ANALYZE SELECT COUNT(PlayID) as cnt FROM pays p, sessions s WHERE p.sessionid =s.sessionid AND s.userid= 1 

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
Aggregate  (cost= 79469 . 35 .. 79469 . 36  rows= 1  width= 8 ) (actual time= 41 . 318 .. 41 . 319  rows= 1  loops= 1 )

  ->  Nested Loop  (cost= 0 . 00 .. 78548 . 84  rows= 368205  width= 8 ) (actual time= 8 . 632 .. 40 . 631  rows= 643  loops= 1 )

        ->  Seq Scan on sessions s  (cost= 0 . 00 .. 204 . 95  rows= 2743  width= 8 ) (actual time= 0 . 100 .. 10 . 511  rows= 2795  loops= 1 )

              Filter: (userid =  1 )

        ->  Index Scan using "sessionIDIndx2" on pays p  (cost= 0 . 00 .. 20 . 60  rows= 637  width= 16 ) (actual time= 0 . 008 .. 0 . 008  rows= 0  loops= 2795 )

              Index Cond: (p.sessionid = "outer".sessionid)

Total runtime:  41 . 504  ms

Спасибо огромное за советы. Возможно метод кривой, но тем не менее удалось ускорить выполнение запроса в 220 раз, а это того стоит.


ЗЫ Признаться честно я не знал о таких вещах как set enable_hashjoin =off. Теперь внимательно прочту п.17.6 официального мануала. ;-)
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028661
LeXa NalBat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
RussianSpyПризнаться честно я не знал о таких вещах как set enable_hashjoin =off.Возможно, удастся подкрутить константы random_page_cost и др (это кропотливое занятие), и необходимость в set enable отпадет, постгрес сразу будет выбирать "правильный" план. Мы эти константы подкручивали. Как написано в migration 8.2, при переходе на версию 8.2 их скорее всего придется "перекручивать".
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028792
Andrew Sagulin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Не совет, а скорее из любопытства: если попробовать (без подкрутки enable_*) следующий запрос:

Код: plaintext
SELECT COUNT(PlayID) as cnt FROM pays p, sessions s WHERE p.sessionid = s.sessionid+ 0  AND s.userid= 1 

что-нибудь изменится?
...
Рейтинг: 0 / 0
Разное время выполнения почти одинаковых запросов
    #34028852
RussianSpy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Andrew SagulinНе совет, а скорее из любопытства: если попробовать (без подкрутки enable_*) следующий запрос:

Код: plaintext
SELECT COUNT(PlayID) as cnt FROM pays p, sessions s WHERE p.sessionid = s.sessionid+ 0  AND s.userid= 1 

что-нибудь изменится?

Да изменилось...в худшую сторону

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
Aggregate  (cost= 67778 . 88 .. 67778 . 89  rows= 1  width= 8 ) (actual time= 2952 . 517 .. 2952 . 518  rows= 1  loops= 1 )

  ->  Merge Join  (cost= 317 . 53 .. 63411 . 85  rows= 1746811  width= 8 ) (actual time= 24 . 140 .. 2951 . 821  rows= 643  loops= 1 )

        Merge Cond: ("outer".sessionid = "inner"."?column2?")

        ->  Index Scan using "sessionIDIndx2" on pays p  (cost= 0 . 00 .. 27373 . 21  rows= 1030383  width= 16 ) (actual time= 9 . 982 .. 1932 . 801  rows= 1026261  loops= 1 )

        ->  Sort  (cost= 317 . 53 .. 324 . 39  rows= 2743  width= 8 ) (actual time= 14 . 037 .. 16 . 725  rows= 3062  loops= 1 )

              Sort Key: (s.sessionid +  0 )

              ->  Bitmap Heap Scan on sessions s  (cost= 17 . 60 .. 160 . 89  rows= 2743  width= 8 ) (actual time= 1 . 131 .. 8 . 384  rows= 2795  loops= 1 )

                    Recheck Cond: (userid =  1 )

                    ->  Bitmap Index Scan on "SessUserIDIndx"  (cost= 0 . 00 .. 17 . 60  rows= 2743  width= 0 ) (actual time= 1 . 034 .. 1 . 034  rows= 2795  loops= 1 )

                          Index Cond: (userid =  1 )

Total runtime:  2953 . 150  ms

...
Рейтинг: 0 / 0
19 сообщений из 19, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Разное время выполнения почти одинаковых запросов
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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