# GUID 主键与 service_mask 索引改造实施方案 ## 目标 - 将 `heartbeat.heartbeat_events` 的主键从自增 `id` 改为 GUID(32 位无连字符 HEX 字符串)。 - 为 `service_mask` 的“首位(最低位)判断”新增表达式索引 `idx_service_mask_first_bit`,用于优化常见 bit 查询。 - 说明:你会删除现有数据库,因此不提供在线迁移流程,仅提供可重建的初始化脚本与验证步骤。 ## 方案概述 ### 1) 主键(GUID) - 表字段 `guid` 使用 `varchar(32)` 并设置默认值 `replace(gen_random_uuid()::text, '-', '')`。 - 通过 `CHECK (guid ~ '^[0-9a-f]{32}$')` 约束输入格式为 32 位小写 HEX。 - 分区表使用组合主键 `PRIMARY KEY (ts_ms, guid)` 以满足 PostgreSQL 分区唯一约束要求(主键/唯一约束需包含分区键)。 ### 2) service_mask 首位查询表达式索引 - 在父表创建索引: - `CREATE INDEX idx_service_mask_first_bit ON heartbeat.heartbeat_events ((service_mask & 1));` - 在按天分区创建索引: - `idx__service_mask_first_bit` ## 实施步骤(重建式) 1. 删除旧数据库/旧 schema(你将执行)。 2. 执行建表脚本: - `scripts/db/010_heartbeat_schema.sql` - `scripts/db/020_partitioning_auto_daily.sql` 3. 启动服务或执行 `npm run db:apply` 进行初始化。 4. 执行验证项: - 确认 `heartbeat.heartbeat_events.guid` 为 `varchar(32)` 且存在默认值 - 确认存在 `idx_service_mask_first_bit` - 确认分区新建后存在 `idx__service_mask_first_bit` ## 风险评估 - GUID 默认生成依赖 `pgcrypto` 扩展:脚本已包含 `CREATE EXTENSION IF NOT EXISTS pgcrypto;`,但执行账号需要具备安装扩展权限。 - 分区表主键约束限制:无法实现“父表仅 guid 作为主键约束”,因此使用 `(ts_ms, guid)` 的组合主键。 - 表达式索引的表达式匹配:业务查询需要匹配索引表达式(详见 checklist),否则无法命中索引。 ## 回滚步骤(重建式) 1. 停止服务写入。 2. `DROP SCHEMA heartbeat CASCADE;`(或删除数据库)。 3. 使用回滚版本的 SQL 脚本重新创建(例如回退到 `id bigserial` 版本的脚本)。 4. 重新启动服务并验证写入与查询。