有没有办法加快 DISTINCT 查询的速度?

lug*_*er1 2 postgresql performance distinct postgresql-10 query-performance

我在数据库中有一个表 t (PostgreSQL 10.4):

\d t;
                Table "public.t"
  Column  |          Type          | Collation | Nullable | Default 
----------+------------------------+-----------+----------+---------
 sn       | character varying(11)  |           |          | 
 site     | character varying(50)  |           |          | 
Indexes:
    "site_2018_idx" btree (site), tablespace "indexspace"
    "sn_2018_idx" btree (sn), tablespace "indexspace"
Run Code Online (Sandbox Code Playgroud)

我需要为特定站点找到不同的 'sn,我这样做:

SELECT DISTINCT sn FROM t WHERE site='a_b301_1' ORDER BY sn ;
Run Code Online (Sandbox Code Playgroud)

它可以工作,但速度很慢,返回 75 个不同的“sn”值大约需要 8 分钟!有没有办法加快速度?解释分析给出了这个输出:

QUERY PLAN                                                                                 
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=42873094.21..42873103.25 rows=3615 width=12) (actual time=190431.413..190431.417 rows=75 loops=1)
   Output: sn
   Sort Key: t.sn
   Sort Method: quicksort  Memory: 28kB
   ->  HashAggregate  (cost=42872844.42..42872880.57 rows=3615 width=12) (actual time=190431.233..190431.263 rows=75 loops=1)
         Output: sn
         Group Key: t.sn
         ->  Bitmap Heap Scan on public.t  (cost=874850.36..42695793.24 rows=70820471 width=12) (actual time=8755.163..168773.143 rows=43096912 loops=1)
               Output: sn, site
               Recheck Cond: ((t.site)::text = 'a_b301_1'::text)
               Heap Blocks: exact=783666
               ->  Bitmap Index Scan on site_2018_idx  (cost=0.00..857145.24 rows=70820471 width=0) (actual time=8540.835..8540.835 rows=43096912 loops=1)
                     Index Cond: ((t.site)::text = 'a_b301_1'::text)
 Planning time: 0.466 ms
 Execution time: 190433.289 ms
(15 rows)
Run Code Online (Sandbox Code Playgroud)

附加信息

在我按照建议创建另一个索引后,(site, sn)时间显着减少,从 8 分钟减少到 30 秒。太好了,我只是不明白为什么会这样?在这种情况下,一个多列索引如何比两个单独的索引更好?

jja*_*nes 5

正如所建议的那样,索引(site, sn)可以加快速度,特别是如果表被很好地清空,以便它从仅索引扫描中受益。

如果这还不足以加快速度,那么有一种方法可以在“跳过扫描”或“松散索引扫描”中使用此索引来加速不同值数量远低于索引大小的查询. 不幸的是,PostgreSQL 的规划器不会自动检测到这种情况,但您可以通过编写递归公用表表达式来自行强制检测。您可以在PostgreSQL wiki上找到有关此技术的讨论,但您需要将其调整为您的参数化版本。

结果查询非常难看,因此您可能希望将它们包装到视图中。或者在这种情况下,因为它是参数化的,所以变成了一个返回集合的函数。像这样的东西对我有用:

CREATE FUNCTION public.foobar(text) RETURNS SETOF text
    LANGUAGE sql
    AS $_$ with recursive r as (
  (select sn from t where site = $1 order by sn limit 1)
   union all
  SELECT (SELECT t.sn FROM t WHERE site=$1 and t.sn > r.sn ORDER BY sn LIMIT 1) from r where sn is not null
)
select * from r where r.sn is not null $_$;
Run Code Online (Sandbox Code Playgroud)

设置表和索引:

create table t as select 
    floor(random()*1.2)::int::varchar as site, 
    (-floor(50*log(random()))::int)::varchar as sn 
from generate_series(1,10000000);

create index on t (site ,sn);
Run Code Online (Sandbox Code Playgroud)