前言
从7月份毕业开始算起,也是将近有5个月的工作经验了吧。在工作上,经历了一段时间的适应,现在接触到不同大神写出来的代码,发现各有优劣,于是就在思考一个问题,什么样的代码才是最正常最规范的呢?我的同事甩给我了一本《阿里巴巴Java开发手册》,从头到尾也算是过了一遍。今天趁着双休的假期,我就来讲一下这本书里面的MySQL规范制约吧~
阿里巴巴开发规范之MySQL
建表规约
1、【强制】每张表必须设置一个主键ID,并且这个主键ID要自增(在满足需要的情况下尽量短),除非是分库分表
理解:由于InnoDB存储引擎决定了需要有一个主键,而且这个主键ID是自增的话可以有效提高插入的性能,避免过多的页分裂,减少表碎片提高空间的利用率。
但是在分库分表下,会有分片规则,这个时候需要统一分配各个表中的主键值,从而避免整个逻辑表中主键重复,一般我们会使用雪花ID来实现。
2、【强制】必须使用utf8mb4字符集
理解:在MySQL中的UTF-8并非是真正的"UTF-8",而utf8mb4才是真正的"UTF-8"。
3、【强制】数据库表、表字段必须加入中文注释
理解:程序员都不要给我懒,这样可以让后来者更好的上手和理解。
4、【强制】库名、表名、字段名必须小写,下划线风格,不超过32个字符,必须见名知意,禁止拼音英文混用
理解:在linux环境下MySQL是区分大小写的,而在windows是不区分大小写的,所以统一使用小写+下划线可以避免不必要的混淆。
5、【强制】单表列数目必须小于30,若超过则应该考虑将表拆分
理解:单表列数太多会使得MySQL处理InnoDB返回数据之间的映射成本太高。
6、【强制】禁止使用外键,如果有外键完整性约束,需要应用程序控制
理解:外键会导致表与表之间耦合,UPDATE与DELETE操作都会涉及相关联的表,十分影响SQL的性能,甚至会造成死锁。
7、【强制】必须把字段定义为NOT NULL并且提供默认值
理解:NULL需要更多的存储空,无论是表还是索引中每行中的NULL的列都需要额外的空间来标识。NULL这种类型需要MySQL内部进行特殊处理,增加了数据库处理记录的复杂性,同等条件下,表中较多NULL字段会导致数据库处理性能下降。
8、【强制】禁止使用保留字,如DESC、RANGE、MAX等
理解:请参考MySQL官方保留字。
9、【强制】如果存储的字符串长度几乎相等,使用CHAR定长字符串类型
理解:能够减少空间碎片,节省存储空间。
10、【强制】小数类型为 decimal,禁止使用 float 和 double
理解:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
11、【强制】表必备三字段:id, gmt_create, gmt_modified。
理解:其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。gmt_create,gmt_modified 的类型均为 datetime 类型,前者现在时表示主动创建,后者过去分词表示被动更新。
12、【推荐】在一些场景下,考虑使用TIMESTAMP代替DATETIME
理解:TIMESTAMP可以表达1970-2038年,而且TIMESTAMP需要4字节存储空间,而DATETIME需要8字节,存储1001-9999年
13、【推荐】对于自动生成的schema不要太过信任,最好自己手动写
理解:对于一些数据库客户端不要过分信任。
索引规约
1、【强制】业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引
理解:不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
2、【强制】超过三个表禁止 join。需要 join 的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引
理解:多表的join操作会影响SQL的性能
3、【强制】在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度即可
理解:索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分度会高达 90%以上,可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度来确定。
4、【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决
理解:索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
5、【推荐】如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能
理解:组合索引的顺序会影响到SQL的查询性能
6、【推荐】利用覆盖索引来进行查询操作,避免回表
理解:覆盖索引只需要通过索引即可拿到所需的数据,而不再需要再次回表查询,提高了SQL的查询效率。
7、【推荐】利用延迟关联或者子查询优化超多分页场景
理解:MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回N 行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。
8、【推荐】建组合索引的时候,区分度最高的在最左边
理解:区分度最高的放左边,能够在一开始过滤掉很多无用数据,提高索引的效率。需要注意的是各个条件的顺序尽量和索引的顺序一致。
9、【推荐】防止因字段类型不同造成的隐式转换,导致索引失效
理解:详细细节可以参考这篇文章哦~MySQL索引(三)索引不生效的情况
SQL规约
1、【强制】不要使用 count(列名)或 count(常量)来替代 count(*),count(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关
理解:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
2、【强制】使用 ISNULL()来判断是否为 NULL 值
理解:NULL与任何值直接比较都为NULL。
- NULL<>NULL返回结果是NULL,而不是false。
- NULL=NULL返回结果是NULL,而不是false。
- NULL&1返回结果是NULL,而不是true。
3、【强制】在代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句
理解:提高SQL的效率,避免不必要的无用查询。
4、【强制】不得使用外键与级联,一切外键概念必须在应用层解决
理解:外键会导致表与表之间耦合,UPDATE与DELETE操作都会涉及相关联的表,十分影响SQL的性能,甚至会造成死锁。
5、【强制】禁止使用存储过程,存储过程难以调试和扩展,更没有移植性
理解:避免不必要的维护。
6、【推荐】TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE无事务且不触发 trigger,有可能造成事故,故不建议在开发代码中使用此语句。
理解:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。
参考
- 《阿里巴巴Java开发手册》
- 实际工作结合《阿里巴巴Java开发手册》得出的理解