bur*_*nsy 8 postgresql performance index ddl performance-tuning postgresql-performance
我正在尝试为 Postgres 9.4 中的大型(1.2TB)静态表创建部分索引。
我的数据是完全静态的,所以我可以插入所有数据,然后创建所有索引。
在这个 1.2TB 的表中,我有一个名为的列run_id,可以清晰地划分数据。通过创建涵盖一系列run_ids 的索引,我们获得了出色的性能。下面是一个例子:
CREATE INDEX perception_run_frame_idx_run_266_thru_270
ON run.perception
(run_id, frame)
WHERE run_id >= 266 AND run_id <= 270;
Run Code Online (Sandbox Code Playgroud)
这些部分索引为我们提供了所需的查询速度。不幸的是,每个部分索引的创建大约需要 70 分钟。
看起来我们的 CPU 有限(top进程显示为 100%)。
我可以做些什么来加快部分索引的创建?
系统规格:
表规格:
表定义:
CREATE TABLE run.perception(
id bigint NOT NULL,
run_id bigint NOT NULL,
frame bigint NOT NULL,
by character varying(45) NOT NULL,
by_anyone bigint NOT NULL,
by_me bigint NOT NULL,
by_s_id integer,
owning_p_id bigint NOT NULL,
obj_type_set bigint,
seq integer,
subj_id bigint NOT NULL,
subj_state_frame bigint NOT NULL,
CONSTRAINT perception_pkey PRIMARY KEY (id))
Run Code Online (Sandbox Code Playgroud)
(不要过多地阅读列名——我已经对它们进行了一些混淆。)
背景资料:
自 Postgres 9.5起可用,可能正是您正在寻找的。更快的索引创建,更小的索引。但是查询通常没有那么快。手册:
BRIN 代表块范围索引。BRIN 设计用于处理非常大的表,其中某些列与它们在表中的物理位置有一些自然的相关性。阿块范围是一组属于在表中物理相邻的页; 对于每个块范围,索引存储了一些摘要信息。
继续阅读,还有更多。
Depesz 进行了初步测试。
对于你的情况,最佳:如果你能写行群集上run_id,索引变得非常小,创作便宜得多。
CREATE INDEX foo ON run.perception USING brin (run_id, frame)
WHERE run_id >= 266 AND run_id <= 270;
Run Code Online (Sandbox Code Playgroud)
您甚至可能只是索引整个表。
无论您做什么,您都可以通过对列进行排序来节省由于每行对齐要求而丢失的 8 个字节:
CREATE TABLE run.perception(
id bigint NOT NULL PRIMARY KEY
, run_id bigint NOT NULL
, frame bigint NOT NULL
, by_anyone bigint NOT NULL
, by_me bigint NOT NULL
, owning_p_id bigint NOT NULL
, subj_id bigint NOT NULL
, subj_state_frame bigint NOT NULL
, obj_type_set bigint
, by_s_id integer
, seq integer
, by varchar(45) NOT NULL -- or just use type text
);
Run Code Online (Sandbox Code Playgroud)
如果没有任何列具有 NULL 值,则使您的表小 79 GB。细节:
此外,您只有三列可以为 NULL。对于 9 - 72 列,NULL 位图占用 8 个字节。如果只有一个 整数列是 NULL,则存在存储悖论的一个极端情况:使用虚拟值会更便宜:浪费了 4 个字节,但由于不需要该行的 NULL 位图而节省了 8 个字节。更多细节在这里:
根据您的实际查询,使用这五个部分索引而不是上面的索引可能更有效:
CREATE INDEX perception_run_id266_idx ON run.perception(frame) WHERE run_id = 266;
CREATE INDEX perception_run_id266_idx ON run.perception(frame) WHERE run_id = 267;
CREATE INDEX perception_run_id266_idx ON run.perception(frame) WHERE run_id = 268;
CREATE INDEX perception_run_id266_idx ON run.perception(frame) WHERE run_id = 269;
CREATE INDEX perception_run_id266_idx ON run.perception(frame) WHERE run_id = 270;
Run Code Online (Sandbox Code Playgroud)
为每个运行一个事务。
run_id以这种方式删除作为索引列可以为每个索引条目节省 8 个字节 - 32 个而不是每行 40 个字节。每个索引的创建成本也更低,但是对于一个太大而无法保留在缓存中的表,创建五个而不是一个所花费的时间要长得多(如@Jürgen 和@Chris 评论)。所以这可能对你有用,也可能没用。
基于继承- 直到 Postgres 9.5 的唯一选择。
(Postgres 11 或最好是 12 中的新声明式分区更智能。)
在约束排除期间检查对父表的所有子表的所有约束,因此大量分区可能会显着增加查询计划时间。因此,基于遗留继承的分区可以很好地处理多达一百个分区;不要尝试使用成千上万个分区。
大胆强调我的。因此,为 估计 1000 个不同的值run_id,您将创建跨越大约 10 个值的分区。
maintenance_work_mem我错过了你maintenance_work_mem在我的第一次阅读中已经在适应的情况。我会在我的回答中留下报价和建议以供参考。根据文档:
maintenance_work_mem(整数)指定要通过维护操作中使用的存储器的最大量,例如
VACUUM,CREATE INDEX,和ALTER TABLE ADD FOREIGN KEY。它默认为 64 兆字节 (64MB)。由于数据库会话一次只能执行这些操作中的一个,并且安装通常不会同时运行许多操作,因此将此值设置为明显大于work_mem. 较大的设置可能会提高清理和恢复数据库转储的性能。请注意,在
autovacuum运行时,最多autovacuum_max_workers可能会分配此内存,因此请注意不要将默认值设置得太高。单独进行控制可能会很有用setting autovacuum_work_mem。
我只会根据需要将它设置为高 - 这取决于(对我们而言)未知的索引大小。并且仅在本地用于执行会话。正如引用所解释的那样,否则过高的常规设置可能会使服务器饿死,因为 autovacuum 也可能会占用更多 RAM。此外,不要将其设置得比需要的高太多,即使在执行会话中,空闲 RAM 也可能会很好地用于缓存数据。
它可能看起来像这样:
BEGIN;
SET LOCAL maintenance_work_mem = 10GB; -- depends on resulting index size
CREATE INDEX perception_run_frame_idx_run_266_thru_270 ON run.perception(run_id, frame)
WHERE run_id >= 266 AND run_id <= 270;
COMMIT;Run Code Online (Sandbox Code Playgroud)
关于SET LOCAL:
SET LOCALlast的效果只持续到当前事务结束,无论是否提交。
测量物体尺寸:
显然,服务器通常应该合理配置。
| 归档时间: |
|
| 查看次数: |
3546 次 |
| 最近记录: |