1. 建表规约

  1. 必须使用InnoDB表。

说明:InnoDB引擎可以提供类似Oracle的性能和数据安全性;
禁止:禁止使用MyISAM表。

  1. 表一定要有主键索引,且主键索引应该是业务主键,不要使用MySQL自增主键,不要使用超过64字符的字符串。

说明:主键用来约束唯一的记录,便于数据的管理,业务主键避免了自增主键不同机房的同步问题,可以实现全局唯一;
建议:尽量使用整型来充当主键,这样会在索引组织表上获得更高的效率;
禁止:不要使用超过64字符的字符串作为主键索引,MySQL对字符串索引的长度有要求,且和字符集相关。

  1. 表名,索引名不要超过30个字符。

说明:过长的表名可读性较差,命名做到见名知意即可,亦有中文注释作为补充。

  1. 数据表采用T开头,表示为数据表,视图以V开头,表示为视图。命名应该符合下面的范式:[T/V][Service][BusiDesc]。

说明:Service代表此表或者视图属于何种微服务,BusiDesc表示该表的具体用途;
示例:T_User_UserInfo,表示User服务的用户信息表;
建议:BusiDesc尽量使用短名称但要表意明确,单词之间用开头用大写字母。

  1. 字段名采用驼峰式命名,但是首字母要大写,禁止出现数字开头。
  2. 禁止使用MySQL保留字,具体的保留字参考MySQL官方文档。

说明:https://dev.mysql.com/doc/refman/5.7/en/keywords.html
反例:使用Desc作为Description的简写,Desc是MySQL的保留字。

  1. 主键索引的命名使用P开头加字段名称的方式;唯一键命名采用U开头加字段名称的方式;普通索引命名采用I_开头加字段名称的方式。
  2. 对于可以确定长度的字符串,使用char(N),不要使用varchar(N)。

说明:char是定长字段,适用于长度固定的字段,且符合最小适用原则;varchar类型需要额外占用一部分的空间作为变长的的说明,因此不适用于定长的字段。
正例:性别一般只有两种,M或者F,考虑使用char(1);
反例:对于上述定长的性别列,使用varchar(1)。

  1. varchar字段长度不要超过4000,超过4000考虑使用blob。

说明:blob字段属于行溢出字段,非超长情况下不要使用。

  1. 保证单一属性原则。

说明:一个字段内只存一种信息,保证单一属性;
示例:订单的用户信息,如果一个订单对应多个用户,要建立一张订单号和用户id的对应关系表;
反例:新建一个字段,用于保存所有的用户id,id之间用逗号分隔。

  1. 库名和服务名称尽量一致。
  2. 每个字段都要有汉字注释。
  3. 库默认采用utf8mb4字符集,没有特殊情况不要自定义表和字段的字符集。

说明:考虑到向前兼容的需求,将默认字符集设置为MySQL8的默认字符集utf8mb4。**

  1. 不必完全采用第三范式设计表,允许以适当的冗余提升查询效率。

说明:完全的第三范式设计不会有任何冗余字段,但是有时候对于简单的业务不需要关联的,表内冗余一些信息反而更加高效。

  1. 保证最小适用原则,字段类型选择时选择能正常使用的最小的字段。

示例:人的寿命一般在100岁左右,可以使用无符号的tinyint来表示人的年龄,区间范围从0到255。
反例:表示是否的字段只有0或者1,字段类型创建为bigint。

  1. 不创建外键,外键会让删除操作变得极为困难。
  2. 默认值不要使用空字符串,空字符串可以认为是null。

示例:如果预见到该字段可能出现空值,则允许此列为空;
反例:默认值设置为’’,加上非空约束。

  1. 考虑使用触发器之类的工具实现表的实时更新,取代视图的使用。

说明:视图查询的效率低于表,MySQL没有实现物化视图,可以利用触发器实现类似于物化视图的表;
建议:查询较为简单的时候,可以将查询封装成视图,简化Java代码的编写难度。
禁止:特别复杂的查询禁止封装成视图。

  1. 不要使用MySQL专有的数据类型,如text等。

说明:要考虑到代码未来存在平台移植的可能,要是用标准的类型。

2. 索引规约

  1. 可以确定唯一记录的列,必须组成唯一索引。

说明:唯一索引的好处是显然的,可以提供接近主键索引的查询速度,且能够有效地提升并发能力,降低CPU占用。

  1. 关联查询的关联条件,应该保证类型一致,如果是字符串类型,则应该保证字符集一致。

说明:关联条件的类型不同,会导致隐式类型转换,无法利用到既有索引,字符集不一致也存在该问题

  1. 建议在varchar字段上建立部分索引,即指定只索引字段的前N个字符。

说明:大部分的varchar字段前20个字符就能区分开不同的记录,因此没有必要对整个字段进行索引,缩短索引长度也能提高效率。

  1. 只检索需要的列,尽量避免select *的使用。

说明:业务查询一般是固定的,对于一些查询实际上可以利用到索引覆盖技术,减少IO消耗,这要求查询的列是索引中的索引列,具体的索引覆盖技术可以参考《高性能MySQL》相关章节。

  1. 分页尽量使用预读来提升效率。

说明:limit 100,10并不是从第100条记录开始读取10条,而是读取110条记录,选择最后10条,因此分页效率很低,建议在业务端采用预读方式进行优化。

  1. 建立索引的时候,选择度最高的列放在最左边。

说明:B+树索引对于选择度高的列更有效,而联合索引的组织顺序合理可以有效地降低树的高度,降低IO成本

  1. 建立索引的时候掌握最左前缀原则,设计合理的组合索引以减少索引冗余。

说明:根据最左前缀原则,一个建立在a,b,c列上的联合索引(a,b,c),可以满足以下几种查询:where a=? and b=? and c=?,where a=? and b=?,where a=?。

3. SQL规约

  1. 除非业务需要,不要使用count(列名),这样会忽略掉这个列为null的行,造成统计结果不准确。

说明:count(*)效率很高,优化器会自动选择辅助索引进行优化,不存在效率问题。

  1. 正确区分inner join和left join,非必要情况下不要使用左连接,会出现效率问题。

说明:左连接会做一次笛卡尔积,然后将悬浮元组进行筛选后得到结果,比单纯的集合交运算复杂且低效。

  1. 避免在in子句内使用大量的元素。

说明:in子句会被优化器改写成union all或者or,效率非常低。

  1. 避免派生表的使用,用join取代派生表。

说明:所谓派生表查询select a.x, b.y from a inner join (select y from c) b on a.y = b.y; 这个SQL实际上可以改写成select a.x, b.y from a inner join c where a.y = c.y; MySQL在对派生表的处理上效率并不高。

  1. 删除全表数据采用truncate不要用delete,truncate可以在瞬间删除大表的所有数据,delete要写日志,效率较低。

说明:truncate语句不会产生undo信息,因此没有办法进行数据的回滚,要格外注意。

  1. 不要使用自定义函数。

说明:自定义函数可能会出现因为测试不充分暴露的bug,大部分自定义函数都有性能问题。

  1. 对于必须要写出的完成业务使用的特别复杂的SQL,要考虑业务设计是否合理。
  2. 除了OLAP应用之外,OLTP禁止使用存储过程。
  3. 不建议使用左模糊或者全模糊查询,这样不会用到索引。
  4. 建议使用SQLyog等工具编写SQL。

    4. Java开发数据库相关代码规约

  5. 使用JPA编写查询语句时,同样要注意不要使用*去查询所有的字段,需要查哪些则写哪些。

  6. 使用绑定变量,禁止使用拼接字符串形式。

说明:代码中的变量部分应该采用占位符,例如jdbc代码使用?作为占位符,HQL使用:{param}作为占位符
反例:定义这样的SQL字符串:String sql = “select a, b from t where c = “ + in_param;

  1. 不要随意更新查询的结果类。

说明:更新查询结果类中的属性,会导致事务结束时自动更新这条记录。

  1. 在正确的位置使用@Transactional注解。

说明:事务是一个重要的概念,要考虑哪些操作需要放在一个事务中,谨慎的使用该注解。

  1. Hibernate会将tinyint(1)视为boolean型,定义表时应注意,一般的非boolean需求,定义为tinyint(4).
  2. 一个方法仅实现一个功能。