# mysql开发规约


<!--more-->


**可以查看阿里的mysql开发规约**

## 1. 建表规约

- 【强制】表达是与否概念的字段，必须使用is_xxx的方式命名，数据类型是unsignedtinyint （1表示是，0表示否）。

  说明：任何字段如果为非负数，必须是 unsigned

  注意：POJO 类中的任何布尔类型的变量，都不要加 is 前缀，所以，需要设置从 is_xxx 到 Xxx 的映射关系

- 【强制】表名、字段名必须使用小写字母或数字，禁止出现数字开头，禁止两个下划线中间只出现数字

- 【强制】表名不使用复数名词。

- 【强制】禁用保留字，如 desc、range、match、delayed 等，请参考 MySQL 官方保留字。

- 【强制】主键索引名为 pk字段名；唯一索引名为 *uk*字段名；普通索引名则为 idx_字段名。

  说明：pk_ 即 primary key；uk_ 即 unique key；idx_ 即 index 的简称。

- 【强制】小数类型为 decimal，禁止使用 float 和 double。

- 【强制】如果存储的字符串长度几乎相等，使用 char 定长字符串类型。

- 【强制】varchar 是可变长字符串，不预先分配存储空间

  长度不要超过 5000，如果存储长度大于此值，定义字段类型为 text，独立出来一张表，用主键来对应，避免影响其它字段索引效率

-  【强制】表必备三字段：id, gmt_create, gmt_modified。

- 【推荐】表的命名最好是加上“业务名称_表的作用”。

- 【推荐】库名与应用名称尽量一致。

- 【推荐】如果修改字段含义或对字段表示的状态追加时，需要及时更新字段注释。

- 【推荐】字段允许适当冗余，以提高查询性能，但必须考虑数据一致。

  不是频繁修改的字段; 不是 varchar 超长字段，更不能是 text 字段

- 【推荐】单表行数超过 500 万行或者单表容量超过 2GB，才推荐进行分库分表。

- 【参考】合适的字符存储长度，不但节约数据库表空间、节约索引存储，更重要的是提升检索速度

  ![示意图](https://img.zhaojq.top/20260727091650522.png "示意图")



## 2. 索引规约

- 【强制】业务上具有唯一特性的字段，即使是多个字段的组合，也必须建成唯一索引。

  说明：不要以为唯一索引影响了 insert 速度，这个速度损耗可以忽略，但提高查找速度是明显的；另外，即 使在应用层做了非常完善的校验控制，只要没有唯一索引，根据墨菲定律，必然有脏数据产生。

- 【强制】三个表以上禁止 join。

  需要 join 的字段，数据类型必须绝对一致；多表关联查询时，保证被关联的字段需要有索引。说明：即使双表 join 也要注意表索引、SQL 性能。

- 【强制】在 varchar 字段上建立索引时，必须指定索引长度，没必要对全字段建立索引，根据实际文本区分度决定索引长度即可。

  说明：索引的长度与区分度是一对矛盾体，一般对字符串类型数据，长度为 20 的索引，区分度会高达 90%以上，可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度来确定。

- 【强制】页面搜索严禁左模糊或者全模糊，如果需要请走搜索引擎来解决

- 【推荐】如果有 order by 的场景，请注意利用索引的有序性

  正例：where a=? and b=? order by c; 索引：a_b_c

  反例：索引中有范围查找，那么索引有序性无法利用，如：WHERE a>10 ORDER BY b; 索引a_b 无法排序。

- 【推荐】利用覆盖索引来进行查询操作，避免回表。

  正例：能够建立索引的种类分为主键索引、唯一索引、普通索引三种，而覆盖索引只是一种查询的一种效果，用 explain 的结果，extra 列会出现：using index

- 【推荐】利用延迟关联或者子查询优化超多分页场景。

  说明：MySQL 并不是跳过 offset 行，而是取 offset+N 行，然后返回放弃前 offset 行，返回N 行，那当 offset 特别大的时候，效率就非常的低下，要么控制返回的总页数，要么对超过特定阈值的页数进行 SQL 改写。

  正例：先快速定位需要获取的 id 段，然后再关联：`SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id`

- 【推荐】SQL 性能优化的目标：至少要达到 range 级别，要求是 ref 级别，如果可以是 consts最好。

  - consts 单表中最多只有一个匹配行（主键或者唯一索引），在优化阶段即可读取到数据
  - ref 指的是使用普通的索引（normal index）。
  - range 对索引进行范围检索。
  - 反例：explain 表的结果，type=index，索引物理文件全扫描，速度非常慢，这个 index 级别比较 range 还低，与全表扫描是小巫见大巫。

- 【推荐】建组合索引的时候，区分度最高的在最左边

  正例：如果 where a=? and b=? ，如果 a 列的几乎接近于唯一值，那么只需要单建 idx_a索引即可。

  说明：存在非等号和等号混合时，在建索引时，请把等号条件的列前置。如：where c>? and d=? 那么即使 c 的区分度更高，也必须把 d 放在索引的最前列，即索引 idx_d_c。

- 【推荐】防止因字段类型不同造成的隐式转换，导致索引失效。

-  【参考】创建索引时避免有如下极端误解

  - 宁滥勿缺。认为一个查询就需要建一个索引。
  - 宁缺勿滥。认为索引会消耗空间、严重拖慢更新和新增速度。
  - 抵制惟一索引。认为业务的惟一性一律需要在应用层通过“先查后插”方式解决。

## 3. sql语句

- 【强制】不要使用 count(列名)或 count(常量)来替代 count(*)*，*count(*)是 SQL92 定义的标准统计行数的语法， 跟数据库无关，跟 NULL 和非 NULL 无关。

  说明：count(*)会统计值为 NULL 的行，而 count(列名)不会统计此列为 NULL 值的行。

- 【强制】count(distinct col) 计算该列除 NULL 之外的不重复行数，注意 count(distinct col1, col2)如果其中一 列全为 NULL，那么即使另一列有不同的值，也返回为 0。 

- 【强制】当某一列的值全是 NULL 时，count(col)的返回结果为 0，但 sum(col)的返回结果为NULL，因此使用 sum()时需注意 NPE (Null Pointer Exception)问题。

  正例：可以使用如下方式来避免 sum 的 NPE 问题：`SELECT IF(ISNULL(SUM(g)),0,SUM(g))FROM table;`

- 【强制】使用 ISNULL()来判断是否为 NULL 值。

  说明：NULL 与任何值的直接比较都为 NULL。

  -  NULL<>NULL 的返回结果是 NULL，而不是 false
  - NULL=NULL 的返回结果是 NULL，而不是 true
  -  NULL<>1 的返回结果是 NULL，而不是 true

-  【强制】在代码中写分页查询逻辑时，若 count 为 0 应直接返回，避免执行后面的分页语句

- 【强制】不得使用外键与级联，一切外键概念必须在应用层解决

  外键与级联更新适用于单机低并发，不适合分布式、高并发集群；级联更新是强阻塞，存在数据库更新风暴的风险；外键影响数据库的插入速度。

- 【强制】禁止使用存储过程，存储过程难以调试和扩展，更没有移植性

- 【强制】数据订正（特别是删除、修改记录操作）时，要先 select，避免出现误删除，确认无误才能执行更新 语句。

- 【推荐】in 操作能避免则避免，若实在避免不了，需要仔细评估 in 后边的集合元素数量，控制在1000 个内

- 【参考】如果有国际化需要，所有的字符存储与表示，均以 utf-8 编码，注意字符统计函数的区别。

  SELECT LENGTH("轻松工作")； 返回为 12

  SELECT CHARACTER_LENGTH("轻松工作")； 返回为 4

  如果需要存储表情，那么选择 **utf8mb4** 来进行存储，注意它与 utf-8 编码的区别。

- 【参考】TRUNCATE TABLE 比 DELETE 速度快，且使用的系统和事务日志资源少，但 TRUNCATE无事务且不 触发 trigger，有可能造成事故，故不建议在开发代码中使用此语句。



## 4. ORM映射

-  【强制】在表查询中，一律不要使用 * 作为查询的字段列表，需要哪些字段必须明确写明。
-  【强制】POJO 类的布尔属性不能加 is，而数据库字段必须加 is_，要求在 resultMap 中进行字段与属性之间的映射。
-  【强制】不要用 resultClass 当返回参数，即使所有类属性名与数据库字段一一对应，也需要定义；反过来，每一个表也必然有一个 POJO 类与之对应。
-  【强制】sql.xml 配置参数使用：#{}，#param# 不要使用${} 此种方式容易出现 SQL 注入
-  【强制】iBATIS 自带的 queryForList(String statementName,int start,int size)不推荐使用。
-  【强制】不允许直接拿 HashMap 与 Hashtable 作为查询结果集的输出。
- 【强制】更新数据表记录时，必须同时更新记录对应的 gmt_modified 字段值为当前时间。
-  【推荐】不要写一个大而全的数据更新接口。传
-  【参考】@Transactional 事务不要滥用。
- 【参考】中的 compareValue 是与属性值对比的常量，一般是数字，表示相等时带上此条件；表示不为空且不为 null 时执行；表示不为 null 值时执行。



> Tips: 优化总结
>
> - 开启慢查询日志，定位运行慢的SQL语句
> - 利用explain执行计划，查看SQL执行情况
> - 关注索引使用情况：type 
> - 关注Rows：行扫描
> - 关注Extra：没有信息最好
> - 加索引后，查看索引使用情况，index只是覆盖索引，并不算很好的使用索引如果有关联尽量将索引用到eq_ref或ref级别
> - 复杂SQL可以做成视图，视图在MySQL内部有优化，而且开发也比较友好
> - 对于复杂的SQL要逐一分析，找到比较费时的SQL语句片段进行优化


