大表上的CLUSTER
命令可能需要很长时间,并且在运行时会阻塞对表的读取和写入。
我不需要我的表中的数据严格按照索引顺序排序,我只希望经常一起查询的行更有可能位于同一个数据库块中,而不是均匀地分散在表中(这是它们的分布由于日期插入表的方式的性质,自然有)。
它可以产生很大的不同。在下面的示例中,唯一的区别是insert
有一个额外的order by mod(g,10)
,以便测试数据由 预先聚类host_id
。当获取一个特定的所有数据时,需要读取的块要少得多host_id
。
有没有一些方法可以在没有独占锁和命令的日志记录开销的情况下实现这种集群cluster
?
create schema stack;
set search_path=stack;
--
create table foo(host_id integer, bar text default repeat('a',400));
insert into foo(host_id) select mod(g,10) from generate_series(1,500000) g;
create index nu_foo on foo(host_id);
explain analyze select count(bar) from foo where host_id=1;
/*
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=30188.66..30188.67 rows=1 width=404) (actual time=1129.858..1129.859 rows=1 loops=1)
-> Bitmap Heap Scan on foo (cost=919.27..30066.46 rows=48883 width=404) (actual time=253.149..1110.013 rows=50000 loops=1)
Recheck Cond: (host_id = 1)
Rows Removed by Index Recheck: 320257
-> Bitmap Index Scan on nu_foo (cost=0.00..907.04 rows=48883 width=0) (actual time=251.863..251.863 rows=50000 loops=1)
Index Cond: (host_id = 1)
Total runtime: 1129.893 ms
*/
--
drop table foo;
--
create table foo(host_id integer, bar text default repeat('a',400));
insert into foo(host_id) select mod(g,10) from generate_series(1,500000) g order by mod(g,10);
create index nu_foo on foo(host_id);
explain analyze select count(bar) from foo where host_id=1;
/*
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=7550.20..7550.21 rows=1 width=32) (actual time=24.397..24.397 rows=1 loops=1)
-> Bitmap Heap Scan on foo (cost=47.80..7543.95 rows=2500 width=32) (actual time=3.988..16.189 rows=50000 loops=1)
Recheck Cond: (host_id = 1)
-> Bitmap Index Scan on nu_foo (cost=0.00..47.17 rows=2500 width=0) (actual time=3.649..3.649 rows=50000 loops=1)
Index Cond: (host_id = 1)
Total runtime: 24.437 ms
*/
--
drop schema stack cascade;
您可以在不使用
cluster
命令并锁定表或为整个表生成 WAL 的情况下执行此操作。代价是你需要定期全表扫描。基本思想是:
测试模式样本数据最初是“部分集群”的:
初始聚类统计:
初始分析(2146.503 毫秒):
删除并重新插入未聚集的行:
新的聚类统计:
新分析(48.804 毫秒):
清理:
以上现在可行,但有点古怪(需要关闭表的自动真空)并且需要定期全面扫描表。我认为没有缺点的类似东西可以内置到 postgres 中。你需要: