一、mysql数据库开发规范
MySQL 数据库
(一) 建表规约
1.【强制】表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
说明:MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。因此,数据库名、表名、字段名,都不允许出现任何大写字母,避免节外生枝。
正例:ds_user
反例:dsdmin
2.【强制】表名不使用复数名词。
说明:表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数形式,符合表达习惯
3.【强制】表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint(1 表示是,0 表示否)。
说明:任何字段如果为非负数,必须是 unsigned。
额外说明:
1) unsigned 指无符号,就是没有负数,只能用正数和0
2) tinyint 字段能表示的范围是:-128~127
3) zerofill 指的一个整型字段在允许的最大位数之前用0填充
无论是int或是tinyint等整型数据,括号里面的数字只是代表这个数据最后的显示位数,不影响数据本身的存储大小。就是说int(1)和int(4)数据存储长度一样,int(1)还是会比tinyint(8)存储空间
占用大。
tinyint(1)与tinyint(4) :在占用空间大小上没区别,都是一个字节
int(1)与int(4) :在占用空间大小上没区别,都是四个字节
int(1)比tinyint(4)占用空间大,但是对于zerofill修饰的字段,最终显示出来的tinyint(4)都是以4个数字(前面补0)呈现出来,而int(1)是不用0补齐来呈现的
tinyint一个字节 smallint 两个字节 MEDIUMINT三个字节 int 4个字节 BIGINT 8个字节,因此在选择字段类型的时候,在够用的情况下,能选择小的就尽量选择小的,节省更多的空间
int(6)与varchar(6)这两个6所表示的含义是不一样的,int(6)只会影响zerofill字段最终的显示长度,而varchar(6)会要求字符串存储的长度不超过6字节
各种字段类型在数据库存放的空间占用大小如下表
数据类型 默认范围 unsigned范围
tinyint -2^7 ~ 2^7-1 0 ~ 2^8-1
smallint -2^15 ~ 2^15-1 0 ~ 2^16-1
MEDIUMINT -2^23 ~ 2^23-1 0 ~ 2^24-1
int -2^31 ~ 2^31-1 0 ~ 2^32-1
bigint -2^63 ~ 2^63-1 0 ~ 2^64-1
注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀,所以,需要在<resultMap>设置从 is_xxx 到 Xxx 的映射关系。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的命名方式是为了明确其取值含义与取值范围。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。
4.【强制】禁用保留字,如 desc、range、match、delayed 等,请参考 MySQL 官方保留字。
5.【强制】主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
说明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称。
6.【强制】小数类型为 decimal,禁止使用 float 和 double。
说明:在存储的时候,float 和 double 都存在精度损失的问题,很可能在比较值的时候,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数并分开存储。
7.【推荐】如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
8.【强制】varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
9.【强制】表必备三字段:id, create_time, update_time。
说明:其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。create_time, update_time 的类型均为 datetime 类型。
示列:`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
10. 【强制】表的命名最好是遵循“应用名称_业务名称_表的作用”。
说明:
**商场:sc_模块_表作用表
正例:sc_pay_task
11. 【推荐】库名与应用名称尽量一致。
12. 【推荐】如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。
13. 【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
1) 不是频繁修改的字段。
2) 不是 varchar 超长字段,更不能是 text 字段。
3) 不是唯一索引的字段。
正例:商品类目名称使用频率高,字段长度短,名称基本一不变,可在相关联的表中冗余存储类目名称,避免关联查询。
14. 【推荐】单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
15. 【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。
正例:如下表,其中无符号值可以避免误存负数,且扩大了表示范围。
16. 【强制】核心业务数据,只能做逻辑删除,不能做物理删除。
17. 【推荐】 MySQL列中尽量避免NULL,应该指定列为NOT NULL,除非你想存储NULL。在MySQL中,含有空值的列很难进行查询优化。因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值。
18.【强制】所有的SQL语句必须要有注释,注释使用“--空格”,注释内容:SQL语句说明-版本号-工号;
说明:-- table新增字段-20200101-工号
19.【强制】建表语句,表级别的comment内容必须增加版本及修改人信息,形如:表中文名称-版本-工号;字段的新增或修改comment注释必须有中文名称
说明:语句上方有注释,表级别的comment内容必须写清,表中文名称-版本-工号。版本:20180818
正例:-- sc支付配置表-20190818
CREATE TABLE `sc_pay_config` (`id` bigint(20) NOT NULL PRIMARY KEY AUTO_INCREMENT) ENGINE=InnoDB COMMENT='sc支付配置表-20200101-工号';
20.【强制】单表字段数量不能超过50个,对于单表数据行数超过500万的表DDL需考虑性能问题。
21.【强制】字符集使用utf8mb4,数据库排序规则用 utf8mb4_general_ci,utf8mb4 可以更好的存储Emoji表情),建表时可以不指定字符集,使用系统默认字符集。
(二) 索引规约
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 的情况,影响查询性能。
正例:where a=? and b=? order by c; 索引:a_b_c
反例:索引如果存在范围查询,那么索引有序性无法利用,如:WHERE a>10 ORDER BY b; 索引 a_b 无法排序。
6.【推荐】利用覆盖索引来进行查询操作,避免回表。
说明:如果一本书需要知道第 11 章是什么标题,会翻开第 11 章对应的那一页吗?目录浏览一下就好,这个目录就是起到覆盖索引的作用。
正例:能够建立索引的种类分为主键索引、唯一索引、普通索引三种,而覆盖索引只是一种查询的一种效果,用 explain 的结果,extra 列会出现:using index。
7.【推荐】利用延迟关联或者子查询优化超多分页场景。
说明: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
8.【推荐】SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是consts 最好。
说明:
1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
2) ref 指的是使用普通的索引(normal index)。
3) range 对索引进行范围检索。
反例:explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级别比较 range 还低,与全表扫描是小巫见大巫。
9.【推荐】建组合索引的时候,区分度最高的在最左边。
正例:如果 where a=? and b=? ,如果 a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。
说明:存在非等号和等号混合时,在建索引时,请把等号条件的列前置。如:where c>? and d=? 那么即使 c 的区分度更高,也必须把 d 放在索引的最前列,即索引 idx_d_c。
10.【强制】防止因字段类型不同造成的隐式转换,导致索引失效。
11.【推荐】索引字段最好设置为NOT NULL,NOT NULL必须有默认值。
12.【推荐】尽量使用复合索引,而不是添加新的索引;比如索引idx1(a, b)已经涵盖了索引idx2(a),就可以考虑删除 idx2 索引。复合索引字段数量不能超过5个
13. 【参考】创建索引时避免有如下极端误解:
1) 宁滥勿缺。认为一个查询就需要建一个索引,单张表中索引数量不建议超过 5 个
2) 宁缺勿滥。认为索引会消耗空间、严重拖慢记录的更新以及行的新增速度。
3) 抵制惟一索引。认为业务的惟一性一律需要在应用层通过“先查后插”方式解决。
14.【参考】字段类型对效率的影响:
char 和 varchar:
1)char(n) 若存入字符数小于n,则以空格补于其后,查询之时再将空格去掉。所以 char 类型存储的字符串末尾不能有空格,varchar 不限于此。
2)char(n) 固定长度,char(4) 不管是存入几个字符,都将占用 4 个字节,varchar 是存入的实际字符数 +1 个字节(n<=255)或2个字节(n>255),所以 varchar(4),存入 3 个字符将占用 4 个字节。
3)char 类型的字符串检索速度要比 varchar 类型的快。
varchar 和 text:
1)varchar 可指定 n,text 不能指定,内部存储 varchar 是存入的实际字符数 +1 个字节(n<=255)或 2 个字节(n>255),text 是实际字符数 +2 个字节。
2)text 类型不能有默认值。
3)varchar 可直接创建索引,text 创建索引要指定前多少个字符。varchar 查询速度快于 text, 在都创建索引的情况下,text 的索引似乎不起作用。
二进制数据(_Blob)
1)_BLOB和_text存储方式不同,_TEXT以文本方式存储,英文存储区分大小写,而_Blob是以二进制方式存储,不分大小写。
2)_BLOB存储的数据只能整体读出。
3)_TEXT可以指定字符集,_BLO不用指定字符集。
参看博文:https://www.cnblogs.com/TomFord/p/10834188.html
(三) SQL 语句
1. 【强制】不要使用 count(列名)或 count(常量)来替代 count(*),count(*)是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
2. 【强制】count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0。
3. 【强制】当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为 NULL,因此使用 sum()时需注意 NPE 问题。
正例:使用如下方式来避免 sum 的 NPE 问题:SELECT IFNULL(SUM(column), 0) FROM table;
4. 【强制】使用 ISNULL()来判断是否为 NULL 值。
说明:NULL 与任何值的直接比较都为 NULL。
1) NULL<>NULL 的返回结果是 NULL,而不是 false。
2) NULL=NULL 的返回结果是 NULL,而不是 true。
3) NULL<>1 的返回结果是 NULL,而不是 true。
5.【强制】in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
6.【强制】DDL及DML语句都必须有对应回滚语句。
7.【强制】join数量限制建议3张表,尽可能的缩小第一个表的范围,多表JOIN的字段注意下以下几方面:
1) 区分度最大的字段放在最前面
2) 核心SQL优先考虑覆盖索引
3) 避免冗余和重复索引
4) 索引要综合评估数据密度和分布以及考虑查询和更新比例
5) JOIN应避免产生笛卡尔及
8.【强制】SQL语句禁止出现隐式转换,例如:select id from test where id=’1’,其中 id 列为 int 等数字类型。
9.【强制】针对单表的多条变更语句,需要合并成一条。
10.【强制】所有ddl语句,在变更语句上需要在注释中标记出目标表的数据量。