web-dev-qa-db-ja.com

ORDER BY ASCはORDER BY DESCより300倍高速

昇順のソートは、Bツリーインデックスを持つ列での降順のソートより300倍高速です。約2,000万行のテーブルがあります。
inserted_atおよびx列。

昇順ソート

select 
  * from table_a a where
  a.inserted_at >= '2018-09-01 00:00:00.0' and a.x in ('some_x_val')
  order by a.inserted_at asc
  offset 0 limit 20;

Limit  (cost=0.44..2027.69 rows=20 width=51) (actual time=418.529..464.558 rows=20 loops=1)
  ->  Index Scan using table_a_inserted_at_index on table_a a  (cost=0.44..5406390.18 rows=53337 width=51) (actual time=418.527..464.543 rows=20 loops=1)
        Index Cond: (inserted_at >= '2018-09-01 00:00:00'::timestamp without time zone)
        Filter: (event_type_id = ''some_x_val'::uuid)
        Rows Removed by Filter: 4092
Planning time: 0.177 ms
Execution time: 464.596 ms

explain (analyze, verbose, buffers) select * from table_a a where a.inserted_at >= '2018-09-01 00:00:00.0' and a.x in ('some_x_val') order by a.inserted_at asc offset 0 limit 20;
                                                                          QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit  (cost=0.44..2027.77 rows=20 width=51) (actual time=527.032..575.516 rows=20 loops=1)
  Output: x, y, z
  Buffers: shared hit=2492 read=905
  ->  Index Scan using table_a_inserted_at_index on public.table_a a  (cost=0.44..5406596.96 rows=53337 width=51) (actual time=527.029..575.496 rows=20 loops=1)
        Output: x, y, z
        Index Cond: (a.inserted_at >= '2018-09-01 00:00:00'::timestamp without time zone)
        Filter: (a.x = 'some_x_val'::uuid)
        Rows Removed by Filter: 4092
        Buffers: shared hit=2492 read=905
Planning time: 0.162 ms
Execution time: 575.550 ms

降順ソート

select 
  * from table_a a where
  a.inserted_at >= '2018-09-01 00:00:00.0' and a.x in ('some_x_val')
  order by a.inserted_at desc
  offset 0 limit 20;

Limit  (cost=0.44..2027.69 rows=20 width=51) (actual time=148377.757..148385.703 rows=20 loops=1)
  ->  Index Scan Backward using table_a_inserted_at_index on table_a a  (cost=0.44..5406383.67 rows=53337 width=51) (actual time=148377.756..148385.691 rows=20 loops=1)
        Index Cond: (inserted_at >= '2018-09-01 00:00:00'::timestamp without time zone)
        Filter: (x = 'some_x_val'::uuid)
        Rows Removed by Filter: 2737952
Planning time: 0.663 ms
Execution time: 148385.756 ms

explain (analyze, verbose, buffers) select * from table_a a where a.inserted_at >= '2018-09-01 00:00:00.0' and a.x in ('some_x_val') order by a.inserted_at desc offset 0 limit 20;
                                                                                QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit  (cost=0.44..2027.77 rows=20 width=51) (actual time=168306.377..168312.296 rows=20 loops=1)
  Output: x, y, z
  Buffers: shared hit=2439002 read=359523 dirtied=288
  ->  Index Scan Backward using table_a_inserted_at_index on public.table_a a  (cost=0.44..5406596.96 rows=53337 width=51) (actual time=168306.375..168312.282 rows=20 loops=1)
        Output: x, y, z
        Index Cond: (a.inserted_at >= '2018-09-01 00:00:00'::timestamp without time zone)
        Filter: (a.x = 'some_x_val'::uuid)
        Rows Removed by Filter: 2745418
        Buffers: shared hit=2439002 read=359523 dirtied=288
Planning time: 0.139 ms
Execution time: 168312.326 ms

何が問題でしょうか?

2
HBalyan

問題は2つ(または3つ)だと思います。

  • フィルタリングされた両方の列に複合インデックスがありません:(x, inserted_at)
  • 偏在
  • 古い統計
   Filter: (a.x = 'some_x_val'::uuid)
   Rows Removed by Filter: 2745418

異常な分布は、単一のインデックススキャンが2番目のフィルター(他の(x)列による)に一致しない行を削除するために多くの作業を行う必要があることを意味します。 ORDER BY ASCのインデックススキャンは幸運であり、運が良ければ、20行が少し早く見つかります。 inserted_atおよびxのパラメーターの他の値と逆になる可能性があります

したがって、これを解決する最良の方法は、この複合インデックスを追加することです。リンクされた質問のアドバイスに従い、Erwinから回答を得て、テーブルの統計を更新できますが、これで問題が解決する場合とそうでない場合があります。確かではありません。

3
ypercubeᵀᴹ