PostgreSQL 分区表——范围分区SQL实践
PostgreSQL 分区表——范围分区SQL实践
- 1、环境准备
- 1-1、新增原始表
- 1-2、执行脚本新增2400w行
- 1-3、创建pg分区表-分区键为创建时间
- 1-4、创建24年所有分区
- 1-5、设置默认分区(兜底用)
- 1-6、迁移数据
- 1-7、创建分区表索引
- 2、SQL增删改查测试
- 2-1、查询速度对比
- 2-1-1、查询总数量时间对比
- 查询总数量反直觉分析
- 2-2-1、带上分区键查询对比
- 带上分区键在分表区间内查询
- 带上分区键跨分表区间查询
- 2-2、删除速度对比
- 2-2-1、全量删除TRUNCATE
- 2-2-2、删除某个月数据
- 原始表DELETE 145.372s
- 分区表DELETE(未指定分区)225.896s
- 分区表DELETE(指定分区)242.770s
- 分区表TRUNCATE(指定分区)13.156s
- 2-2-3、删除一条数据
- 原始表 平均8.009s
- 分区表(未带上分区键) 平均7.858s
- 分区表(带上分区键) 平均1.567s
- 2-3、写入速度对比
- 2-2-1、全量插入2400w数据,均匀分布在每个月
- 原始表 3387.520s
- 分区表 4988.111s
- 2-2-2、指定某个月插入10w数据
- 原始表 63.572s
- 分区表(未指定分区) 58.759s
- 分区表(指定分区) 48.098s
- 2-4、更新速度对比
- 2-4-1、修改指定月份的处理人信息
- 原始表 538.133s
- 分区表(未指定分区) 1252.286s
- 分区表(指定分区) 1252.286s
- 2-4-1、根据主键修处理人信息
- 原始表 12.597s
- 分区表(未带上分区键) 10.870s
- 分区表(带上分区键) 0.438s
- 3、总结
1、环境准备
1-1、新增原始表
CREATE TABLE t_common_work_order_log (work_order_log_id varchar(32) COLLATE pg_catalog.default NOT NULL,operation_type int2,work_order_id varchar(32) COLLATE pg_catalog.default,work_order_name varchar(100) COLLATE pg_catalog.default,work_order_type int4,biz_location_id int4,planned_completion_time timestamp(6),actual_completion_time timestamp(6),handle_user_account varchar(100) COLLATE pg_catalog.default,handle_user_name varchar(100) COLLATE pg_catalog.default,create_time timestamp(0),create_by_uuid varchar(32) COLLATE pg_catalog.default,create_by_account varchar(32) COLLATE pg_catalog.default,create_by_name varchar(100) COLLATE pg_catalog.default,last_update_time timestamp(0),last_update_uuid varchar(32) COLLATE pg_catalog.default,last_update_account varchar(32) COLLATE pg_catalog.default,last_update_name varchar(100) COLLATE pg_catalog.default,biz_attribute_1 varchar(255) COLLATE pg_catalog.default,biz_attribute_2 varchar(255) COLLATE pg_catalog.default,biz_attribute_3 varchar(255) COLLATE pg_catalog.default,biz_attribute_4 varchar(255) COLLATE pg_catalog.default,biz_attribute_5 varchar(255) COLLATE pg_catalog.default,biz_attribute_6 varchar(255) COLLATE pg_catalog.default,biz_attribute_7 varchar(255) COLLATE pg_catalog.default,biz_attribute_8 varchar(255) COLLATE pg_catalog.default,biz_attribute_9 varchar(255) COLLATE pg_catalog.default,biz_attribute_10 varchar(255) COLLATE pg_catalog.default,biz_attribute_11 varchar(255) COLLATE pg_catalog.default,biz_attribute_12 varchar(255) COLLATE pg_catalog.default,biz_attribute_13 varchar(255) COLLATE pg_catalog.default,biz_attribute_14 varchar(255) COLLATE pg_catalog.default,biz_attribute_15 varchar(255) COLLATE pg_catalog.default,biz_attribute_16 varchar(255) COLLATE pg_catalog.default,biz_attribute_17 varchar(255) COLLATE pg_catalog.default,biz_attribute_18 varchar(255) COLLATE pg_catalog.default,biz_attribute_19 varchar(255) COLLATE pg_catalog.default,biz_attribute_20 varchar(255) COLLATE pg_catalog.default
)
;ALTER TABLE t_common_work_order_log OWNER TO postgres;CREATE INDEX order_log_biz_location_id_index ON t_common_work_order_log USING btree (biz_location_id pg_catalog.int4_ops ASC NULLS LAST
);CREATE INDEX order_log_create_by_account_index ON t_common_work_order_log USING btree (create_by_account COLLATE pg_catalog.default pg_catalog.text_ops ASC NULLS LAST
);CREATE INDEX order_log_create_time_index ON t_common_work_order_log USING btree (create_time pg_catalog.timestamp_ops ASC NULLS LAST
);CREATE INDEX order_log_handle_user_account_index ON t_common_work_order_log USING btree (handle_user_account COLLATE pg_catalog.default pg_catalog.text_ops ASC NULLS LAST
);CREATE INDEX order_log_last_update_account_index ON t_common_work_order_log USING btree (last_update_account COLLATE pg_catalog.default pg_catalog.text_ops ASC NULLS LAST
);CREATE INDEX order_log_last_update_time_index ON t_common_work_order_log USING btree (last_update_time pg_catalog.timestamp_ops ASC NULLS LAST
);CREATE INDEX order_log_operation_type_index ON t_common_work_order_log USING btree (operation_type pg_catalog.int2_ops ASC NULLS LAST
);CREATE INDEX order_log_work_order_name_index ON t_common_work_order_log USING btree (work_order_name COLLATE pg_catalog.default pg_catalog.text_ops ASC NULLS LAST
);CREATE INDEX order_log_work_order_type_index ON t_common_work_order_log USING btree (work_order_type pg_catalog.int4_ops ASC NULLS LAST
);COMMENT ON COLUMN t_common_work_order_log.work_order_log_id IS '工单日志ID';COMMENT ON COLUMN t_common_work_order_log.operation_type IS '操作类型';COMMENT ON COLUMN t_common_work_order_log.work_order_id IS '工单ID';COMMENT ON COLUMN t_common_work_order_log.work_order_name IS '工单名称';COMMENT ON COLUMN t_common_work_order_log.work_order_type IS '工单类型';COMMENT ON COLUMN t_common_work_order_log.biz_location_id IS '业务位置ID';COMMENT ON COLUMN t_common_work_order_log.planned_completion_time IS '计划完成时间';COMMENT ON COLUMN t_common_work_order_log.actual_completion_time IS '实际完成时间';COMMENT ON COLUMN t_common_work_order_log.handle_user_account IS '处理人账号';COMMENT ON COLUMN t_common_work_order_log.handle_user_name IS '处理人名称';COMMENT ON COLUMN t_common_work_order_log.create_time IS '创建时间';COMMENT ON COLUMN t_common_work_order_log.create_by_uuid IS '创建人uuid';COMMENT ON COLUMN t_common_work_order_log.create_by_account IS '创建人账号';COMMENT ON COLUMN t_common_work_order_log.create_by_name IS '创建人名称';COMMENT ON COLUMN t_common_work_order_log.last_update_time IS '最后更新时间';COMMENT ON COLUMN t_common_work_order_log.last_update_uuid IS '最后跟新人uuid';COMMENT ON COLUMN t_common_work_order_log.last_update_account IS '最后更新人账号';COMMENT ON COLUMN t_common_work_order_log.last_update_name IS '最后更新人名称';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_1 IS '业务属性1';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_2 IS '业务属性2';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_3 IS '业务属性3';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_4 IS '业务属性4';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_5 IS '业务属性5';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_6 IS '业务属性6';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_7 IS '业务属性7';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_8 IS '业务属性8';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_9 IS '业务属性9';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_10 IS '业务属性10';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_11 IS '业务属性11';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_12 IS '业务属性12';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_13 IS '业务属性13';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_14 IS '业务属性14';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_15 IS '业务属性15';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_16 IS '业务属性16';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_17 IS '业务属性17';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_18 IS '业务属性18';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_19 IS '业务属性19';COMMENT ON COLUMN t_common_work_order_log.biz_attribute_20 IS '业务属性20';COMMENT ON TABLE t_common_work_order_log IS '某工单操作日志表';COMMENT ON INDEX order_log_biz_location_id_index IS '位置索引';COMMENT ON INDEX order_log_create_by_account_index IS '创建人索引';COMMENT ON INDEX order_log_create_time_index IS '创建时间索引';COMMENT ON INDEX order_log_handle_user_account_index IS '处理人索引';COMMENT ON INDEX order_log_last_update_account_index IS '最后更新人索引';COMMENT ON INDEX order_log_last_update_time_index IS '最后更新时间索引';COMMENT ON INDEX order_log_operation_type_index IS '操作类型索引';COMMENT ON INDEX order_log_work_order_name_index IS '工单名称索引';COMMENT ON INDEX order_log_work_order_type_index IS '工单类型索引';
1-2、执行脚本新增2400w行
-- 1. 首先创建随机中文名函数
CREATE OR REPLACE FUNCTION random_chinese_name() RETURNS varchar(100) AS $$
DECLAREsurnames varchar[] := ARRAY['张','王','李','赵','刘','陈','杨','黄','吴','周','徐','孙','马','朱','胡','林','郭','何','高','罗'];givennames varchar[] := ARRAY['伟','芳','娜','秀英','敏','静','丽','强','磊','军','洋','勇','艳','杰','娟','涛','明','超','秀兰','霞'];
BEGINRETURN surnames[floor(random()*array_length(surnames,1)+1)] || givennames[floor(random()*array_length(givennames,1)+1)];
END;
$$ LANGUAGE plpgsql;-- 2. 执行数据生成DO块
DO $$
DECLAREmonth_start timestamp;month_end timestamp;month_intervals interval[] := ARRAY[interval '0 months', interval '1 month', interval '2 months',interval '3 months', interval '4 months', interval '5 months',interval '6 months', interval '7 months', interval '8 months',interval '9 months', interval '10 months', interval '11 months'];month_interval interval;
BEGIN-- 设置维护内存提高性能SET LOCAL maintenance_work_mem = '1GB';SET LOCAL work_mem = '256MB';-- 为每个月生成200万条数据FOREACH month_interval IN ARRAY month_intervals LOOPmonth_start := (date '2024-01-01' + month_interval)::timestamp;month_end := (date '2024-01-01' + month_interval + interval '1 month')::timestamp;RAISE NOTICE 'Generating data for month: % (%)', to_char(month_start, 'YYYY-MM'), to_char(clock_timestamp(), 'YYYY-MM-DD HH24:MI:SS');-- 每月插入200万条INSERT INTO t_common_work_order_log (work_order_log_id,operation_type,work_order_id,work_order_name,work_order_type,biz_location_id,planned_completion_time,actual_completion_time,handle_user_account,handle_user_name,create_time,create_by_uuid,create_by_account,create_by_name,last_update_time,last_update_uuid,last_update_account,last_update_name,biz_attribute_1,biz_attribute_2,biz_attribute_3,biz_attribute_4,biz_attribute_5,biz_attribute_6,biz_attribute_7,biz_attribute_8,biz_attribute_9,biz_attribute_10,biz_attribute_11,biz_attribute_12,biz_attribute_13,biz_attribute_14,biz_attribute_15,biz_attribute_16,biz_attribute_17,biz_attribute_18,biz_attribute_19,biz_attribute_20)SELECT substr(md5(random()::text || i::text), 1, 32),floor(random() * 5 + 1)::int2,'WO' || (2000000*extract(month FROM month_start) + i)::varchar,(ARRAY['设备维修','软件升级','网络故障','数据迁移','常规维护'])[floor(random()*5+1)],floor(random() * 10 + 1)::int4,floor(random() * 100 + 1)::int4,month_start + random() * (month_end - month_start) + (interval '1 day' * floor(random() * 3 + 1)),CASE WHEN random() > 0.2 THEN month_start + random() * (month_end - month_start) + (interval '1 hour' * floor(random() * 4 - 2))ELSE NULL END,'user' || floor(random() * 100 + 1)::varchar,random_chinese_name(),month_start + random() * (month_end - month_start),substr(md5(random()::text || i::text), 1, 32),'admin' || floor(random() * 10 + 1)::varchar,'系统管理员' || floor(random() * 5 + 1)::varchar,month_start + random() * (month_end - month_start) + (interval '1 hour' * floor(random() * 24)),substr(md5(random()::text || i::text), 1, 32),'user' || floor(random() * 100 + 1)::varchar,random_chinese_name(),md5(random()::text), md5(random()::text), md5(random()::text),md5(random()::text), md5(random()::text), md5(random()::text),md5(random()::text), md5(random()::text), md5(random()::text),md5(random()::text), md5(random()::text), md5(random()::text),md5(random()::text), md5(random()::text), md5(random()::text),md5(random()::text), md5(random()::text), md5(random()::text),md5(random()::text), md5(random()::text)FROM generate_series(1, 2000000) AS i;RAISE NOTICE 'Completed month: %, % records inserted', to_char(month_start, 'YYYY-MM'), 2000000;END LOOP;
END $$;
1-3、创建pg分区表-分区键为创建时间
-- 1-创建分区表
CREATE TABLE t_common_work_order_log_new (work_order_log_id varchar(32) NOT NULL,operation_type int2,work_order_id varchar(32),work_order_name varchar(100),work_order_type int4,biz_location_id int4,planned_completion_time timestamp,actual_completion_time timestamp,handle_user_account varchar(100),handle_user_name varchar(100),create_time timestamp(0),create_by_uuid varchar(32),create_by_account varchar(32),create_by_name varchar(100),last_update_time timestamp(0),last_update_uuid varchar(32),last_update_account varchar(32),last_update_name varchar(100),biz_attribute_1 varchar(255),biz_attribute_2 varchar(255),biz_attribute_3 varchar(255),biz_attribute_4 varchar(255),biz_attribute_5 varchar(255),biz_attribute_6 varchar(255),biz_attribute_7 varchar(255),biz_attribute_8 varchar(255),biz_attribute_9 varchar(255),biz_attribute_10 varchar(255),biz_attribute_11 varchar(255),biz_attribute_12 varchar(255),biz_attribute_13 varchar(255),biz_attribute_14 varchar(255),biz_attribute_15 varchar(255),biz_attribute_16 varchar(255),biz_attribute_17 varchar(255),biz_attribute_18 varchar(255),biz_attribute_19 varchar(255),biz_attribute_20 varchar(255)
) PARTITION BY RANGE (create_time)
;
1-4、创建24年所有分区
-- 2-创建所24年有分区
CREATE TABLE t_common_work_order_log_202401 PARTITION OF t_common_work_order_log_new
FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
CREATE TABLE t_common_work_order_log_202402 PARTITION OF t_common_work_order_log_new
FOR VALUES FROM ('2024-02-01') TO ('2024-03-01');
CREATE TABLE t_common_work_order_log_202403 PARTITION OF t_common_work_order_log_new
FOR VALUES FROM ('2024-03-01') TO ('2024-04-01');
CREATE TABLE t_common_work_order_log_202404 PARTITION OF t_common_work_order_log_new
FOR VALUES FROM ('2024-04-01') TO ('2024-05-01');
CREATE TABLE t_common_work_order_log_202405 PARTITION OF t_common_work_order_log_new
FOR VALUES FROM ('2024-05-01') TO ('2024-06-01');
CREATE TABLE t_common_work_order_log_202406 PARTITION OF t_common_work_order_log_new
FOR VALUES FROM ('2024-06-01') TO ('2024-07-01');
CREATE TABLE t_common_work_order_log_202407 PARTITION OF t_common_work_order_log_new
FOR VALUES FROM ('2024-07-01') TO ('2024-08-01');
CREATE TABLE t_common_work_order_log_202408 PARTITION OF t_common_work_order_log_new
FOR VALUES FROM ('2024-08-01') TO ('2024-09-01');
CREATE TABLE t_common_work_order_log_202409 PARTITION OF t_common_work_order_log_new
FOR VALUES FROM ('2024-09-01') TO ('2024-10-01');
CREATE TABLE t_common_work_order_log_202410 PARTITION OF t_common_work_order_log_new
FOR VALUES FROM ('2024-10-01') TO ('2024-11-01');
CREATE TABLE t_common_work_order_log_202411 PARTITION OF t_common_work_order_log_new
FOR VALUES FROM ('2024-11-01') TO ('2024-12-01');
CREATE TABLE t_common_work_order_log_202412 PARTITION OF t_common_work_order_log_new
FOR VALUES FROM ('2024-12-01') TO ('2025-01-01');
1-5、设置默认分区(兜底用)
当写入数据的创建时间没有匹配到已有分区时,会写入默认分区,避免数据库异常
-- 3. 设置默认分区(兜底用)
CREATE TABLE t_common_work_order_log_default PARTITION OF t_common_work_order_log_new DEFAULT;
1-6、迁移数据
-- 4. 迁移数据
INSERT INTO t_common_work_order_log_new
SELECT * FROM t_common_work_order_log;
1-7、创建分区表索引
-- 5. 创建分区表索引
CREATE INDEX order_log_biz_location_id_p_index ON t_common_work_order_log_new USING btree (biz_location_id pg_catalog.int4_ops ASC NULLS LAST
);CREATE INDEX order_log_create_by_account_p_index ON t_common_work_order_log_new USING btree (create_by_account COLLATE pg_catalog.default pg_catalog.text_ops ASC NULLS LAST
);CREATE INDEX order_log_create_time_p_index ON t_common_work_order_log_new USING btree (create_time pg_catalog.timestamp_ops ASC NULLS LAST
);CREATE INDEX order_log_handle_user_account_p_index ON t_common_work_order_log_new USING btree (handle_user_account COLLATE pg_catalog.default pg_catalog.text_ops ASC NULLS LAST
);CREATE INDEX order_log_last_update_account_p_index ON t_common_work_order_log_new USING btree (last_update_account COLLATE pg_catalog.default pg_catalog.text_ops ASC NULLS LAST
);CREATE INDEX order_log_last_update_time_p_index ON t_common_work_order_log_new USING btree (last_update_time pg_catalog.timestamp_ops ASC NULLS LAST
);CREATE INDEX order_log_operation_type_p_index ON t_common_work_order_log_new USING btree (operation_type pg_catalog.int2_ops ASC NULLS LAST
);CREATE INDEX order_log_work_order_name_p_index ON t_common_work_order_log_new USING btree (work_order_name COLLATE pg_catalog.default pg_catalog.text_ops ASC NULLS LAST
);CREATE INDEX order_log_work_order_type_p_index ON t_common_work_order_log_new USING btree (work_order_type pg_catalog.int4_ops ASC NULLS LAST
);
2、SQL增删改查测试
2-1、查询速度对比
2-1-1、查询总数量时间对比
- 非分区表首次查询约为
800s,预热后查询为2.4s - 分区表首次查询约为
380s,预热后查询为1.26s
-- 819.769s/2.427s
SELECT COUNT(*) FROM t_common_work_order_log;
-- 387.680s/1.260s
SELECT COUNT(*) FROM t_common_work_order_log_new;
查询总数量反直觉分析
为什么查询总数量分区表会比非分区表快?
执行EXPLAIN ANALYZE可以发现,分区表为并行扫描,最后会将并行扫描结果汇总:
-- 819.769s/2.427s
EXPLAIN ANALYZE SELECT COUNT(*) FROM t_common_work_order_log;
-- 387.680s/1.260s
EXPLAIN ANALYZE SELECT COUNT(*) FROM t_common_work_order_log_new;
Finalize Aggregate (cost=4126001.24..4126001.25 rows=1 width=8) (actual time=2554.156..2559.141 rows=1 loops=1)-> Gather (cost=4126001.03..4126001.24 rows=2 width=8) (actual time=2554.069..2559.129 rows=3 loops=1)Workers Planned: 2Workers Launched: 2-> Partial Aggregate (cost=4125001.03..4125001.04 rows=1 width=8) (actual time=2540.244..2540.245 rows=1 loops=3)-> Parallel Seq Scan on t_common_work_order_log (cost=0.00..4100001.02 rows=10000002 width=0) (actual time=0.036..2256.003 rows=8000000 loops=3)
Planning Time: 0.155 ms
JIT:Functions: 8Options: Inlining true, Optimization true, Expressions true, Deforming trueTiming: Generation 0.595 ms, Inlining 66.620 ms, Optimization 16.470 ms, Emission 12.494 ms, Total 96.179 ms
Execution Time: 2559.602 ms
Finalize Aggregate (cost=2071847.96..2071847.97 rows=1 width=8) (actual time=1737.444..1748.516 rows=1 loops=1)-> Gather (cost=2071847.74..2071847.95 rows=2 width=8) (actual time=1737.313..1748.503 rows=3 loops=1)Workers Planned: 2Workers Launched: 2-> Partial Aggregate (cost=2070847.74..2070847.75 rows=1 width=8) (actual time=1725.717..1725.721 rows=1 loops=3)-> Parallel Append (cost=0.43..2045847.74 rows=10000000 width=0) (actual time=35.470..1495.583 rows=8000000 loops=3)-> Parallel Index Only Scan using t_common_work_order_log_202406_operation_type_idx on t_common_work_order_log_202406 t_common_work_order_log_new_6 (cost=0.43..274942.74 rows=833486 width=0) (actual time=42.229..175.568 rows=2000366 loops=1)Heap Fetches: 94752-> Parallel Index Only Scan using t_common_work_order_log_202407_operation_type_idx on t_common_work_order_log_202407 t_common_work_order_log_new_7 (cost=0.43..203921.26 rows=833669 width=0) (actual time=42.227..164.912 rows=2000805 loops=1)Heap Fetches: 67128-> Parallel Index Only Scan using t_common_work_order_log_202410_work_order_type_idx on t_common_work_order_log_202410 t_common_work_order_log_new_10 (cost=0.43..25125.31 rows=833677 width=0) (actual time=0.073..101.861 rows=2000824 loops=1)Heap Fetches: 0-> Parallel Index Only Scan using t_common_work_order_log_202404_work_order_type_idx on t_common_work_order_log_202404 t_common_work_order_log_new_4 (cost=0.43..25116.53 rows=833459 width=0) (actual time=0.055..101.724 rows=2000302 loops=1)Heap Fetches: 0-> Parallel Index Only Scan using t_common_work_order_log_202403_work_order_type_idx on t_common_work_order_log_202403 t_common_work_order_log_new_3 (cost=0.43..25115.34 rows=833405 width=0) (actual time=0.075..101.928 rows=2000172 loops=1)Heap Fetches: 0-> Parallel Index Only Scan using t_common_work_order_log_202402_work_order_type_idx on t_common_work_order_log_202402 t_common_work_order_log_new_2 (cost=0.43..25115.08 rows=833393 width=0) (actual time=0.049..101.426 rows=2000144 loops=1)Heap Fetches: 0-> Parallel Index Only Scan using t_common_work_order_log_202401_work_order_type_idx on t_common_work_order_log_202401 t_common_work_order_log_new_1 (cost=0.43..25114.91 rows=833385 width=0) (actual time=0.079..101.461 rows=2000125 loops=1)Heap Fetches: 0-> Parallel Index Only Scan using t_common_work_order_log_202412_work_order_type_idx on t_common_work_order_log_202412 t_common_work_order_log_new_12 (cost=0.43..22083.01 rows=732663 width=0) (actual time=0.044..89.077 rows=1758391 loops=1)Heap Fetches: 0-> Parallel Index Only Scan using t_common_work_order_log_default_work_order_type_idx on t_common_work_order_log_default t_common_work_order_log_new_13 (cost=0.29..3030.63 rows=100470 width=0) (actual time=0.050..12.340 rows=241127 loops=1)Heap Fetches: 0-> Parallel Seq Scan on t_common_work_order_log_202409 t_common_work_order_log_new_9 (cost=0.00..341619.16 rows=833216 width=0) (actual time=0.053..196.980 rows=666573 loops=3)-> Parallel Seq Scan on t_common_work_order_log_202411 t_common_work_order_log_new_11 (cost=0.00..341578.15 rows=833115 width=0) (actual time=0.048..296.090 rows=999738 loops=2)-> Parallel Seq Scan on t_common_work_order_log_202408 t_common_work_order_log_new_8 (cost=0.00..341574.07 rows=833107 width=0) (actual time=0.057..584.129 rows=1999457 loops=1)-> Parallel Seq Scan on t_common_work_order_log_202405 t_common_work_order_log_new_5 (cost=0.00..341511.55 rows=832955 width=0) (actual time=21.951..597.384 rows=1999092 loops=1)
Planning Time: 0.608 ms
JIT:Functions: 47Options: Inlining true, Optimization true, Expressions true, Deforming trueTiming: Generation 1.001 ms, Inlining 65.639 ms, Optimization 19.031 ms, Emission 21.833 ms, Total 107.504 ms
Execution Time: 1749.153 ms
2-2-1、带上分区键查询对比
带上分区键在分表区间内查询
查询2024年4月份总数据量
-- 原始表2.066s
SELECT COUNT(*) FROM t_common_work_order_log WHERE create_time > '2024-04-01' AND create_time < '2024-05-01';
-- 分区表0.715s
SELECT COUNT(*) FROM t_common_work_order_log_new WHERE create_time > '2024-04-01' AND create_time < '2024-05-01';
带上分区键跨分表区间查询
查询2024年5月~6月总数据量
-- 原始表3.303s
SELECT COUNT(*) FROM t_common_work_order_log WHERE create_time > '2024-05-01' AND create_time < '2024-07-01';
-- 分区表1.083s
SELECT COUNT(*) FROM t_common_work_order_log_new WHERE create_time > '2024-05-01' AND create_time < '2024-07-01';
2-2、删除速度对比
2-2-1、全量删除TRUNCATE
相同的数据量分区表删除耗时远远大于原始表
-- 清空速度对比:
TRUNCATE TABLE t_common_work_order_log;
TRUNCATE TABLE t_common_work_order_log_new;


2-2-2、删除某个月数据
原始表DELETE 145.372s
DELETE FROM t_common_work_order_log
WHERE create_time >= '2024-05-01 00:00:00' AND create_time < '2024-06-01 00:00:00';

分区表DELETE(未指定分区)225.896s

分区表DELETE(指定分区)242.770s

分区表TRUNCATE(指定分区)13.156s

2-2-3、删除一条数据
测试10次,每次删除一条
原始表 平均8.009s
-- 113.489s 10.768s 7.393s 6.713s 8.896s 6.089s 7.506s 8.005s 7.955s 6.838s
DELETE FROM t_common_work_order_log WHERE work_order_log_id = '0e817fcd240bf4ecbaf0b5d05e793b9f';
分区表(未带上分区键) 平均7.858s
-- 33.275s 7.227s 7.545s 8.674s 6.186s 10.045s 7.103s 5.707s 7.112s 8.972s
DELETE FROM t_common_work_order_log_new WHERE work_order_log_id = '8326be2f926c1437a8b623b044518888';
分区表(带上分区键) 平均1.567s
-- 0.346s 2.341s 1.003s 2.226s 0.620s 2.955s 1.191s 0.071s 7.196s 1.855s
DELETE
FROMt_common_work_order_log_new
WHEREcreate_time = '2024-3-27 23:58:50' AND work_order_log_id = '040ba71c39f80e1217296f262e8e999f';
2-3、写入速度对比
2-2-1、全量插入2400w数据,均匀分布在每个月
插入sql可以参考执行脚本新增2400w行
原始表 3387.520s

分区表 4988.111s

2-2-2、指定某个月插入10w数据
原始表 63.572s
-- 生成10万条6月数据
INSERT INTO t_common_work_order_log (work_order_log_id,operation_type,work_order_id,work_order_name,work_order_type,biz_location_id,planned_completion_time,actual_completion_time,handle_user_account,handle_user_name,create_time,create_by_uuid,create_by_account,create_by_name,last_update_time,last_update_uuid,last_update_account,last_update_name,biz_attribute_1,biz_attribute_2,biz_attribute_3,biz_attribute_4,biz_attribute_5,biz_attribute_6,biz_attribute_7,biz_attribute_8,biz_attribute_9,biz_attribute_10
)
SELECT -- work_order_log_id: UUID简写substr(md5(random()::text || i::text), 1, 32),-- operation_type: 1-5随机值floor(random() * 5 + 1)::int2,-- work_order_id: WO+6月标识+序号'WO202406' || lpad(i::text, 5, '0'),-- work_order_name: 随机工单类型(ARRAY['设备维修','软件升级','网络故障','数据迁移','常规维护'])[floor(random()*5+1)],-- work_order_type: 1-10随机值floor(random() * 10 + 1)::int4,-- biz_location_id: 1-100随机floor(random() * 100 + 1)::int4,-- planned_completion_time: 6月随机时间+1-3天(timestamp '2024-06-01' + random() * interval '30 days') + (interval '1 day' * floor(random() * 3 + 1)),-- actual_completion_time: 80%有值,在计划时间±2小时CASE WHEN random() > 0.2 THEN (timestamp '2024-06-01' + random() * interval '30 days') + (interval '1 hour' * floor(random() * 4 - 2))ELSE NULL END,-- handle_user_account: user+随机编号'user' || floor(random() * 50 + 1)::varchar,-- handle_user_name: 随机中文名random_chinese_name(),-- create_time: 6月随机时间timestamp '2024-06-01' + random() * interval '30 days',-- create_by_uuid: 随机UUID简写substr(md5(random()::text || i::text), 1, 32),-- create_by_account: admin+随机编号'admin' || floor(random() * 5 + 1)::varchar,-- create_by_name: 管理员+编号'系统管理员' || floor(random() * 3 + 1)::varchar,-- last_update_time: create_time+0-24小时timestamp '2024-06-01' + random() * interval '30 days' + (interval '1 hour' * floor(random() * 24)),-- last_update_uuid: 随机UUID简写substr(md5(random()::text || i::text), 1, 32),-- last_update_account: user+随机编号'user' || floor(random() * 50 + 1)::varchar,-- last_update_name: 随机中文名random_chinese_name(),-- biz_attribute_1-10: 随机MD5值md5(random()::text), md5(random()::text), md5(random()::text),md5(random()::text), md5(random()::text), md5(random()::text),md5(random()::text), md5(random()::text), md5(random()::text),md5(random()::text)
FROM generate_series(1, 100000) AS i;

分区表(未指定分区) 58.759s
-- 生成10万条6月数据
INSERT INTO t_common_work_order_log_new (work_order_log_id,operation_type,work_order_id,work_order_name,work_order_type,biz_location_id,planned_completion_time,actual_completion_time,handle_user_account,handle_user_name,create_time,create_by_uuid,create_by_account,create_by_name,last_update_time,last_update_uuid,last_update_account,last_update_name,biz_attribute_1,biz_attribute_2,biz_attribute_3,biz_attribute_4,biz_attribute_5,biz_attribute_6,biz_attribute_7,biz_attribute_8,biz_attribute_9,biz_attribute_10
)
SELECT -- work_order_log_id: UUID简写substr(md5(random()::text || i::text), 1, 32),-- operation_type: 1-5随机值floor(random() * 5 + 1)::int2,-- work_order_id: WO+6月标识+序号'WO202406' || lpad(i::text, 5, '0'),-- work_order_name: 随机工单类型(ARRAY['设备维修','软件升级','网络故障','数据迁移','常规维护'])[floor(random()*5+1)],-- work_order_type: 1-10随机值floor(random() * 10 + 1)::int4,-- biz_location_id: 1-100随机floor(random() * 100 + 1)::int4,-- planned_completion_time: 6月随机时间+1-3天(timestamp '2024-06-01' + random() * interval '30 days') + (interval '1 day' * floor(random() * 3 + 1)),-- actual_completion_time: 80%有值,在计划时间±2小时CASE WHEN random() > 0.2 THEN (timestamp '2024-06-01' + random() * interval '30 days') + (interval '1 hour' * floor(random() * 4 - 2))ELSE NULL END,-- handle_user_account: user+随机编号'user' || floor(random() * 50 + 1)::varchar,-- handle_user_name: 随机中文名random_chinese_name(),-- create_time: 6月随机时间timestamp '2024-06-01' + random() * interval '30 days',-- create_by_uuid: 随机UUID简写substr(md5(random()::text || i::text), 1, 32),-- create_by_account: admin+随机编号'admin' || floor(random() * 5 + 1)::varchar,-- create_by_name: 管理员+编号'系统管理员' || floor(random() * 3 + 1)::varchar,-- last_update_time: create_time+0-24小时timestamp '2024-06-01' + random() * interval '30 days' + (interval '1 hour' * floor(random() * 24)),-- last_update_uuid: 随机UUID简写substr(md5(random()::text || i::text), 1, 32),-- last_update_account: user+随机编号'user' || floor(random() * 50 + 1)::varchar,-- last_update_name: 随机中文名random_chinese_name(),-- biz_attribute_1-10: 随机MD5值md5(random()::text), md5(random()::text), md5(random()::text),md5(random()::text), md5(random()::text), md5(random()::text),md5(random()::text), md5(random()::text), md5(random()::text),md5(random()::text)
FROM generate_series(1, 100000) AS i;

分区表(指定分区) 48.098s

2-4、更新速度对比
2-4-1、修改指定月份的处理人信息
原始表 538.133s
UPDATE t_common_work_order_log
SET handle_user_account = 'hander-system'
WHEREcreate_time > '2024-07-01' AND create_time < '2024-08-01';

分区表(未指定分区) 1252.286s
UPDATE t_common_work_order_log_new
SET handle_user_account = 'hander-system'
WHEREcreate_time > '2024-07-01' AND create_time < '2024-08-01';

分区表(指定分区) 1252.286s
UPDATE t_common_work_order_log_new
SET handle_user_account = 'hander-system'
WHEREcreate_time > '2024-07-01' AND create_time < '2024-08-01';
2-4-1、根据主键修处理人信息
原始表 12.597s
UPDATE t_common_work_order_log
SET handle_user_account = 'hander-system-byid'
WHERE work_order_log_id = 'db3114ba4a676937269e2cf0ef7454b8';

分区表(未带上分区键) 10.870s
UPDATE t_common_work_order_log_new
SET handle_user_account = 'hander-system-byid'
WHERE work_order_log_id = '9ac76e7e262cd7b374d6939a7e0f4144';

分区表(带上分区键) 0.438s
UPDATE t_common_work_order_log_new
SET handle_user_account = 'hander-system-by shard id'
WHEREcreate_time = '2024-12-31 23:59:19' AND work_order_log_id = '9ac76e7e262cd7b374d6939a7e0f4144';

3、总结
文本基于PostgreSQL原生分区能力,以创建时间作为分区键进行水平分表验证,要想发挥分区表的优势,多需要带上分区键进行操作,例如上文的删除以及更新操作中,带上分区键比未带分区键操作,性能存在10倍左右差异,但是进行大批量的操作,分区表的优势不明显,甚至有些场景还会非常慢。
PostgreSQL分区为数据库引擎内置功能,无需额外的应用层中间件,性能损失较小,提供了基础的分区能力,适用于如下场景:
- 数据量在单机可承受范围内(<5TB)
- 只需要表级别的分区
- 希望最小化技术栈复杂度
- 需要利用PG特定功能(如GIS、JSONB)
相关文章:
PostgreSQL 分区表——范围分区SQL实践
PostgreSQL 分区表——范围分区SQL实践 1、环境准备1-1、新增原始表1-2、执行脚本新增2400w行1-3、创建pg分区表-分区键为创建时间1-4、创建24年所有分区1-5、设置默认分区(兜底用)1-6、迁移数据1-7、创建分区表索引 2、SQL增删改查测试2-1、查询速度对比…...
4.3 工具调用与外部系统集成:API调用、MCP(模型上下文协议)、A2A、数据库查询与信息检索的实现
工具调用与外部系统集成是智能代理(Agent)系统实现复杂功能和企业级应用的核心支柱。Agent通过API调用访问实时服务,**模型上下文协议(Model Context Protocol, MCP)**标准化数据交互,Agent-to-Agent&#…...
展锐Android13电池问题导致系统的崩溃,(2)电池电压计算和电池曲线
先看is_bat_low函数的代码: #ifndef LOW_BAT_VOL //# define LOW_BAT_VOL 3400 #define LOW_BAT_VOL 3672 #endif #ifndef LOW_BAT_VOL_CHG //# define LOW_BAT_VOL_CHG 3500 #define LOW_BAT_VOL_CHG 3719 #endifint is_bat_low(void) {int32_t vbat_vol;uin…...
SpringCloud 微服务复习笔记
文章目录 微服务概述单体架构微服务架构 微服务拆分微服务拆分原则拆分实战第一步:创建一个新工程第二步:创建对应模块第三步:引入依赖第四步:被配置文件拷贝过来第五步:把对应的东西全部拷过来第六步:创建…...
【Python爬虫基础篇】--4.Selenium入门详细教程
先解释:Selenium:n.硒;硒元素 目录 1.Selenium--简介 2.Selenium--原理 3.Selenium--环境搭建 4.Selenium--简单案例 5.Selenium--定位方式 6.Selenium--常用方法 6.1.控制操作 6.2.鼠标操作 6.3.键盘操作 6.4.获取断言信息 6.5.…...
【Python爬虫详解】第四篇:使用解析库提取网页数据——XPath
在前一篇文章中,我们介绍了如何使用BeautifulSoup解析库从HTML中提取数据。本篇文章将介绍另一个强大的解析工具:XPath。XPath是一种在XML文档中查找信息的语言,同样适用于HTML文档。它的语法简洁而强大,特别适合处理结构复杂的网…...
二分小专题
P1102 A-B 数对 P1102 A-B 数对 暴力枚举还是很好做的,直接上双层循环OK 二分思路:查找边界情况,找出最大下标和最小下标,两者相减1即为答案所求 废话不多说,上代码 //暴力O(n^3) 72pts // #include<bits/stdc.h> // usin…...
Langchain检索YouTube字幕
创建一个简单搜索引擎,将用户原始问题传递该搜索系统 本文重点:获取保存文档——保存向量数据库——加载向量数据库 专注于youtube的字幕,利用youtube的公开接口,获取元数据 pip install youtube-transscript-api pytube 初始化 …...
【Linux网络】应用层自定义协议与序列化及Socket模拟封装
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
客户案例:西范优选通过日事清实现流程与项目管理的优化
近几年来,新零售行业返璞归真,从线上销售重返线下发展,满足消费者更加多元化的需求,国内家居集合店如井喷式崛起。为在激烈的市场竞争中立于不败之地,西范优选专注于加强管理能力、优化协作效率的“内功修炼”…...
LabVIEW实现Voronoi图绘制功能
该 LabVIEW 虚拟仪器(VI)借助 MathScript 节点,实现基于手机信号塔位置计算 Voronoi 图的功能。通过操作演示,能直观展示 Voronoi 图在空间划分上的应用。 各部分功能详细说明 随机地形创建部分 功能:根据 “Maximum a…...
【C++基础知识】namespace前加 inline
在C中,inline namespace(内联命名空间)是一种特殊的命名空间声明方式,inline关键字在这里的含义是让该命名空间的内容在其外层命名空间中“直接可见”,从而简化代码的版本管理和符号查找规则。以下是详细解释ÿ…...
离线部署kubernetes
麒麟Linux服务器 AMR架构 🧰 离线部署 Kubernetes v1.25.9(麒麟系统 Docker) 一、验证Docker部署状态 检查Docker服务运行状态 systemctl status docker 预期输出应显示 Active: active (running),表明服务已启动18。 …...
【AI提示词】私人教练
提示说明 以专业且细致的方式帮助客户实现健康与健身目标,提升整体生活质量。 提示词 # Role: 私人教练## Profile - language: 中文 - description: 以专业且细致的方式帮助客户实现健康与健身目标,提升整体生活质量 - background: 具备丰富的健身经…...
爬虫学习——获取动态网页信息
对于静态网页可以直接研究html网页代码实现内容获取,对于动态网页绝大多数都是页面内容是通过JavaScript脚本动态生成(也就是json数据格式),而不是静态的,故需要使用一些新方法对其进行内容获取。凡是通过静态方法获取不到的内容,…...
第54讲:总结与前沿展望——农业智能化的未来趋势与研究方向
目录 一、本板块内容回顾:人工智能助力农业的多元化应用 ✅ 精准农业与AI ✅ 农业金融与AI ✅ AI与农业政策 ✅ 农业物联网与AI 二、前沿趋势与研究方向:迈向智能、可持续农业的未来 1. AIGC(生成式AI)在农业中的应用 2. 数字孪生农业:虚拟与现实的无缝对接 3. A…...
创新项目实训开发日志4
一、开发简介 核心工作内容:logo实现、注册实现、登录实现、上传gitee 工作时间:第十周 二、logo实现 1.设计logo 2.添加logo const logoUrl new URL(/assets/images/logo.png, import.meta.url).href <div class"aside-first">…...
常见接口测试常见面试题(JMeter)
JMeter 是 Apache 提供的开源性能测试工具,主要用于对 Web 应用、REST API、数据库、FTP 等进行性能、负载和功能测试。它支持多种协议,如 HTTP、HTTPS、JDBC、SOAP、FTP 等。 在一个线程组中,JMeter 的执行顺序通常为:配置元件…...
发布事件和Insert数据库先后顺序
代码解释 csharp await PublishCreatedAsync(entity).ConfigureAwait(false); await Repository.InsertAsync(entity).ConfigureAwait(false);PublishCreatedAsync(entity):这是一个异步方法,其功能是发布与实体创建相关的事件。此方法或许会通知其他组…...
函数重载(Function Overloading)
1. 函数重载的核心概念 函数重载允许在 同一作用域内定义多个同名函数,但它们的 参数列表(参数类型、顺序或数量)必须不同。编译器在编译时根据 调用时的实参类型和数量 静态选择最匹配的函数版本。 2. 源码示例:基础函数重载 示…...
CGAL 网格等高线计算
文章目录 一、简介二、实现代码三、实现效果一、简介 这里等高线的计算其实很简单,使用不同高度的水平面与网格进行相交,最后获取不同高度的相交线即可。 二、实现代码 #include <iostream> #include <iterator> #include <map>...
计算机组成与体系结构:缓存(Cache)
目录 为什么需要 Cache? 🧱 Cache 的分层设计 🔹 Level 1 Cache(L1 Cache)一级缓存 🔹 Level 2 Cache(L2 Cache)二级缓存 🔹 Level 3 Cache(L3 Cache&am…...
Flutter 在全新 Platform 和 UI 线程合并后,出现了什么大坑和变化?
Flutter 在全新 Platform 和 UI 线程合并后,出现了什么大坑和变化? 在两个月前,我们就聊过 3.29 上《Platform 和 UI 线程合并》的具体原因和实现方式,而事实上 Platform 和 UI 线程合并,确实为后续原生语言和 Dart 的…...
开发 MCP Proxy(代理)也可以用 Solon AI MCP 哟!
MCP 有三种通讯方式: 通道说明备注stdio本地进程内通讯现有sse http远程 http 通讯现有streamable http远程 http 通讯(MCP 官方刚通过决定,mcp-java-sdk 还没实现) 也可以按两大类分: 本地进程间通讯远程通讯&…...
JetBrains GoLang IDE无限重置试用期,适用最新2025版
注意本文仅用于学习使用!!! 本文在重置2024.3.5版本亲测有效,环境为window(mac下应该也一样奏效) 之前eval-reset插件只能在比较低的版本才能起作用。 总结起来就一句:卸载重装,额外要删掉旧安装文件和注册…...
python中socket(套接字)库详细解析
目录 1. 前言 2. socket 库基础 2.1 什么是 socket? 2.2 socket 的类型 3. 基于 TCP 的 socket 编程 3.1 TCP 服务器端代码示例 3.2 TCP 客户端代码示例 3.3 代码分析 4. 基于 UDP 的 socket 编程 4.1 UDP 服务器端代码示例 4.2 UDP 客户端代码示例 4.3…...
鸿蒙-状态管理V1和V2在ForEach循环渲染的表现
目录 前提遇到的问题换V2呗 状态管理V2已经出来好长时间了,移除GAP说明也有一段时间了,相信有一部分朋友已经开始着手从V1迁移到V2了,应该也踩了不少坑。 下面向大家分享一下我使用状态管理V1和Foreach时遇到的坑,以及状态管理V2在…...
深入了解递归、堆与栈:C#中的内存管理与函数调用
在编程中,理解如何有效地管理内存以及如何控制程序的执行流程是每个开发者必须掌握的基本概念。C#作为一种高级编程语言,其内存管理和函数调用机制包括递归、堆与栈。本文将详细讲解这三者的工作原理、用途以及它们在C#中的实现和应用。 1. 递归 (Recur…...
图论---Prim堆优化(稀疏图)
题目通常会提示数据范围: 若 V ≤ 500,两种方法均可(朴素Prim更稳)。 若 V ≤ 1e5,必须用优先队列Prim vector 存图。 #include <iostream> #include <vector> #include <queue> #include <…...
stm32之GPIO函数详解和上机实验
目录 1.LED和蜂鸣器1.1 LED1.2 蜂鸣器 2.实验2.1 库函数:RCC和GPIO2.1.1 RCC函数1. RCC_AHBPeriphClockCmd2. RCC_APB2PeriphClockCmd3. RCC_APB1PeriphClockCmd 2.1.2 GPIO函数1. GPIO_DeInit2. GPIO_AFIODeInit3. GPIO_Init4. GPIO_StructInit5. GPIO_ReadInputDa…...
