数据库中的表根据数据的时间戳(即tablename_y2019w20)按周进行分区。但是,当引入分区时,postgres开始占用太多的CPU时间。
通过运行
收集CPU使用情况统计信息
SELECT substring(query, 1, 50) AS short_query, round(total_time::numeric, 2) AS total_time, calls, rows, round(total_time::numeric / calls, 2) AS avg_time, round((100 * total_time / sum(total_time::numeric) OVER ())::numeric, 2) AS percentage_cpu FROM pg_stat_statements ORDER BY total_time DESC LIMIT 20;
表明瓶颈是触发函数(见下文)中的语句SELECT NOT EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name=tablename)
每次插入都会执行。
例如,每秒插入18个元素的EXPLAIN ANALYZE
看起来像这样:
Planning time: 0.787 ms
Trigger before_insert_data_trigger: time=253.374 calls=18
Execution time: 254.161 ms
但是,由于我们每周只需要创建一次分区表,因此大多数情况下所有语句都是无用的。但是我无法事先从postgres外部创建分区。
是否可以安排在postgres中创建分区,例如仅在每个星期日进行创建?
这里是函数和各自的触发器:
CREATE OR REPLACE FUNCTION data_insert_child_date()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
match data."timestamp"%TYPE;
checks TEXT;
tablename_parent text := "data";
tablename text;
BEGIN
IF NEW."timestamp" IS NULL THEN
tablename := tablename_parent||'_null';
checks := '"timestamp" IS NULL';
ELSE
match := DATE_TRUNC('week', NEW."timestamp");
tablename := tablename_parent||'_' || TO_CHAR(NEW."timestamp", '"y"IYYY"w"IW');
checks := '"timestamp" >= ''' || match || ''' AND "timestamp" < ''' || (match + INTERVAL '1 week') || '''';
END IF;
IF NOT EXISTS(
SELECT 1 FROM information_schema.tables WHERE table_name=tablename)
THEN
BEGIN
EXECUTE 'CREATE TABLE part.' || tablename || ' (
CHECK (' || checks || '),
LIKE "data" INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES
) INHERITS (part."'||tablename_parent||'");
';
EXCEPTION WHEN duplicate_table THEN
-- pass
END;
END IF;
EXECUTE 'INSERT INTO part.' || tablename || ' VALUES (($1).*);' USING NEW;
RETURN NEW;
END;
$function$;
和触发器
CREATE TRIGGER before_insert_data_trigger BEFORE INSERT
ON data
FOR EACH ROW
EXECUTE PROCEDURE data_insert_child_date();
答案 0 :(得分:0)
您只能使用cron或pgagent之类的外部工具进行调度,并且在需要此分区之前必须这样做,或者确保在创建cronjob或pgagent作业(或任何其他方法)之前,没有任何东西可以插入数据。就像您提到的那样,假设您无法预先创建分区。但是随后您仍然必须为所有表创建它们,或者以某种方式找出需要它们的表(例如将当天的数据插入到父表中,然后在一天或一周结束时将其移至分区)。
您可以将分区检查限制为仅星期日:
IF extract(dow FROM current_date) = 7 /*maybe = 0 in US*/ AND NOT EXISTS(..
但是,如果由于某种原因(星期天没有数据,服务器关闭)在星期天不执行该触发器,那么一旦星期一到来,您就不会在该特定星期进行分区。
如果您不必使用information_schema.tables
,则可以使检查更快。在其中有〜100000行(索引,表,视图等)的目录上,检查我的计算机上是否存在一张表大约需要100毫秒,而对pg_catalog.pg_class
进行的同一检查直接需要不到1毫秒。
--pg 9.5 and up
SELECT 1 FROM pg_class
WHERE relname = 'table_name'
AND relnamespace = 'schema_name'::regnamespace;
--pg 9.4
SELECT 1 FROM pg_class
WHERE relname = 'table_name'
AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'schema_name');
我个人认为我会每隔几个月就提前创建几个分区。这就是我目前的雇主所拥有的。 当然,最好升级到PG 11,但是我知道这可能不是一个选择。