跳转至

04 数据库与索引

难度:中级|前置:系统思维|目标:从查询模式理解表、索引、事务和分页

数据库首先保存业务事实

关系数据库适合保存:

  • 有明确结构和约束的数据;
  • 需要事务的数据;
  • 需要长期追溯的数据;
  • 多种条件组合查询的数据。

Redis 很快,但不能因此把关键业务事实只放 Redis。

表设计从实体和关系开始

设备系统中常见实体:

tenant
device
device_credential
command
alarm
task

每张表都要问:

  • 主键是什么?
  • 哪些字段必须唯一?
  • 哪些状态变化需要历史?
  • 删除是物理删除还是逻辑删除?
  • 多租户条件是否始终存在?

索引不是“给常查字段都加上”

索引的价值由实际查询决定:

SELECT id, name
FROM devices
WHERE tenant_id = ?
  AND status = ?
ORDER BY id
LIMIT 100;

可能需要围绕 (tenant_id, status, id) 设计联合索引。字段顺序与等值过滤、范围条件和排序有关。

索引代价:

  • 占磁盘和缓存;
  • 插入更新需要维护;
  • 太多索引会降低写入性能;
  • 低选择性字段单独建索引未必有价值。

用 EXPLAIN 验证

不要凭感觉说“用了索引”。观察:

  • Seq Scan 还是 Index Scan;
  • 估算行数与实际行数差异;
  • 过滤掉多少行;
  • 排序是否额外发生;
  • 总耗时和 buffer 读取。

优化流程:

记录慢查询
→ 看执行计划
→ 判断扫描和排序成本
→ 调整查询、索引或数据模型
→ 用同样数据重新验证

事务解决什么

事务把多个数据库操作组织成一个一致的业务单元。ACID 并不意味着跨外部系统也自动一致。

例如:

数据库写入工单成功
→ 调用外部通知失败

这不能靠单个数据库事务完全解决,通常需要 Outbox、重试和幂等。

分页

OFFSET

简单,适合较浅页面,但偏移很大时数据库仍需跳过前面的行。

游标分页

WHERE tenant_id = ?
  AND id > ?
ORDER BY id
LIMIT 100

适合大数据量连续翻页,但需要稳定排序键,不能随意跳到任意页。

自测

  1. 为什么不能给所有字段都加索引?
  2. 联合索引顺序为什么与查询有关?
  3. 数据库事务为什么不能自动保证邮件只发一次?
  4. OFFSET 和游标分页分别适合什么场景?

完成标准

  • 能从一个真实 SQL 推导候选联合索引
  • 能解释索引的读写取舍
  • 能说出 EXPLAIN 至少四个观察点
  • 能区分数据库事务和跨系统一致性

延伸阅读:PostgreSQL 当前官方文档

下一单元:Redis 数据结构与选择