事件背景
客户新上线了一套监控系统,可以监控到所有的执行慢的SQL,监控到有个批量任务导入大量数据后,进行索引创建。耗时需要几分钟,虽然不影响业务,但是需要整改。
这种问题的处理思路,都是大拆小,搞并发。
准备工作
在测试环境,创建一个大表进行测试,创建大量的假数据。
创建表
CREATE TABLE TABTEST (
logid bigint,
name varchar(255),
log varchar(1024),
logdate timestamp
);
随机字符串生成函数
CREATE OR REPLACE FUNCTION public.random_string(i integer)
RETURNS text
LANGUAGE sql
AS $function$
select array_to_string(array(select substring('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' FROM (ceil(random()*62))::int FOR 1) FROM generate_series(1, $1)), '');
$function$
;
生成大量的数据
INSERT INTO TABTEST SELECT GENERATE_SERIES(0, 100000000, 1), random_string(50), random_string(100), now();
经测试发现这种方法创建数据太慢了,改成使用COPY的方式创建数据。
排查发现random_string效率太低,生成一条数据接近1ms
重新创建表
CREATE SEQUENCE tabtest_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
CREATE TABLE TABTEST (
logid bigint default nextval('tabtest_seq'::regclass),
name varchar(255),
log varchar(1024),
logdate timestamp default now()
);
写程序创建8600万条数据放在test.csv中
导入大量数据
COPY TABTEST(name, log) FROM '/tmp/test.csv' WITH csv;
测试基准数据
\timing 开启计时
CREATE INDEX idx_test ON public.TABTEST USING BTREE(NAME ASC NULLS LAST);
耗时532824.434 ms
尝试一:并发创建索引
set max_parallel_maintenance_workers to 8;
set max_parallel_workers to 8;
set max_worker_processes to 8; //需要重启
set maintenance_work_mem to '16GB';
alter table public.TABTEST set(parallel_workers=8);
耗时385838.893 ms,提升 38%的性能,非常不错,但是远远不够。
仍然会出发告警。
尝试二:表分区并发创建索引
重新创建表
CREATE TABLE TABTEST (
logid bigint default nextval('tabtest_seq'::regclass),
name varchar(255),
log varchar(1024),
logdate timestamp default now()
) partition by hash(name);
创建分区
CREATE TABLE TABTEST_00 PARTITION OF TABTEST FOR VALUES WITH (MODULUS 10, REMAINDER 0);
CREATE TABLE TABTEST_01 PARTITION OF TABTEST FOR VALUES WITH (MODULUS 10, REMAINDER 1);
CREATE TABLE TABTEST_02 PARTITION OF TABTEST FOR VALUES WITH (MODULUS 10, REMAINDER 2);
CREATE TABLE TABTEST_03 PARTITION OF TABTEST FOR VALUES WITH (MODULUS 10, REMAINDER 3);
CREATE TABLE TABTEST_04 PARTITION OF TABTEST FOR VALUES WITH (MODULUS 10, REMAINDER 4);
CREATE TABLE TABTEST_05 PARTITION OF TABTEST FOR VALUES WITH (MODULUS 10, REMAINDER 5);
CREATE TABLE TABTEST_06 PARTITION OF TABTEST FOR VALUES WITH (MODULUS 10, REMAINDER 6);
CREATE TABLE TABTEST_07 PARTITION OF TABTEST FOR VALUES WITH (MODULUS 10, REMAINDER 7);
CREATE TABLE TABTEST_08 PARTITION OF TABTEST FOR VALUES WITH (MODULUS 10, REMAINDER 8);
CREATE TABLE TABTEST_09 PARTITION OF TABTEST FOR VALUES WITH (MODULUS 10, REMAINDER 9);
并发创建INDEX,并记录每个分区索引创建的开始时间和结束时间;
耗时 = 最大结束时间 - 最小开始时间 = 137 s,速度提升接近4倍。
顺序创建INDEX,并记录每个分区索引创建的开始时间和结束时间;
耗时 = 每个索引的耗时相加 = 457358.14 ms,速度提升 16.5%
顺序创建INDEX,优化并发
set max_parallel_maintenance_workers to 8;
set max_parallel_workers to 8;
set max_worker_processes to 8; //需要重启
set maintenance_work_mem to '16GB';
alter table public.TABTEST_00 set(parallel_workers=8);
alter table public.TABTEST_01 set(parallel_workers=8);
alter table public.TABTEST_02 set(parallel_workers=8);
alter table public.TABTEST_03 set(parallel_workers=8);
alter table public.TABTEST_04 set(parallel_workers=8);
alter table public.TABTEST_05 set(parallel_workers=8);
alter table public.TABTEST_06 set(parallel_workers=8);
alter table public.TABTEST_07 set(parallel_workers=8);
alter table public.TABTEST_08 set(parallel_workers=8);
alter table public.TABTEST_09 set(parallel_workers=8);
耗时 = 每个索引的耗时相加 = 292027.642 ms, 速度提升接近两倍。
在开启了并发参数的情况下,如果再叠加并发分区INDEX创建,会不会有惊喜呢?
并发创建INDEX,并记录每个分区索引创建的开始时间和结束时间;
耗时 = 最大结束时间 - 最小开始时间 = 141 s,速度还不如默认并发参数下的表现。应该是资源发生争抢导致的,通过系统监控发现CPU已经打满了。
分区并发是目前能想到的最优化手段了。
警告
还需要结合查询的情况进行分析,分区会带来一点点的性能下降是否影响也需要考虑一下。
结论
分区时目前能避开监控报警的唯一手段了,另外还钻了监控报警的空子。
客户的监控是基于单条语句的,单个分区的最大创建时间为47s,控制在分钟以内了。