feat: 添加分区表空间管理功能,优化分区表和索引的表空间设置
This commit is contained in:
@@ -3,6 +3,8 @@ import dbManager from './databaseManager.js';
|
|||||||
|
|
||||||
const PARENT_TABLE = 'rcu_action.rcu_action_events';
|
const PARENT_TABLE = 'rcu_action.rcu_action_events';
|
||||||
const PARTITION_TABLESPACE = 'ts_hot';
|
const PARTITION_TABLESPACE = 'ts_hot';
|
||||||
|
const PARTITION_SCHEMA = 'rcu_action';
|
||||||
|
const PARTITION_PREFIX = 'rcu_action_events_';
|
||||||
const PARENT_INDEX_STATEMENTS = [
|
const PARENT_INDEX_STATEMENTS = [
|
||||||
'CREATE INDEX IF NOT EXISTS idx_rcu_action_hotel_id ON rcu_action.rcu_action_events (hotel_id);',
|
'CREATE INDEX IF NOT EXISTS idx_rcu_action_hotel_id ON rcu_action.rcu_action_events (hotel_id);',
|
||||||
'CREATE INDEX IF NOT EXISTS idx_rcu_action_room_id ON rcu_action.rcu_action_events (room_id);',
|
'CREATE INDEX IF NOT EXISTS idx_rcu_action_room_id ON rcu_action.rcu_action_events (room_id);',
|
||||||
@@ -14,6 +16,72 @@ const PARENT_INDEX_STATEMENTS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
class PartitionManager {
|
class PartitionManager {
|
||||||
|
toSqlTextLiteral(value) {
|
||||||
|
return `'${String(value).replace(/'/g, "''")}'`;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildForceTablespaceSql({ schema, partition, tablespace }) {
|
||||||
|
return `
|
||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
v_schema text := ${this.toSqlTextLiteral(schema)};
|
||||||
|
v_partition text := ${this.toSqlTextLiteral(partition)};
|
||||||
|
v_hot text := ${this.toSqlTextLiteral(tablespace)};
|
||||||
|
v_part_oid oid;
|
||||||
|
v_toast_oid oid;
|
||||||
|
r record;
|
||||||
|
BEGIN
|
||||||
|
SELECT c.oid INTO v_part_oid
|
||||||
|
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
|
||||||
|
WHERE n.nspname = v_schema AND c.relname = v_partition AND c.relkind = 'r';
|
||||||
|
|
||||||
|
IF v_part_oid IS NULL THEN
|
||||||
|
RAISE EXCEPTION 'partition %.% not found', v_schema, v_partition;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- 1) 分区表对象 -> hot
|
||||||
|
EXECUTE format('ALTER TABLE %I.%I SET TABLESPACE %I', v_schema, v_partition, v_hot);
|
||||||
|
|
||||||
|
-- 2) 分区全部索引 -> hot
|
||||||
|
FOR r 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 = v_schema
|
||||||
|
AND t.relname = v_partition
|
||||||
|
AND COALESCE(ts.spcname, 'pg_default') <> v_hot
|
||||||
|
LOOP
|
||||||
|
EXECUTE format('ALTER INDEX %I.%I SET TABLESPACE %I', r.index_schema, r.index_name, v_hot);
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
-- 3) TOAST 表 + TOAST 全部索引 -> hot(若存在)
|
||||||
|
SELECT reltoastrelid INTO v_toast_oid FROM pg_class WHERE oid = v_part_oid;
|
||||||
|
IF v_toast_oid IS NOT NULL AND v_toast_oid <> 0 THEN
|
||||||
|
EXECUTE format('ALTER TABLE %s SET TABLESPACE %I', v_toast_oid::regclass, v_hot);
|
||||||
|
|
||||||
|
FOR r IN
|
||||||
|
SELECT idxn.nspname AS index_schema, i.relname AS index_name
|
||||||
|
FROM pg_index x
|
||||||
|
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 x.indrelid = v_toast_oid
|
||||||
|
AND COALESCE(ts.spcname, 'pg_default') <> v_hot
|
||||||
|
LOOP
|
||||||
|
EXECUTE format('ALTER INDEX %I.%I SET TABLESPACE %I', r.index_schema, r.index_name, v_hot);
|
||||||
|
END LOOP;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- 4) 统计信息
|
||||||
|
EXECUTE format('ANALYZE %I.%I', v_schema, v_partition);
|
||||||
|
END $$;
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
async ensureParentIndexes(client) {
|
async ensureParentIndexes(client) {
|
||||||
for (const sql of PARENT_INDEX_STATEMENTS) {
|
for (const sql of PARENT_INDEX_STATEMENTS) {
|
||||||
await client.query(sql);
|
await client.query(sql);
|
||||||
@@ -59,7 +127,8 @@ class PartitionManager {
|
|||||||
targetDate.setDate(now.getDate() + i);
|
targetDate.setDate(now.getDate() + i);
|
||||||
|
|
||||||
const { startMs, endMs, partitionSuffix } = this.getPartitionInfo(targetDate);
|
const { startMs, endMs, partitionSuffix } = this.getPartitionInfo(targetDate);
|
||||||
const partitionName = `rcu_action.rcu_action_events_${partitionSuffix}`;
|
const partitionTable = `${PARTITION_PREFIX}${partitionSuffix}`;
|
||||||
|
const partitionName = `${PARTITION_SCHEMA}.${partitionTable}`;
|
||||||
|
|
||||||
// Check if partition exists
|
// Check if partition exists
|
||||||
const checkSql = `
|
const checkSql = `
|
||||||
@@ -76,6 +145,13 @@ class PartitionManager {
|
|||||||
TABLESPACE ${PARTITION_TABLESPACE};
|
TABLESPACE ${PARTITION_TABLESPACE};
|
||||||
`;
|
`;
|
||||||
await client.query(createSql);
|
await client.query(createSql);
|
||||||
|
|
||||||
|
const forceTablespaceSql = this.buildForceTablespaceSql({
|
||||||
|
schema: PARTITION_SCHEMA,
|
||||||
|
partition: partitionTable,
|
||||||
|
tablespace: PARTITION_TABLESPACE
|
||||||
|
});
|
||||||
|
await client.query(forceTablespaceSql);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.info('Partition check completed.');
|
logger.info('Partition check completed.');
|
||||||
|
|||||||
Reference in New Issue
Block a user