Запрос:
SELECT "replays_game".*
FROM "replays_game"
INNER JOIN
"replays_playeringame" ON "replays_game"."id" = "replays_playeringame"."game_id"
WHERE "replays_playeringame"."player_id" = 50027
Если я устанавливаю SET enable_seqscan = off
, то он выполняет быструю вещь, которая:
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=0.00..27349.80 rows=3395 width=72) (actual time=28.726..65.056 rows=3398 loops=1)
-> Index Scan using replays_playeringame_player_id on replays_playeringame (cost=0.00..8934.43 rows=3395 width=4) (actual time=0.019..2.412 rows=3398 loops=1)
Index Cond: (player_id = 50027)
-> Index Scan using replays_game_pkey on replays_game (cost=0.00..5.41 rows=1 width=72) (actual time=0.017..0.017 rows=1 loops=3398)
Index Cond: (id = replays_playeringame.game_id)
Total runtime: 65.437 ms
Но без страшного enable_seqscan он предпочитает делать медленнее:
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Hash Join (cost=7330.18..18145.24 rows=3395 width=72) (actual time=92.380..535.422 rows=3398 loops=1)
Hash Cond: (replays_playeringame.game_id = replays_game.id)
-> Index Scan using replays_playeringame_player_id on replays_playeringame (cost=0.00..8934.43 rows=3395 width=4) (actual time=0.020..2.899 rows=3398 loops=1)
Index Cond: (player_id = 50027)
-> Hash (cost=3668.08..3668.08 rows=151208 width=72) (actual time=90.842..90.842 rows=151208 loops=1)
Buckets: 1024 Batches: 32 (originally 16) Memory Usage: 1025kB
-> Seq Scan on replays_game (cost=0.00..3668.08 rows=151208 width=72) (actual time=0.020..29.061 rows=151208 loops=1)
Total runtime: 535.821 ms
Вот соответствующие индексы:
Index "public.replays_game_pkey"
Column | Type | Definition
--------+---------+------------
id | integer | id
primary key, btree, for table "public.replays_game"
Index "public.replays_playeringame_player_id"
Column | Type | Definition
-----------+---------+------------
player_id | integer | player_id
btree, for table "public.replays_playeringame"
Итак, мой вопрос: что я делаю неправильно, что Postgres неправильно оценивает относительные затраты на два способа соединения? В оценках затрат я вижу, что хеш-соединение будет быстрее. И его оценка стоимости индекса-соединения отключена в 500 раз.
Как я могу дать Postgres больше подсказки? Я запускал a VACUUM ANALYZE
сразу же после запуска всего вышеперечисленного.
Интересно, что если я запустил этот запрос для игрока с меньшим количеством игр, Postgres решит сделать index-scan + вложенный цикл. Так что что-то о больших # играх щекочет это нежелательное поведение, когда относительная оценочная стоимость не соответствует фактической сметной стоимости.
Наконец, следует ли вообще использовать Postgres? Я не хочу стать экспертом в настройке базы данных, поэтому я ищу базу данных, которая будет достаточно хорошо работать с сознательным уровнем внимания разработчиков, в отличие от выделенного администратора баз данных. Я боюсь, что если я буду придерживаться Postgres, у меня будет постоянный поток таких проблем, который заставит меня стать экспертом Postgres, и, возможно, еще одна БД будет более прощать более случайный подход.
Эксперт Postgres (RhodiumToad) рассмотрел мои полные настройки базы данных (http://pastebin.com/77QuiQSp) и рекомендовал set cpu_tuple_cost = 0.1
. Это дало резкое ускорение: http://pastebin.com/nTHvSHVd
В качестве альтернативы, переключение на MySQL также решило проблему довольно красиво. У меня установлена установка MySQL и Postgres по умолчанию в моем ящике OS X, а MySQL в 2 раза быстрее, сравнивая запросы, которые "разогреваются", повторно выполняя запрос. В "холодных" запросах, то есть в первый раз, когда данный запрос выполняется, MySQL в 5-150 раз быстрее. Производительность холодных запросов очень важна для моего конкретного приложения.
Большой вопрос, насколько я могу судить, все еще остается в силе - будет ли Postgres больше возиться и конфигурировать, чтобы работать хорошо, чем MySQL? Например, учтите, что ни одна из предложений, предложенных комментаторами, не работала.