查询:
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 错误地估计了两种加入方式的相对成本?我在成本估算中看到它认为散列连接会更快。它对 index-join 成本的估计降低了 500 倍。
我怎样才能给 Postgres 更多的线索?我确实VACUUM ANALYZE
在运行上述所有内容之前立即运行了。
有趣的是,如果我对游戏数量较少的玩家运行此查询,Postgres 会选择执行索引扫描 + 嵌套循环。因此,关于大量游戏的某些东西会引起这种不受欢迎的行为,即相对估计成本与实际估计成本不一致。
最后,我应该使用 Postgres 吗?我不希望成为数据库调优方面的专家,因此我正在寻找一种数据库,该数据库能够在尽职尽责的开发人员的关注水平下运行得相当好,而不是专门的 DBA。我担心如果我坚持使用 Postgres,我会遇到源源不断的此类问题,这将迫使我成为 Postgres 专家,也许另一个 DB 会更宽容地采用更随意的方法。
Postgres 专家 (RhodiumToad) 审查了我的完整数据库设置 ( http://pastebin.com/77QuiQSp ) 并推荐了set cpu_tuple_cost = 0.1
. 这给了一个戏剧性的加速: http: //pastebin.com/nTHvSHVd
或者,切换到 MySQL 也很好地解决了这个问题。我在我的 OS X 机器上默认安装了 MySQL 和 Postgres,MySQL 的速度提高了 2 倍,通过重复执行查询来比较“预热”的查询。在“冷”查询上,即第一次执行给定查询时,MySQL 的速度要快 5 到 150 倍。冷查询的性能对于我的特定应用程序非常重要。
就我而言,最大的问题仍然悬而未决——Postgres 是否需要更多的摆弄和配置才能比 MySQL 运行得更好?例如,考虑到这里评论者提供的建议都没有奏效。