SQL 查询优化
关系数据库为何适合关联查询?
- 关系模型: 用行列表示实体属性,用显式关系连接数据,查询可以重新组合表而不改变原始布局;
- SQL: 描述想要什么结果,由数据库选择执行方案,方便表达过滤、联接和聚合;
- 事务约束: 常见关系数据库支持约束和事务,帮助应用维护业务规则,相关保证见 事务;
- 优点: 通用查询、成熟管理工具和明确模式使数据易于理解和访问;
- 代价: 复杂联接、模式迁移与大规模分布式写入可能增加性能和维护成本;
- 原文示例: PostgreSQL、MySQL、MariaDB、Amazon Aurora,选型仍需依据具体负载;
物化视图怎样减少重复计算?
- 物化视图: 把查询结果提前计算并存储,下次读取结果而不是重复扫描、联接和聚合;
- 适用条件: 相同复杂查询频繁执行,而且业务可以接受定义清楚的刷新时效;
- 性能收益: 用存储和维护开销换取更快的查询,必要时还能只暴露数据子集并减少传输;
- 新鲜度代价: 底表变更后视图需要刷新,刷新失败或延迟会让结果落后;
- 与普通视图区别: 普通视图通常主要保存查询定义,物化视图保存结果,不能只因名称带“视图”就认为性能相同;
订单明细 → 定期或增量聚合 → 每日销售额物化结果
报表请求 → 读取物化结果
N+1 查询为什么会拖慢页面?
- N+1: 先查询 N 个对象,再为每个对象分别查询关联数据,导致一次页面请求触发 1+N 次数据库查询;
- 典型来源: ORM 懒加载或 GraphQL resolver 逐个取关联实体,而没有合并访问;
- 代价来源: 即使每次查询不慢,额外网络往返、解析和连接占用仍会累加;
- 识别方法: 观察一次请求的 SQL 数量和重复结构,不能只看单条 SQL 的耗时;
如何用批量加载消除 N+1?
- 批量加载: 收集本次需要的关联 ID,一次取回并在内存按 ID 组装结果;
- 联接选择: 当结果形状合适时也可用联接或预加载减少请求,但要避免一对多展开造成大量重复行;
- DataLoader: 把同一轮 resolver 请求合并成批次,可同时做请求范围内的去重;
- 隔离边界: 批量或缓存不能跳过用户权限检查,也不应把不同用户的授权结果无条件共享;