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 秒。太好了,我只是不明白为什么会这样?在这种情况下,一个多列索引如何比两个单独的索引更好?
正如所建议的那样,索引(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)
归档时间: |
|
查看次数: |
2407 次 |
最近记录: |