Обсуждение: limit + order by is slow if no rows in result set

Поиск
Список
Период
Сортировка

limit + order by is slow if no rows in result set

От
Brian Cox
Дата:
There are 1.9M rows in ts_defects and indexes on b.ts_id (primary key)
d.ts_biz_event_id and d.ts_occur_date.  Both queries below return 0
rows.  The 1st runs fast and the 2nd > 400x slower.  The 2nd query
differs from the 1st only by the addition of "limit 1".

Why the big difference in performance?

Thanks,
Brian

[bcox@athena jsp]$ time PGPASSWORD=**** psql -U admin -d cemdb -h
192.168.1.30 -c 'select * from ts_defects d join ts_biz_events b on
b.ts_id = d.ts_biz_event_id where b.ts_status=3 order by d.ts_occur_date
desc;'
(column list deleted)

-------+--------------+--------------+---------------+---------------------------+----------------+----------------+------------+------------------------+------------------+-----------------+---------------+------------------+---------------+-----------------+-------------------+---------------------+---------------------+---------------------+--------------------+----------------+--------------------+----------------------+--------------------+----------------------+-----------------------+----------------+----------------------+---------------+-----------------+------------------+--------------+----------------+-------+--------------+---------------+---------------------------+------------------+---------+--------------------+---------------+-----------------+------------------+---------------+----------------------+---------------------+--------------------+-----------+---------------------+----------+---------------+--------------+------------------+-------------+--------
-----+--------------+--------------+----------------
(0 rows)


real    0m0.022s
user    0m0.003s
sys     0m0.003s


[bcox@athena jsp]$ time PGPASSWORD=**** psql -U admin -d cemdb -h
192.168.1.30 -c 'select * from ts_defects d join ts_biz_events b on
b.ts_id = d.ts_biz_event_id where b.ts_status=3 order by d.ts_occur_date
desc limit 1;'
(column list deleted)

-------+--------------+--------------+---------------+---------------------------+----------------+----------------+------------+------------------------+------------------+-----------------+---------------+------------------+---------------+-----------------+-------------------+---------------------+---------------------+---------------------+--------------------+----------------+--------------------+----------------------+--------------------+----------------------+-----------------------+----------------+----------------------+---------------+-----------------+------------------+--------------+----------------+-------+--------------+---------------+---------------------------+------------------+---------+--------------------+---------------+-----------------+------------------+---------------+----------------------+---------------------+--------------------+-----------+---------------------+----------+---------------+--------------+------------------+-------------+--------
-----+--------------+--------------+----------------
(0 rows)


real    0m9.410s
user    0m0.005s
sys     0m0.002s

Re: limit + order by is slow if no rows in result set

От
Heikki Linnakangas
Дата:
Brian Cox wrote:
>
> There are 1.9M rows in ts_defects and indexes on b.ts_id (primary key)
> d.ts_biz_event_id and d.ts_occur_date.  Both queries below return 0
> rows.  The 1st runs fast and the 2nd > 400x slower.  The 2nd query
> differs from the 1st only by the addition of "limit 1".
>
> Why the big difference in performance?

Please run EXPLAIN ANALYZE on both queries, and send back the results.
Also, what indexes are there on the tables involved?

--
   Heikki Linnakangas
   EnterpriseDB   http://www.enterprisedb.com

Re: limit + order by is slow if no rows in result set

От
Brian Cox
Дата:
Hi Heikki,

Thanks for your response.

> Please run EXPLAIN ANALYZE on both queries, and send back the results.

[bcox@athena jsp]$ PGPASSWORD=quality psql -U admin -d cemdb -h
192.168.1.30 -c 'explain analyze select * from ts_defects d join
ts_biz_events b on b.ts_id = d.ts_biz_event_id where b.ts_status=3 order
by d.ts_occur_date desc;'

QUERY PLAN

------------------------------------------------------------------------------------------------------------------------------------------------------
  Sort  (cost=160400.01..160646.91 rows=98762 width=2715) (actual
time=0.303..0.303 rows=0 loops=1)
    Sort Key: d.ts_occur_date
    ->  Hash Join  (cost=33.20..82567.14 rows=98762 width=2715) (actual
time=0.218..0.218 rows=0 loops=1)
          Hash Cond: ("outer".ts_biz_event_id = "inner".ts_id)
          ->  Seq Scan on ts_defects d  (cost=0.00..71882.88
rows=1932688 width=1545) (actual time=0.022..0.022 rows=1 loops=1)
          ->  Hash  (cost=33.04..33.04 rows=65 width=1170) (actual
time=0.135..0.135 rows=0 loops=1)
                ->  Bitmap Heap Scan on ts_biz_events b
(cost=2.23..33.04 rows=65 width=1170) (actual time=0.132..0.132 rows=0
loops=1)
                      Recheck Cond: (ts_status = 3)
                      ->  Bitmap Index Scan on ts_biz_events_statusindex
  (cost=0.00..2.23 rows=65 width=0) (actual time=0.054..0.054 rows=61
loops=1)
                            Index Cond: (ts_status = 3)
  Total runtime: 0.586 ms
(11 rows)

[bcox@athena jsp]$ PGPASSWORD=quality psql -U admin -d cemdb -h
192.168.1.30 -c 'explain analyze select * from ts_defects d join
ts_biz_events b on b.ts_id = d.ts_biz_event_id where b.ts_status=3 order
by d.ts_occur_date desc limit 1;'
    QUERY PLAN

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  Limit  (cost=0.00..87.37 rows=1 width=2715) (actual
time=17999.482..17999.482 rows=0 loops=1)
    ->  Nested Loop  (cost=0.00..8628543.77 rows=98762 width=2715)
(actual time=17999.476..17999.476 rows=0 loops=1)
          ->  Index Scan Backward using ts_defects_dateindex on
ts_defects d  (cost=0.00..227675.97 rows=1932688 width=1545) (actual
time=0.047..3814.923 rows=1932303 loops=1)
          ->  Index Scan using ts_biz_events_pkey on ts_biz_events b
(cost=0.00..4.33 rows=1 width=1170) (actual time=0.005..0.005 rows=0
loops=1932303)
                Index Cond: (b.ts_id = "outer".ts_biz_event_id)
                Filter: (ts_status = 3)
  Total runtime: 17999.751 ms
(7 rows)

> Also, what indexes are there on the tables involved?

I tried to mention the relevant indexes in my original posting, but
omitted one; here's a list of all indexes:

ts_defects: ts_id, ts_occur_date, ts_defect_def_id, ts_biz_event_id,
ts_trancomp_id, ts_transet_incarnation_id, ts_transet_id,
ts_tranunit_id, ts_user_incarnation_id, ts_user_id

ts_biz_events: ts_id, ts_defect_def_id, ts_status

Thanks,
Brian

Re: limit + order by is slow if no rows in result set

От
Tom Lane
Дата:
Brian Cox <brian.cox@ca.com> writes:
>> Please run EXPLAIN ANALYZE on both queries, and send back the results.
> [ results... ]

The reason the hash plan is fairly fast is that the hash join code has a
special hack: if it reads the inner relation and finds it contains no
rows, it knows there can be no join result rows, so it can fall out
without reading the outer relation at all.  This saves it from scanning
the large ts_defects table.  (If you look close you'll see that it
actually reads just the first row from ts_defects; this is because the
inner relation isn't read until after we know the outer is nonempty,
so as to try to win for the other case of empty outer and nonempty
inner.)

The reason the nestloop/limit plan is not fast is that it has to scan
the inner relation (ts_biz_events) for each row of ts_defects, and there
are lots of them.  Even though each inner scan is just a fast index
probe, it adds up.

The reason the planner goes for the nestloop/limit plan is that it's
expecting that about 5% (98762/1932688) of the ts_defects rows will
have a match in ts_biz_events, and so it figures it'll only have to
probe ts_biz_events about 20 times before producing an output row,
and the Limit only wants one row.  So this looks a lot cheaper than
the hash plan --- especially since the latter is being costed without
any assumption that the zero-inner-rows situation applies.

The bottom line is that the plans are being chosen on "typical" rather
than corner-case assumptions, and zero matching rows is a corner case
that happens to work real well for the hash plan and not well at all for
the nestloop plan.  I'm not sure what we can do about that without
making the performance worse for the case of not-quite-zero matching
rows.

You might be able to get a better result if you increased the statistics
target for ts_status --- it looks like the planner thinks there are many
more ts_status = 3 rows than there really are.

            regards, tom lane