完善数据库 SQL 约束与运维治理
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# 数据库 SQL 约束与运维
|
||||
|
||||
本项目使用 MySQL 8.4、GORM 和 Goose。本文是提交、上线和故障排查的数据库基线;SQL 静态检查不能替代真实数据量下的执行计划。
|
||||
|
||||
## 提交前约束
|
||||
|
||||
新增或修改 SQL、GORM 查询或迁移必须满足:
|
||||
|
||||
1. 列表查询必须有分页、最大数量或明确日期范围,禁止无界读取订单、流水、消息和日志历史。
|
||||
2. `GROUP BY`、`COUNT(DISTINCT)`、窗口函数、跨表聚合、`UNION` 和相关子查询必须在接近生产数据量的 MySQL 上保存 `EXPLAIN FORMAT=JSON` 或 `EXPLAIN ANALYZE` 结果。
|
||||
3. 高频查询先限定当前时间范围或当前页 ID,再做关联和聚合;禁止读取用户全部历史记录后在应用层过滤。
|
||||
4. 新增索引必须通过新的 Goose migration 提交,并在迁移注释中说明覆盖的查询条件、排序和基数;大表索引要评估锁影响和上线窗口。
|
||||
5. SQL 值必须使用 GORM 参数绑定或 `?` 占位符,禁止拼接请求参数。动态排序字段只能来自白名单。
|
||||
6. 已应用的迁移文件禁止修改或删除,只能新增迁移完成结构修复;除 `000001_init.sql` 外的新迁移必须包含 `-- +goose Up` 和 `-- +goose Down`。
|
||||
7. 角色、权限、推送渠道、客服组等小型配置表允许全量读取,但必须保持数量边界或在本文件的白名单中登记;业务数据不得套用该例外。
|
||||
|
||||
本地 `scripts/check.sh` 会执行 `scripts/check-sql-guard.sh`,保护关键索引、时间范围和迁移完整性。它只防止关键代码被重构移除,不判断执行计划是否优秀。
|
||||
|
||||
## 执行计划
|
||||
|
||||
在维护库或只读副本上替换真实参数后执行:
|
||||
|
||||
```sql
|
||||
EXPLAIN FORMAT=JSON
|
||||
SELECT ...;
|
||||
|
||||
EXPLAIN ANALYZE
|
||||
SELECT ...;
|
||||
```
|
||||
|
||||
重点检查扫描行数、实际返回行数、索引使用、临时表、文件排序和 join 顺序。财务首页的仪表盘、结算明细和提现合并列表属于必须定期复核的查询。
|
||||
|
||||
## 生产观测
|
||||
|
||||
生产 MySQL 应保持 `performance_schema` 开启,并启用慢查询日志。项目的 `deploy/mysql/my.cnf` 默认记录超过 500ms 的语句;应用日志只记录 SQL 形状、耗时和返回行数,不记录绑定参数值。
|
||||
|
||||
常用排查 SQL:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
DIGEST_TEXT,
|
||||
COUNT_STAR,
|
||||
ROUND(SUM_TIMER_WAIT / 1000000000000, 2) AS total_seconds,
|
||||
ROUND(AVG_TIMER_WAIT / 1000000000, 2) AS avg_ms,
|
||||
SUM_ROWS_SENT,
|
||||
SUM_ROWS_EXAMINED
|
||||
FROM performance_schema.events_statements_summary_by_digest
|
||||
ORDER BY SUM_TIMER_WAIT DESC
|
||||
LIMIT 30;
|
||||
|
||||
SELECT THREAD_ID, EVENT_NAME, TIMER_WAIT, SQL_TEXT, ROWS_EXAMINED
|
||||
FROM performance_schema.events_statements_current
|
||||
WHERE SQL_TEXT IS NOT NULL
|
||||
ORDER BY TIMER_WAIT DESC;
|
||||
```
|
||||
|
||||
应用慢查询阈值由 `DATABASE_SLOW_QUERY_THRESHOLD_MS` 配置。阈值调整后要同步检查 MySQL `long_query_time`,避免应用和数据库观测口径长期不一致。
|
||||
|
||||
## 连接、超时与容量
|
||||
|
||||
- `DATABASE_MAX_OPEN_CONNS` 是单个 backend 实例的连接上限,总量必须按实例数核算,不能简单随实例扩容同比增加。
|
||||
- `DATABASE_MAX_IDLE_CONNS`、`DATABASE_CONN_MAX_LIFETIME_MINUTES` 和 `DATABASE_CONN_MAX_IDLE_TIME_MINUTES` 控制空闲连接和连接轮换。
|
||||
- 请求必须设置 Go `context` 截止时间;锁等待由 MySQL `innodb_lock_wait_timeout` 控制。长报表查询应进入异步任务或汇总表。
|
||||
- 数据量增长后,排行榜、财务历史统计和审计报表应迁移到日汇总表、缓存或只读副本,不能持续依赖在线全表聚合。
|
||||
- 修改连接池或容器资源后,用 `docker compose config` 核对最终插值;资源限额应高于稳定 RSS 峰值并留出数据库连接和备份余量。
|
||||
|
||||
## 备份与恢复
|
||||
|
||||
生产使用 XtraBackup 全量备份加 MySQL ROW binlog 归档实现时间点恢复。备份对象先客户端加密,再使用独立 OSS bucket 的服务端加密;恢复必须在隔离目录和隔离 MySQL 容器演练,禁止直接向生产实例回放。
|
||||
|
||||
```bash
|
||||
./scripts/backup-online.sh status
|
||||
./scripts/archive-binlog.sh verify
|
||||
./scripts/restore-online.sh prepare <complete.env-OSS-URI>
|
||||
```
|
||||
|
||||
备份完成标识、SHA-256、manifest、server UUID 和 Goose 版本必须一致;恢复演练至少按月验证一次,并记录恢复时间点和结果。
|
||||
Reference in New Issue
Block a user