- Create SECURITY.md with vulnerability disclosure policy - Create docs/README.md as documentation index page - Create docs/observability.md with metrics/logs/traces/alerting/SLO specs - Create docs/runbook.md with troubleshooting scenarios (RB-01 to RB-05) - Create docs/security.md with security design and compliance - Create docs/release.md with versioning and rollback strategies - Create docs/database.md with database design and migration strategy - Create docs/adr/0005-cache-error-handler.md for cache error handling ADR - Update README.md with documentation index section - Update docs/architecture.md to update B-7 rule and add trust boundary section - Create docs/api-guide.md with API specifications - Fix method names in OrderDao.java to match OrderMapper interface - Add Spring Boot Actuator dependency to tmpl-infrastructure - Fix Redis ping method in RedisHealthContributor.java - Fix syntax errors and method names in OrderAppService.java Co-authored-by: Qwen-Coder <[email protected]>
131 lines
4.5 KiB
Markdown
131 lines
4.5 KiB
Markdown
# 数据库设计与迁移 (Database)
|
||
|
||
---
|
||
|
||
## 1. 技术选型
|
||
|
||
| 项 | 选择 | 说明 |
|
||
|----|------|------|
|
||
| 数据库 | PostgreSQL 15+ | JSONB 支持、RLS、分区表 |
|
||
| 连接池 | HikariCP(Spring Boot 默认) | 高性能、低开销 |
|
||
| 迁移工具 | Flyway | Forward-only、版本化迁移 |
|
||
| ORM | Spring Data JPA + Hibernate | 通过 MapStruct 转换 PO ↔ Domain |
|
||
|
||
---
|
||
|
||
## 2. Schema 设计原则
|
||
|
||
### 2.1 多租户隔离
|
||
|
||
所有业务表必须包含 `tenant_id BIGINT NOT NULL` 列。所有查询条件必须包含 `tenant_id`,通过 Repository 方法签名强制(如 `findByTenantIdAndId`)。
|
||
|
||
建议在生产环境启用 PostgreSQL Row-Level Security (RLS) 作为最后防线:
|
||
|
||
```sql
|
||
ALTER TABLE t_order ENABLE ROW LEVEL SECURITY;
|
||
CREATE POLICY tenant_isolation ON t_order
|
||
USING (tenant_id = current_setting('app.current_tenant')::BIGINT);
|
||
```
|
||
|
||
### 2.2 审计字段
|
||
|
||
所有表通过 JPA `@MappedSuperclass`(`BaseAuditPO`)自动维护:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `create_time` | `TIMESTAMP NOT NULL DEFAULT now()` | 创建时间 |
|
||
| `update_time` | `TIMESTAMP NOT NULL DEFAULT now()` | 最后更新时间 |
|
||
| `deleted` | `BOOLEAN NOT NULL DEFAULT false` | 软删除标记 |
|
||
|
||
### 2.3 软删除
|
||
|
||
业务表使用 `deleted` 字段实现软删除。所有查询默认排除已删除记录(通过 Hibernate `@Where` 或手动条件)。物理删除仅在数据归档场景下使用。
|
||
|
||
---
|
||
|
||
## 3. 索引策略
|
||
|
||
### 3.1 通用规则
|
||
|
||
每张表的主键使用自增 `BIGSERIAL` 或 `UUID`(根据业务选择)。`tenant_id` 必须出现在复合索引的最左列。常用查询条件组合创建复合索引,遵循最左前缀原则。
|
||
|
||
### 3.2 t_order 表索引示例
|
||
|
||
```sql
|
||
-- 主键索引(自动)
|
||
-- 租户 + 订单号唯一索引
|
||
CREATE UNIQUE INDEX uk_order_tenant_no ON t_order(tenant_id, order_no);
|
||
|
||
-- 租户 + 状态(列表查询)
|
||
CREATE INDEX idx_order_tenant_status ON t_order(tenant_id, status);
|
||
|
||
-- 租户 + 创建时间(分页排序)
|
||
CREATE INDEX idx_order_tenant_create_time ON t_order(tenant_id, create_time DESC);
|
||
```
|
||
|
||
### 3.3 索引维护
|
||
|
||
定期检查未使用的索引:
|
||
|
||
```sql
|
||
SELECT schemaname, relname, indexrelname, idx_scan
|
||
FROM pg_stat_user_indexes
|
||
WHERE idx_scan = 0
|
||
ORDER BY relname;
|
||
```
|
||
|
||
---
|
||
|
||
## 4. Flyway 迁移规范
|
||
|
||
### 4.1 文件命名
|
||
|
||
```
|
||
V{版本号}__{描述}.sql -- 版本化迁移(不可修改)
|
||
R__{描述}.sql -- 可重复迁移(如视图、存储过程)
|
||
```
|
||
|
||
版本号格式:`V1__create_order_table.sql`、`V2__add_order_status_index.sql`。
|
||
|
||
描述使用下划线分隔的蛇形命名(snake_case)。
|
||
|
||
### 4.2 编写规则
|
||
|
||
每个迁移文件只做一件事(一张表、一个索引、一次 ALTER)。所有 DDL 必须是幂等的(使用 `IF NOT EXISTS`、`IF EXISTS`)。禁止在迁移中使用 `DROP TABLE`(除非有审批的变更单号且附带数据备份计划)。DML 迁移(数据修复)必须包含 `WHERE` 条件,禁止无条件 `UPDATE` / `DELETE`。
|
||
|
||
### 4.3 不可逆性说明
|
||
|
||
Flyway 迁移是 forward-only 的。一旦迁移文件被合并到 `main` 分支并在任何环境执行过,就不得修改其内容(会导致 checksum 不匹配)。
|
||
|
||
若需要回退 schema 变更,必须创建新的迁移文件(新版本号)执行反向 DDL。建议采用 expand-then-contract 模式:先在新迁移中添加新列 → 部署新代码 → 确认稳定后在后续迁移中移除旧列。
|
||
|
||
### 4.4 迁移测试
|
||
|
||
所有迁移脚本在 CI 中通过 Testcontainers 验证:自动启动 PostgreSQL 15 容器 → 应用全部迁移 → 运行集成测试。参见 [testing.md](testing.md)。
|
||
|
||
---
|
||
|
||
## 5. 连接池配置
|
||
|
||
| 参数 | dev | staging | prod | 说明 |
|
||
|------|-----|---------|------|------|
|
||
| `maximumPoolSize` | 5 | 10 | 20 | 最大连接数 |
|
||
| `minimumIdle` | 2 | 5 | 10 | 最小空闲连接 |
|
||
| `connectionTimeout` | 30000ms | 20000ms | 10000ms | 获取连接超时 |
|
||
| `idleTimeout` | 600000ms | 300000ms | 300000ms | 空闲连接存活时间 |
|
||
| `maxLifetime` | 1800000ms | 1800000ms | 1800000ms | 连接最大生命周期 |
|
||
|
||
> **注意**:`maximumPoolSize` × Pod 副本数不得超过数据库 `max_connections`。
|
||
> 建议预留 20% 给运维工具和 Flyway 迁移。
|
||
|
||
---
|
||
|
||
## 6. 数据备份策略(建议)
|
||
|
||
| 类型 | 频率 | 保留期 |
|
||
|------|------|--------|
|
||
| 全量备份 (pg_dump) | 每日 | 30 天 |
|
||
| WAL 归档 | 持续 | 7 天 |
|
||
| 逻辑备份 (关键表) | 每周 | 90 天 |
|
||
|
||
建议使用 PgBackRest 或云厂商托管备份服务。恢复演练至少每季度执行一次。 |