2.4 KiB
2.4 KiB
-- 通过 docker compose 在容器内执行 psql,并使用 here-doc 传入 SQL docker compose exec -T postgres psql -U log_admin -d log_platform -v ON_ERROR_STOP=1 <<'SQL'
-- 使用匿名代码块批量处理分区创建与索引迁移 DO $$ DECLARE d date; -- 循环日期(从今天到未来 29 天) pname text; -- 分区表名,例如 heartbeat_events_20260303 start_ms bigint; -- 分区起始毫秒时间戳(UTC,含) end_ms bigint; -- 分区结束毫秒时间戳(UTC,不含) idx record; -- 遍历分区索引时的游标记录 BEGIN -- 生成从 current_date 到 current_date+29 的日期序列(共 30 天) FOR d IN SELECT generate_series(current_date, current_date + 29, interval '1 day')::date LOOP -- 按约定命名分区名:heartbeat_events_YYYYMMDD pname := format('heartbeat_events_%s', to_char(d, 'YYYYMMDD'));
-- 计算该日期 00:00:00 UTC 的毫秒时间戳作为分区下界
start_ms := (extract(epoch from (d::timestamp at time zone 'UTC')) * 1000)::bigint;
-- 计算下一天 00:00:00 UTC 的毫秒时间戳作为分区上界
end_ms := (extract(epoch from ((d + 1)::timestamp at time zone 'UTC')) * 1000)::bigint;
-- 若分区不存在则创建;存在则跳过(幂等)
EXECUTE format(
'CREATE TABLE IF NOT EXISTS heartbeat.%I PARTITION OF heartbeat.heartbeat_events FOR VALUES FROM (%s) TO (%s) TABLESPACE ts_hot',
pname, start_ms, end_ms
);
-- 无论新建或已存在,强制把分区表迁移到 ts_hot(保证热分区落热盘)
EXECUTE format('ALTER TABLE heartbeat.%I SET TABLESPACE ts_hot', pname);
-- 遍历该分区的全部索引,筛出不在 ts_hot 的索引
FOR idx IN
SELECT idxn.nspname AS index_schema, i.relname AS index_name
FROM pg_index x
JOIN pg_class t ON t.oid = x.indrelid
JOIN pg_namespace nt ON nt.oid = t.relnamespace
JOIN pg_class i ON i.oid = x.indexrelid
JOIN pg_namespace idxn ON idxn.oid = i.relnamespace
LEFT JOIN pg_tablespace ts ON ts.oid = i.reltablespace
WHERE nt.nspname = 'heartbeat'
AND t.relname = pname
AND COALESCE(ts.spcname, 'pg_default') <> 'ts_hot'
LOOP
-- 将索引迁移到 ts_hot,确保“分区与索引同盘”
EXECUTE format('ALTER INDEX %I.%I SET TABLESPACE ts_hot', idx.index_schema, idx.index_name);
END LOOP;
END LOOP; END $$;
-- here-doc 结束标记 SQL