问这个问题,特别是针对 Postgres,因为它对 R 树/空间索引有很好的支持。
我们有下表,其中包含单词及其频率的树结构(嵌套集模型):
lexikon
-------
_id integer PRIMARY KEY
word text
frequency integer
lset integer UNIQUE KEY
rset integer UNIQUE KEY
Run Code Online (Sandbox Code Playgroud)
和查询:
SELECT word
FROM lexikon
WHERE lset BETWEEN @Low AND @High
ORDER BY frequency DESC
LIMIT @N
Run Code Online (Sandbox Code Playgroud)
我认为覆盖索引(lset, frequency, word)会很有用,但我觉得如果范围内的lset值太多,它可能表现不佳(@High, @Low)。
(frequency DESC)有时,当使用该索引的搜索早期产生@N与范围条件匹配的行时,一个简单的索引也可能就足够了。
但似乎性能在很大程度上取决于参数值。
有没有办法让它快速执行,不管范围(@Low, @High)是宽还是窄,也不管高频词是否幸运地在(窄)选择的范围内?
R-tree/空间索引有帮助吗?
添加索引,重写查询,重新设计表,没有限制。
postgresql performance index database-design query-performance
我刚刚查看了一些为8.4 之前的 PostgreSQL编写的旧代码,我看到了一些非常棒的东西。我记得以前有一个自定义函数可以做一些这样的事情,但我忘记了预先的array_agg()样子。回顾一下,现代聚合是这样写的。
SELECT array_agg(x ORDER BY x DESC) FROM foobar;
Run Code Online (Sandbox Code Playgroud)
然而,曾几何时,它是这样写的,
SELECT ARRAY(SELECT x FROM foobar ORDER BY x DESC);
Run Code Online (Sandbox Code Playgroud)
所以,我用一些测试数据试了一下..
CREATE TEMP TABLE foobar AS
SELECT * FROM generate_series(1,1e7)
AS t(x);
Run Code Online (Sandbox Code Playgroud)
结果令人惊讶..#OldSchoolCool 方式要快得多:加速了 25%。此外,在没有ORDER 的情况下简化它,表现出同样的缓慢。
# EXPLAIN ANALYZE SELECT ARRAY(SELECT x FROM foobar);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Result (cost=104425.28..104425.29 rows=1 width=0) (actual time=1665.948..1665.949 rows=1 loops=1)
InitPlan 1 (returns $0)
-> Seq Scan on foobar (cost=0.00..104425.28 rows=6017728 width=32) (actual time=0.032..716.793 rows=10000000 loops=1) …Run Code Online (Sandbox Code Playgroud) 在我的应用程序服务器中,我想使用LIMIT和对数据集进行分页OFFSET,并另外将数据集的总数返回给用户。
而不是对数据库进行两次远程调用:
select count(1) as total_count from foo;
select c1 from foo;
Run Code Online (Sandbox Code Playgroud)
我认为在单个数据库调用中完成此操作会更明智:
select c1, count(1) over (partition by null) from foo;
Run Code Online (Sandbox Code Playgroud)
但是,与不使用窗口函数相比,添加此窗口函数会导致执行时间长一个数量级。
我觉得这很令人惊讶,因为类似的时间select count(1) from foo只需要两倍的时间select c1 from foo。然而,将其转换为窗口函数会导致性能下降。
此外,使用以下使用子查询的替代方案非常快:
select c1, (select count(1) from foo) as total_count from foo;
Run Code Online (Sandbox Code Playgroud)
我本来期望 postgresql 能够优化partition by null
我在 Oracle 中尝试过这一点,发现了类似的性能损失。
如何解释为什么这里会出现性能损失?对于核心 postgresql 开发人员来说,进行更改以优化这一点是否相对容易,甚至值得,例如通过将 PARTITION BY NULL 的窗口函数转换为子查询?
设置:
drop table foo;
create table foo (c1 int);
insert into foo
select i from …Run Code Online (Sandbox Code Playgroud) postgresql count window-functions postgresql-performance postgresql-13
我们有一个包含约 50 亿行的 PostgreSQL 表,它养成了一个讨厌的习惯,即缺少正确的索引并对某些LIMIT操作进行主键扫描。
问题通常出现在一个ORDER BY .. LIMIT ..子句(Django 分页中的常见模式)上,其中LIMIT是索引匹配的结果的一些相对较小的子集。一个极端的例子是这样的:
SELECT * FROM mcqueen_base_imagemeta2
WHERE image_id IN ( 123, ... )
ORDER BY id DESC
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
其中该IN子句中的项目约为 20,索引匹配的总行数image_id为 16。
在EXPLAIN表明,它错过了image_id指数,而是确实5B行的PK扫描:
限制(成本=0.58..4632.03 行=1 宽度=28)
-> 在 mcqueen_base_imagemeta2 上使用 mcqueen_base_imagemeta2_pkey 向后扫描索引(成本=0.58..364597074.75 行=78722 宽度=28)
过滤器:(image_id = ANY ('{123, ...}'::bigint[]))
如果LIMIT增加到2,它会按预期工作:
限制(成本=7585.92..7585.93 行=2 宽度=28)
-> 排序(成本=7585.92..7782.73 行=78722 宽度=28)
排序键:id DESC
-> 在 mcqueen_base_imagemeta2 上使用 … postgresql performance index-tuning paging postgresql-9.6 query-performance
postgresql ×4
performance ×2
aggregate ×1
array ×1
benchmark ×1
count ×1
index ×1
index-tuning ×1
paging ×1