增加数据库慢查询观测与索引防线并收窄打手拼单分页查询
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
# 数据库 SQL 约束与运维
|
||||
|
||||
## 提交前约束
|
||||
|
||||
新增或修改 SQL 必须满足:
|
||||
|
||||
1. 列表查询必须有明确的分页、最大数量或日期范围;禁止无界返回历史数据。
|
||||
2. `GROUP BY`、`COUNT(DISTINCT)`、窗口函数和跨表聚合必须附带 `EXPLAIN (ANALYZE, BUFFERS)` 结果。
|
||||
3. 高频接口禁止在公共 `SELECT` 中叠加未必要的 LATERAL、相关子查询或全表聚合。
|
||||
4. 当前页关联数据必须按当前页 ID 批量查询,不得读取用户全部历史记录再在应用层过滤。
|
||||
5. 新增索引必须通过数据库 migration 提交,并说明覆盖的查询条件;大表生产索引应评估锁影响。
|
||||
6. SQL 必须使用参数绑定,禁止把请求参数直接拼接到 SQL 值中。
|
||||
|
||||
后端 `npm run check` 会执行 `npm run check:sql`,检查关键性能防线没有被重构移除。该检查不能替代真实数据量下的 `EXPLAIN`。
|
||||
|
||||
## 生产观测
|
||||
|
||||
生产 PostgreSQL 应启用 `pg_stat_statements` 和慢查询日志。Compose 默认通过 `POSTGRES_LOG_MIN_DURATION_STATEMENT_MS`(默认 500ms)配置 PostgreSQL 原生慢查询日志;应用默认将执行超过 `DATABASE_SLOW_QUERY_THRESHOLD_MS`(默认 500ms)的 SQL 记录为慢查询,只记录 SQL 形状、耗时和返回行数,不记录参数值。
|
||||
|
||||
常用排查 SQL:
|
||||
|
||||
```sql
|
||||
SELECT calls, total_exec_time, mean_exec_time, rows, query
|
||||
FROM pg_stat_statements
|
||||
ORDER BY total_exec_time DESC
|
||||
LIMIT 30;
|
||||
|
||||
SELECT pid, state, wait_event_type, wait_event,
|
||||
now() - query_start AS duration, query
|
||||
FROM pg_stat_activity
|
||||
WHERE datname = current_database() AND state <> 'idle'
|
||||
ORDER BY query_start;
|
||||
```
|
||||
|
||||
## 容量边界
|
||||
|
||||
- `DATABASE_STATEMENT_TIMEOUT_MS` 防止单条 SQL 长时间占用连接。
|
||||
- `DATABASE_MAX_CONNECTIONS` 按后端实例数总量评估;扩容后端实例时不能简单地每实例增加连接数。
|
||||
- CPU 限额只能防止数据库拖垮整机,不能替代 SQL 优化。
|
||||
- 数据量持续增长后,排行榜和历史统计应迁移到汇总表、缓存或只读副本,不应继续依赖在线全量聚合。
|
||||
Reference in New Issue
Block a user