database-development-performance.mdc 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. ---
  2. description: 数据库开发与性能约束(SQL、分页、索引、事务、批量、慢 SQL、上线门禁)
  3. globs: "**/*.java,**/*.sql,**/mapper/**/*.xml"
  4. alwaysApply: false
  5. ---
  6. # 数据库开发与性能约束规范
  7. ## 1. 目的
  8. 为统一数据库相关开发标准,减少慢 SQL、锁等待、全表扫描、连接池耗尽等线上问题,制定本规范。所有涉及数据库访问的后端代码、SQL、Mapper、定时任务、数据同步任务、报表查询均应遵守本规范。
  9. ## 2. 适用范围
  10. 本规范适用于以下场景:
  11. - 新增、修改数据库表结构;
  12. - 新增、修改业务 SQL;
  13. - 新增、修改 MyBatis Mapper、JPA、JdbcTemplate 等数据库访问代码;
  14. - 数据导入、数据同步、批量更新、统计报表;
  15. - 定时任务、接口查询、后台管理查询;
  16. - 上线前 SQL 审查与性能评估。
  17. ## 3. 基本原则
  18. - 查询必须有明确边界,禁止无条件查询大表。
  19. - 接口 SQL 必须优先保证稳定响应时间,不允许把复杂离线统计放到在线接口实时计算。
  20. - 写入操作必须控制事务范围,禁止长事务。
  21. - 大数据量处理必须分页、分批、可恢复。
  22. - 代码中禁止拼接不可信 SQL,必须使用参数绑定。
  23. - 新增 SQL 必须考虑索引、执行计划和数据量增长。
  24. ## 4. SQL 编写约束
  25. ### 4.1 查询字段
  26. - 禁止在业务代码中使用 `SELECT *`。
  27. - 必须显式列出需要返回的字段。
  28. - 列表页、分页接口只返回页面需要展示的字段。
  29. - 大字段(`TEXT`、`CLOB`、大 JSON、二进制字段)不得出现在列表查询中,确需展示时应单独查询详情。
  30. 示例:
  31. ```sql
  32. -- 不允许
  33. SELECT * FROM tf_wrs_rainfall_b WHERE station_id = ?;
  34. -- 推荐
  35. SELECT id, station_id, rainfall_value, measure_time
  36. FROM tf_wrs_rainfall_b
  37. WHERE station_id = ?;
  38. ```
  39. ### 4.2 WHERE 条件
  40. - 大表查询必须带有高选择性条件,如主键、业务唯一键、时间范围、机构范围等。
  41. - 禁止对索引字段使用函数、表达式或隐式类型转换。
  42. - 禁止使用无法命中索引的前置模糊查询,如 `LIKE '%keyword'`、`LIKE '%keyword%'`。
  43. - `IN` 条件数量应控制在 200 个以内,超过时应使用临时表、批量分片或关联查询。
  44. - 所有逻辑删除表的查询默认必须包含 `is_delete = 0`。
  45. 示例:
  46. ```sql
  47. -- 不允许,字段上使用函数会影响索引
  48. WHERE DATE(create_time) = '2026-04-30'
  49. -- 推荐
  50. WHERE create_time >= '2026-04-30 00:00:00'
  51. AND create_time < '2026-05-01 00:00:00'
  52. ```
  53. ### 4.3 排序
  54. - 排序字段应尽量命中索引。
  55. - 大表禁止无条件 `ORDER BY create_time DESC`。
  56. - 分页查询中的排序字段必须稳定,建议追加主键作为二级排序。
  57. 示例:
  58. ```sql
  59. ORDER BY measure_time DESC, id DESC
  60. ```
  61. ### 4.4 聚合统计
  62. - 在线接口禁止对大表进行无边界 `COUNT`、`SUM`、`GROUP BY`。
  63. - 报表统计应优先使用汇总表、物化结果或离线任务。
  64. - `GROUP BY` 字段应结合业务场景建立合适索引。
  65. - 统计口径必须明确时间范围、机构范围、数据状态范围。
  66. ## 5. 分页规范
  67. ### 5.1 普通分页
  68. - 小数据量或后台管理场景可使用 `LIMIT/OFFSET`。
  69. - 大偏移量分页禁止直接使用超大 `OFFSET`。
  70. 示例:
  71. ```sql
  72. -- 不推荐,大偏移量性能差
  73. SELECT id, station_id, measure_time
  74. FROM tf_wrs_rainfall_b
  75. ORDER BY id
  76. LIMIT 20 OFFSET 1000000;
  77. ```
  78. ### 5.2 深分页
  79. 深分页应使用游标分页或基于主键的延续查询。
  80. ```sql
  81. SELECT id, station_id, measure_time
  82. FROM tf_wrs_rainfall_b
  83. WHERE id > ?
  84. ORDER BY id
  85. LIMIT 500;
  86. ```
  87. ### 5.3 分页返回
  88. - 接口分页必须限制 `pageSize` 最大值,建议不超过 500。
  89. - 导出接口不得复用普通分页接口一次性拉取全部数据。
  90. - 导出必须采用流式、分批或异步任务方式处理。
  91. ## 6. 索引使用约束
  92. ### 6.1 新增索引
  93. 新增索引前必须说明:
  94. - 支撑哪个接口或任务;
  95. - 对应 SQL 是什么;
  96. - 查询条件、排序字段、返回数据量;
  97. - 预计表数据量;
  98. - 是否会影响写入性能。
  99. ### 6.2 组合索引
  100. - 等值查询字段放前面;
  101. - 范围查询字段放后面;
  102. - 排序字段可结合查询条件放入组合索引;
  103. - 避免重复索引和左前缀重复索引。
  104. 示例:
  105. ```sql
  106. -- 查询条件
  107. WHERE station_id = ?
  108. AND measure_time >= ?
  109. AND measure_time < ?
  110. ORDER BY measure_time DESC
  111. -- 推荐索引
  112. CREATE INDEX idx_rainfall_station_time
  113. ON tf_wrs_rainfall_b (station_id, measure_time);
  114. ```
  115. ### 6.3 禁止项
  116. - 禁止为低区分度字段单独建索引,如 `is_delete`、`status`、`gender`。
  117. - 禁止在长字符串字段上直接建立完整索引。
  118. - 禁止盲目为每个查询字段都建单列索引。
  119. - 禁止上线未验证执行计划的新索引。
  120. ## 7. 事务约束
  121. - 事务中只放必须保持一致的数据操作。
  122. - 禁止在事务内调用第三方接口、文件上传、远程服务、消息等待、长时间计算。
  123. - 批量更新、批量删除必须分批提交。
  124. - 事务内查询数据量应可控,禁止锁住大范围数据后再逐条处理。
  125. - 涉及库存、余额、状态流转等并发修改时,必须明确锁策略。
  126. 示例:
  127. ```java
  128. // 不推荐:事务内调用远程接口
  129. @Transactional
  130. public void updateAndNotify() {
  131. repository.updateStatus(id);
  132. remoteClient.notify(id);
  133. }
  134. ```
  135. ## 8. 批量处理约束
  136. ### 8.1 批量查询
  137. - 禁止一次性加载大量数据到内存。
  138. - 批量任务必须分页读取。
  139. - 每批数量建议控制在 500 到 5000,具体按数据大小和数据库压力调整。
  140. ### 8.2 批量写入
  141. - 使用批量插入或批量更新能力,避免循环单条 SQL。
  142. - 大批量写入必须分批提交。
  143. - 批量任务必须具备失败重试、断点续跑或幂等能力。
  144. ### 8.3 批量删除
  145. - 生产环境禁止直接大范围物理删除。
  146. - 逻辑删除也必须分批执行。
  147. - 历史数据清理必须先评估影响行数,并在低峰期执行。
  148. ## 9. 代码开发约束
  149. ### 9.1 Mapper 与 DAO
  150. - Mapper 方法名应表达查询意图,禁止使用 `queryList1`、`selectData` 等模糊命名。
  151. - Mapper 入参必须使用明确对象或 `@Param`,避免大量散乱参数。
  152. - 返回集合的方法必须有明确数据量控制。
  153. - 查询单条数据时必须使用唯一条件,并处理多条异常情况。
  154. ### 9.2 动态 SQL
  155. - 动态 SQL 必须避免条件缺失导致全表扫描。
  156. - 多条件查询至少应有一个核心过滤条件。
  157. - 可选条件全部为空时,应直接拒绝查询或返回空结果。
  158. - 排序字段不得直接接收前端原始字符串,必须使用白名单映射。
  159. 示例:
  160. ```java
  161. private static final Map<String, String> SORT_FIELD_MAP = Map.of(
  162. "measureTime", "measure_time",
  163. "createTime", "create_time"
  164. );
  165. ```
  166. ### 9.3 防 SQL 注入
  167. - 禁止使用字符串拼接构造用户输入条件。
  168. - MyBatis 中普通参数必须使用 `#{}`。
  169. - `${}` 仅允许用于经过白名单校验的表名、字段名、排序方向。
  170. 示例:
  171. ```xml
  172. <!-- 不允许 -->
  173. ORDER BY ${sortField} ${sortOrder}
  174. <!-- 推荐:代码中先做白名单映射后再传入 -->
  175. ORDER BY ${safeSortField} ${safeSortOrder}
  176. ```
  177. ## 10. 连接池与资源释放
  178. - 禁止手工创建数据库连接,统一使用项目连接池。
  179. - ResultSet、Statement、Connection 如手工使用必须正确关闭。
  180. - 慢任务不得长时间占用数据库连接。
  181. - 定时任务并发数必须受控,避免同时打满连接池。
  182. - 数据库连接池大小调整必须结合接口并发、SQL耗时和数据库承载能力评估。
  183. ## 11. 定时任务与报表约束
  184. - 定时任务必须限制单次处理数据量。
  185. - 统计报表应优先写入中间表或结果表,接口直接读取结果。
  186. - 定时任务必须避免与业务高峰写入时间冲突。
  187. - 任务必须记录开始时间、结束时间、处理数量、失败数量、耗时。
  188. - 任务重复执行必须幂等。
  189. ## 12. 慢 SQL 标准
  190. 满足以下任一条件,应按慢 SQL 处理:
  191. - 在线接口单条 SQL 超过 500ms;
  192. - 后台管理查询单条 SQL 超过 1s;
  193. - 报表统计单条 SQL 超过 3s;
  194. - SQL 扫描行数远大于返回行数;
  195. - SQL 执行计划出现大表全表扫描;
  196. - SQL 导致明显锁等待或连接池排队。
  197. 慢 SQL 必须记录:
  198. - SQL 原文;
  199. - 参数;
  200. - 表数据量;
  201. - 执行计划;
  202. - 扫描行数和返回行数;
  203. - 优化前后耗时对比。
  204. ## 13. 上线前检查清单
  205. 新增或修改数据库访问代码上线前,必须检查:
  206. - [ ] 是否禁止 `SELECT *`;
  207. - [ ] 是否具备必要 WHERE 条件;
  208. - [ ] 是否默认过滤 `is_delete = 0`;
  209. - [ ] 是否存在深分页问题;
  210. - [ ] 是否存在大事务或事务内远程调用;
  211. - [ ] 是否存在循环单条 SQL;
  212. - [ ] 是否存在无白名单的动态排序;
  213. - [ ] 是否验证执行计划;
  214. - [ ] 是否评估索引影响;
  215. - [ ] 是否提供大数据量处理方案;
  216. - [ ] 是否对导出、报表、定时任务做限流或分批;
  217. - [ ] 是否在预发布环境使用接近真实数据量验证。
  218. ## 14. 禁止上线项
  219. 以下问题未处理前禁止上线:
  220. - 无条件查询大表;
  221. - 大表无索引分页或排序;
  222. - 接口中实时执行大范围统计;
  223. - 事务内调用外部服务;
  224. - 循环逐条查询或逐条更新形成 N+1 SQL;
  225. - 前端参数直接拼接 SQL;
  226. - 批量任务无分页、无幂等、无失败恢复;
  227. - 删除或更新语句缺少明确范围条件;
  228. - 未经评估直接修改大表字段类型;
  229. - 高风险 DDL 无回滚或备份方案。
  230. ## 15. 评审要求
  231. 涉及数据库的代码评审必须关注:
  232. - SQL 是否可命中索引;
  233. - 数据量增长后是否仍可接受;
  234. - 事务范围是否合理;
  235. - 是否存在并发写入风险;
  236. - 是否存在 N+1 查询;
  237. - 是否存在不安全动态 SQL;
  238. - 是否有必要的日志和监控指标;
  239. - 是否符合上线前检查清单。