From 9165ce0bd018621194b874351f5f8e14911c7c1e Mon Sep 17 00:00:00 2001 From: XuJiacheng Date: Tue, 3 Mar 2026 21:00:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=88=86=E5=8C=BA?= =?UTF-8?q?=E8=A1=A8=E7=A9=BA=E9=97=B4=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E5=88=86=E5=8C=BA=E8=A1=A8=E5=92=8C?= =?UTF-8?q?=E7=B4=A2=E5=BC=95=E7=9A=84=E8=A1=A8=E7=A9=BA=E9=97=B4=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/db/partitionManager.js | 78 ++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/bls-rcu-action-backend/src/db/partitionManager.js b/bls-rcu-action-backend/src/db/partitionManager.js index 16e532c..c3e4be6 100644 --- a/bls-rcu-action-backend/src/db/partitionManager.js +++ b/bls-rcu-action-backend/src/db/partitionManager.js @@ -3,6 +3,8 @@ import dbManager from './databaseManager.js'; const PARENT_TABLE = 'rcu_action.rcu_action_events'; const PARTITION_TABLESPACE = 'ts_hot'; +const PARTITION_SCHEMA = 'rcu_action'; +const PARTITION_PREFIX = 'rcu_action_events_'; 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_room_id ON rcu_action.rcu_action_events (room_id);', @@ -14,6 +16,72 @@ const PARENT_INDEX_STATEMENTS = [ ]; 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) { for (const sql of PARENT_INDEX_STATEMENTS) { await client.query(sql); @@ -59,7 +127,8 @@ class PartitionManager { targetDate.setDate(now.getDate() + i); 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 const checkSql = ` @@ -76,6 +145,13 @@ class PartitionManager { TABLESPACE ${PARTITION_TABLESPACE}; `; 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.');