【问题标题】:PostgreSql 9.2 Join Aggregate takes long timePostgreSql 9.2 加入聚合需要很长时间
【发布时间】:2013-12-17 11:31:08
【问题描述】:

早上好,我在 JOIN 每个表中都有一些表,每个表都有索引,我不明白为什么我们在聚合上浪费了时间;查询是:

EXPLAIN ANALYZE SELECT COUNT(handsData.id) as hands 

FROM 
  stats stats JOIN heronames players on (stats.heronameid = players.heronameid) 
  JOIN PREFLOP preflopStats on preflopStats.preflopstatsid = stats.preflopstatsid 
  JOIN FLOP flopStats on flopStats.flopstatsid = stats.flopstatsid 
  JOIN TURN turnStats on turnStats.turnstatsid = stats.turnstatsid 
  JOIN RIVER riverStats on riverStats.riverstatsid = stats.riverstatsid 
  JOIN hands AS handsData on handsData.id = stats.handid 
  JOIN (SELECT tournamentid FROM tournaments WHERE tournamentcode IS NULL) AS smallTournaments ON handsData.tournamentid = smallTournaments.tournamentid 
  JOIN (SELECT pokertypeid AS ptid, pokertype AS pokertypeName FROM poker_types) AS poker_types ON handsData.pokerType = poker_types.ptid 

WHERE players.heroName='RubenGrimes' AND players.networkNumber=2

在 stats、flopstats、turnstats 和 riverstas 表上,我们有大约 70 万条记录。

在手牌上大约有 120k 条记录。

在其他表上一些 k 记录。

"Aggregate  (cost=5219.65..5219.66 rows=1 width=8) (actual time=2080.230..2080.230 rows=1 loops=1)"
"  ->  Nested Loop  (cost=10.47..5219.31 rows=138 width=8) (actual time=20.190..2065.578 rows=119755 loops=1)"
"        Join Filter: (handsdata.pokertype = (public.poker_types.pokertypeid)::double precision)"
"        Rows Removed by Join Filter: 239510"
"        ->  Nested Loop  (cost=10.47..5211.03 rows=138 width=16) (actual time=20.169..1953.894 rows=119755 loops=1)"
"              ->  Nested Loop  (cost=10.47..4235.67 rows=138 width=20) (actual time=20.145..1641.112 rows=119937 loops=1)"
"                    ->  Nested Loop  (cost=10.47..3260.20 rows=138 width=4) (actual time=20.134..1315.970 rows=119937 loops=1)"
"                          ->  Nested Loop  (cost=10.47..2732.02 rows=138 width=8) (actual time=20.100..1047.149 rows=119937 loops=1)"
"                                ->  Nested Loop  (cost=10.47..2203.84 rows=138 width=12) (actual time=20.065..774.187 rows=119937 loops=1)"
"                                      ->  Nested Loop  (cost=10.47..1675.66 rows=138 width=16) (actual time=20.027..496.012 rows=119937 loops=1)"
"                                            ->  Nested Loop  (cost=10.47..1147.48 rows=138 width=20) (actual time=19.983..183.725 rows=119937 loops=1)"
"                                                  ->  Seq Scan on heronames players  (cost=0.00..226.41 rows=1 width=4) (actual time=1.669..1.682 rows=1 loops=1)"
"                                                        Filter: ((heroname = 'RubenGrimes'::text) AND (networknumber = 2))"
"                                                        Rows Removed by Filter: 9826"
"                                                  ->  Bitmap Heap Scan on stats  (cost=10.47..918.63 rows=244 width=24) (actual time=18.308..143.084 rows=119937 loops=1)"
"                                                        Recheck Cond: (heronameid = players.heronameid)"
"                                                        ->  Bitmap Index Scan on "stats index"  (cost=0.00..10.41 rows=244 width=0) (actual time=15.829..15.829 rows=119937 loops=1)"
"                                                              Index Cond: (heronameid = players.heronameid)"
"                                            ->  Index Only Scan using "preflop index" on preflop preflopstats  (cost=0.00..3.82 rows=1 width=8) (actual time=0.002..0.002 rows=1 loops=119937)"
"                                                  Index Cond: (preflopstatsid = stats.preflopstatsid)"
"                                                  Heap Fetches: 0"
"                                      ->  Index Only Scan using "flop index" on flop flopstats  (cost=0.00..3.82 rows=1 width=8) (actual time=0.002..0.002 rows=1 loops=119937)"
"                                            Index Cond: (flopstatsid = stats.flopstatsid)"
"                                            Heap Fetches: 0"
"                                ->  Index Only Scan using "turn index" on turn turnstats  (cost=0.00..3.82 rows=1 width=8) (actual time=0.002..0.002 rows=1 loops=119937)"
"                                      Index Cond: (turnstatsid = stats.turnstatsid)"
"                                      Heap Fetches: 0"
"                          ->  Index Only Scan using "river index" on river riverstats  (cost=0.00..3.82 rows=1 width=8) (actual time=0.002..0.002 rows=1 loops=119937)"
"                                Index Cond: (riverstatsid = stats.riverstatsid)"
"                                Heap Fetches: 0"
"                    ->  Index Scan using hands_pkey on hands handsdata  (cost=0.00..7.06 rows=1 width=20) (actual time=0.002..0.002 rows=1 loops=119937)"
"                          Index Cond: (id = stats.handid)"
"              ->  Index Scan using "tournaments index" on tournaments  (cost=0.00..7.06 rows=1 width=8) (actual time=0.002..0.002 rows=1 loops=119937)"
"                    Index Cond: (tournamentid = handsdata.tournamentid)"
"                    Filter: (tournamentcode IS NULL)"
"                    Rows Removed by Filter: 0"
"        ->  Materialize  (cost=0.00..1.04 rows=3 width=4) (actual time=0.000..0.000 rows=3 loops=119755)"
"              ->  Seq Scan on poker_types  (cost=0.00..1.03 rows=3 width=4) (actual time=0.010..0.010 rows=3 loops=1)"

有什么建议可以改善我们的加入吗?

提前致谢

编辑:

我添加了两个缺失的索引,现在:

"Aggregate  (cost=4537.05..4537.06 rows=1 width=8) (actual time=4726.083..4726.083 rows=1 loops=1)"
"  ->  Nested Loop  (cost=10.47..4536.70 rows=138 width=8) (actual time=41.193..4692.321 rows=119755 loops=1)"
"        Join Filter: (handsdata.pokertype = (public.poker_types.pokertypeid)::double precision)"
"        Rows Removed by Join Filter: 239510"
"        ->  Nested Loop  (cost=10.47..4528.42 rows=138 width=16) (actual time=41.144..4438.428 rows=119755 loops=1)"
"              ->  Nested Loop  (cost=10.47..4017.53 rows=138 width=20) (actual time=41.100..3656.244 rows=119937 loops=1)"
"                    ->  Nested Loop  (cost=10.47..3042.06 rows=138 width=4) (actual time=41.081..2925.372 rows=119937 loops=1)"
"                          ->  Nested Loop  (cost=10.47..2513.88 rows=138 width=8) (actual time=41.064..2318.648 rows=119937 loops=1)"
"                                ->  Nested Loop  (cost=10.47..1985.70 rows=138 width=12) (actual time=41.046..1708.789 rows=119937 loops=1)"
"                                      ->  Nested Loop  (cost=10.47..1457.52 rows=138 width=16) (actual time=41.028..1084.847 rows=119937 loops=1)"
"                                            ->  Nested Loop  (cost=10.47..929.34 rows=138 width=20) (actual time=40.982..388.839 rows=119937 loops=1)"
"                                                  ->  Index Scan using "big index" on heronames players  (cost=0.00..8.27 rows=1 width=4) (actual time=0.078..0.079 rows=1 loops=1)"
"                                                        Index Cond: ((networknumber = 2) AND (heroname = 'RubenGrimes'::text))"
"                                                  ->  Bitmap Heap Scan on stats  (cost=10.47..918.63 rows=244 width=24) (actual time=40.889..301.217 rows=119937 loops=1)"
"                                                        Recheck Cond: (heronameid = players.heronameid)"
"                                                        ->  Bitmap Index Scan on "stats index"  (cost=0.00..10.41 rows=244 width=0) (actual time=35.179..35.179 rows=119937 loops=1)"
"                                                              Index Cond: (heronameid = players.heronameid)"
"                                            ->  Index Only Scan using "preflop index" on preflop preflopstats  (cost=0.00..3.82 rows=1 width=8) (actual time=0.005..0.005 rows=1 loops=119937)"
"                                                  Index Cond: (preflopstatsid = stats.preflopstatsid)"
"                                                  Heap Fetches: 0"
"                                      ->  Index Only Scan using "flop index" on flop flopstats  (cost=0.00..3.82 rows=1 width=8) (actual time=0.004..0.004 rows=1 loops=119937)"
"                                            Index Cond: (flopstatsid = stats.flopstatsid)"
"                                            Heap Fetches: 0"
"                                ->  Index Only Scan using "turn index" on turn turnstats  (cost=0.00..3.82 rows=1 width=8) (actual time=0.004..0.004 rows=1 loops=119937)"
"                                      Index Cond: (turnstatsid = stats.turnstatsid)"
"                                      Heap Fetches: 0"
"                          ->  Index Only Scan using "river index" on river riverstats  (cost=0.00..3.82 rows=1 width=8) (actual time=0.004..0.004 rows=1 loops=119937)"
"                                Index Cond: (riverstatsid = stats.riverstatsid)"
"                                Heap Fetches: 0"
"                    ->  Index Scan using hands_pkey on hands handsdata  (cost=0.00..7.06 rows=1 width=20) (actual time=0.005..0.005 rows=1 loops=119937)"
"                          Index Cond: (id = stats.handid)"
"              ->  Index Only Scan using "tournament big index" on tournaments  (cost=0.00..3.69 rows=1 width=8) (actual time=0.005..0.006 rows=1 loops=119937)"
"                    Index Cond: ((tournamentcode IS NULL) AND (tournamentid = handsdata.tournamentid))"
"                    Heap Fetches: 0"
"        ->  Materialize  (cost=0.00..1.04 rows=3 width=4) (actual time=0.000..0.001 rows=3 loops=119755)"
"              ->  Seq Scan on poker_types  (cost=0.00..1.03 rows=3 width=4) (actual time=0.029..0.031 rows=3 loops=1)"
"Total runtime: 4726.684 ms"

我认为真正的问题是:

->  Bitmap Index Scan on "stats index"  (cost=0.00..10.41 rows=244 width=0) (actual time=35.179..35.179 rows=119937 loops=1)"
    "                                                              Index Cond: (heronameid = players.heronameid)"

【问题讨论】:

  • 请格式化您的查询。

标签: sql postgresql join


【解决方案1】:

您的计划中突出的内容包括:

  • handsdata.pockertype 的可疑类型(double 而不是 int 或 bigint)
  • heronames (networknumber, heroname) 的缺失索引
  • 如果您获得大量实际具有非空代码的行,则缺少锦标赛索引(锦标赛代码、锦标赛 ID)

跟进您的编辑:Bitmap Index Scan on "stats index" 部分很好。真正的问题是错误的统计信息,这导致嵌套循环计划有大约 120k 行而不是预期的 138 行。在每个表上运行 analyze 以查看它是否有所作为。在基数较高的统计表上,运行 set statistics(请参阅:alter table)以在执行此操作之前稍微增加采样。

除此之外:你仍然有这个非常非常丑陋的问题:

Join Filter: (handsdata.pokertype = (public.poker_types.pokertypeid)::double precision)
    Rows Removed by Join Filter: 239510"

铸造相当快......除非你必须做 240k 次。

【讨论】:

  • 感谢您的建议,我添加了 2 个缺失的索引,您会在下一个答案中看到结果
  • 您忘记修复类型问题。然后改变你的统计数据。我怀疑你可以用这个查询做更多的事情。
猜你喜欢
  • 2015-05-14
  • 1970-01-01
  • 2020-08-04
  • 1970-01-01
  • 2017-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多