MySQL数据库使用规范

MySQL数据库常用规范记录
一、基础规范
1、必须使用InnoDB存储引擎
1)5.5版本开始mysql默认存储引擎就是InnoDB,5.7版本开始,系统表都放弃MyISAM了。目前公司使用的有5.6、5.7两个大版本。
2)支持事务、行级锁、并发性能更好、CPU及内存缓存页优化使得资源利用率更高。
例如:CREATE TABLE tb (
字段列表
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT=‘订单统计表’

2、必须使用UTF8字符集
1)万国码,无需转码,无乱码风险
2)UTF8字符集存储汉字占用3个字节,存储英文字符占用一个字节

3、数据表、表字段必须加入中文注释
例如:CREATE TABLE tb (
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT ‘自增主键’,
domain VARCHAR(50) NOT NULL COMMENT ‘域名’,
instime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT ‘记录新增时间’,
PRIMARY KEY (id)
)

4、库名、表名、字段名禁止超过32个字符,且需见名知意
库名、表名、字段名支持最多64个字符,但为了统一规范、易于辨识以及减少传输量

5、库名、表名、字段名必须区分大小写字母
表名和字段名采用下划线分割,库名不能加下划线。
为了统一规范,库名、表名、字段名必须区分大小写敏感。

6、适度使用存储过程、视图,禁止使用触发器、事件
1)存储过程:不记录日志,不便于调试,且分库分表处理困难
2)视图:为降低代码里SQL的复杂度,但为了视图的通用性会损失性能(如返回无用字段)
3)触发器:经常造成性能问题

7、禁止存储大文件或者大照片
大文件和照片存储在文件系统,数据库里存URL

二、表设计规范
1、所有表都必须要显式指定主键
1)主键强制要求采用自增字段 或 自定义的递增字段,确保新增在末尾
2)小表 或 增删改极少的表,可以考虑其它主键

2、禁止使用外键,如果有外键完整性约束,需要应用程序控制
外键耦合高,性能低下,甚至会造成死锁,不适应高并发场景

3、单实例表数目必须小于500

4、控制单表字段数量
1)单表字段数上限30左右,更多请考虑垂直分表,冷热数据、常用字段或大字段分离
2)表字段控制少而精,可以提高IO效率,缓存容量,且alter table 也更快。

5、单表数据行数 控制在500w以内.数据量太大,请考虑分库分表,或者冷热数据分离。单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表
单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。
说明: 如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。

6、数据库中不允许存储明文密码,必备三字段: id, gmt_create, gmt_modified。gmt_create, gmt_modified 的类型均为date_time 类型。

7、表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint
(1 表示是, 0 表示否),此规则同样适用于 odps 建表。
说明: 任何字段如果为非负数,必须是 unsigned。

8、表名、字段名必须使用小写字母或数字;禁止出现数字开头,禁止两个下划线中间只

出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
正例: getter_admin, task_config, level3_name
反例: GetterAdmin, taskConfig, level_3_name

9、表名不使用复数名词。

说明: 表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数
形式,符合表达习惯。

10 禁用保留字,如 desc、 range、 match、 delayed 等, 参考官方保留字。

11 小数类型为 decimal,禁止使用 float 和 double。
说明: float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。

12、如果存储的字符串长度几乎相等,使用 CHAR 定长字符串类型
varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度
大于此值,定义字段类型为 TEXT,独立出来一张表,用主键来对应,避免影响其它字段索引效率。

13、数据库中不允许存储明文密码表的命名最好是加上“业务名称_表的作用” ,避免上云梯后,再与其它业务表关联时有混淆。

正例: tiger_task / tiger_reader / mpp_config

14、库名与应用名称尽量一致。

25、合适的字符存储长度
合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索
速度。
正例: 人的年龄用 unsigned tinyint(表示范围 0-255,人的寿命不会超过 255 岁);海龟就必须是 smallint,但如果是太阳的年龄,就必须是 int;如果是所有恒星的年龄都加起来,那么就必须使用 bigint。

三、字段设计规范
1、字段类型优先级:整型 >date,time > char > varchar > blob,text
列的特点分析
1)整型:定长,没有国家/地区之分,没有字符集的差异,order by排序比字符类型快。
2)date,time:定长,运算快,节省空间,考虑时区。
3)Char:定长, 考虑字符集和(排序)校对集
4)varchar:不定长,要考虑字符集的转换与排序时的校对集,速度慢。
5)text/Blob:无法使用内存临时表

2、数据类型长度够用就行,不要慷慨
数据类型长度过大的字段浪费内存,影响速度。

3、字段强制要求NOT NULL,且提供默认值
1)null列 使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化
2)null 类型MySQL内部需要特殊处理,增加处理复杂性,并导致数据库性能降低
3)null需要更多的存储空间,表or索引中的null列都需要额外的空间来标识
4)null只能采用is null或is not null,而不能采用=、in、<、<>、!=、not in这些操作符号
5)null在业务场景中经常造成各种异常或业务逻辑bug

4、避免使用TEXT、LONGTEXT类型,禁用BLOB类型
1)会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,导致内存命中率急剧降低,影响数据库性能。

5、避免使用小数定义 float double number
1)对于金额,可以使用整数,精确到分,小数精度问题容易出现金额差异
2)百分比、折扣等字段也使用整数,业务代码中再除以100使用

6、建议使用varchar(20)存储手机号
1)涉及到区号或者国家代号,可能出现±()
2)手机号不会去做数学运算,所以不要定义为数值类型
3)varchar可以支持模糊查询,例如:like“138%”

7、使用datetime/timestamp/int区别

Int: 适合需要进行大量时间范围查询的数据表

Datetime: 类型适合用来记录数据的原始的创建时间,因为无论你怎么更改记录中其他字段的值,datetime字段的值都不会改变,除非你手动更改它。

Timestamp: 类型适合用来记录数据的最后修改时间,因为只要你更改了记录中其他字段的值,timestamp字段的值都会被自动更新。(如果需要可以设置timestamp不自动更新)

8、使用count(*)与count(1)与count(field1)的区别

count(*)和count(1)无太大差别,count(field)是对field列不为null的行进行统计,因此某一行的该列为null,则不予计数
count(field)若使用不当会带来错误或性能问题,不建议使用

四、索引设计规范
1、检查sql索引使用情况
任何新的select,update,delete上线都要先explain,看索引使用情况。避免等到上线后有性能瓶颈后才发现无索引。表小或表使用不多都不是借口。

2、索引个数限制
1)单表索引建议控制在5个以内,索引过多会导致insert效率下降。
2)单索引字段数不允许超过5个,字段超过5个时,实际已经起不到有效过滤数据的作用了。

3、禁止在更新十分频繁、区分度不高的属性上建立索引
1)更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能。
2)"性别"这种区分度不大的属性,建立索引没有意义,性能与全表扫描类似。

4、建立组合索引,必须把区分度高的字段放在前面
1)能够更加有效的过滤数据

5、业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引

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

6、超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时,

保证被关联的字段需要有索引。
说明: 即使双表 join 也要注意表索引、 SQL 性能。

7、在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。

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

8、搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。

说明: 索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索
引。

9、如果有 order by 的场景,请注意利用索引的有序性。 order by 最后的字段是组合索

引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能。
正例: where a=? and b=? order by c; 索引: a_b_c
反例: 索引中有范围查找,那么索引有序性无法利用,如: WHERE a>10 ORDER BY b; 索引 a_b无法排序。

10、利用覆盖索引来进行查询操作,来避免回表操作。

说明: 如果一本书需要知道第 11 章是什么标题,会翻开第 11 章对应的那一页吗?目录浏览一下就好,这个目录就是起到覆盖索引的作用。
正例: IDB 能够建立索引的种类:主键索引、唯一索引、普通索引,而覆盖索引是一种查询的一种效果,用 explain 的结果, extra 列会出现: using index.

11、利用延迟关联或者子查询优化超多分页场景

说明: 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

12、SQL 性能优化的目标:至少要达到 range 级别, 要求是 ref 级别, 如果可以是 consts最好。

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

13、建组合索引的时候,区分度最高的在最左边。

正例: 如果 where a=? and b=? , a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。
说明: 存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如: where a>?
and b=? 那么即使 a 的区分度更高,也必须把 b 放在索引的最前列。

14、创建索引时避免有如下极端误解:

1)误认为一个查询就需要建一个索引。
2)误认为索引会消耗空间、严重拖慢更新和新增速度。
3)误认为唯一索引一律需要在应用层通过“先查后插” 方式解决。

五、SQL使用规范
1、避免使用SELECT *,只获取必要的字段,需要显示说明列属性
1)读取不需要的列会增加CPU、IO、NET消耗
2)不能有效的利用覆盖索引
3)使用SELECT *容易在增加或者删除字段后出现程序BUG

2、禁止使用INSERT INTO t_xxx VALUES(xxx),必须显示指定插入的列属性
1)容易在增加或者删除字段后,字段与值出现错位引发出现程序BUG。

3、禁止使用属性隐式转换
如:手机号phone字段为varchar(20)
SELECT uid FROM t_user WHERE phone=13812345678 会导致全表扫描,不能命中索引

4、禁止在WHERE条件的属性上使用函数或者表达式
如:SELECT uid FROM t_user WHERE from_unixtime(day)>=‘2017-05-12’ 会导致全表扫描
正确写法:SELECT uid FROM t_user WHERE day>= unix_timestamp(‘2017-05-12 00:00:00’)

5、避免负向查询,以及%开头的模糊查询
1)负向查询条件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,会导致全表扫描
2)%开头的模糊查询,会导致全表扫描

6、避免JOIN连接大表,避免对大表使用子查询
1)会产生临时表,消耗较多内存与CPU,极大影响数据库性能

7、避免使用OR条件,推荐改为IN查询,且不超过1000个项
8、应用程序必须捕获SQL异常,并有相应处理。
9、必须使用参数化查询,可以复用查询计划
10、禁止加载全部数据内存中分页的行为
11、只读取一行数据,必须加上limit 1
1)如果不加limit 1,会对全表或全索引一直检索到末尾

12、禁止使用随机函数排序,如 order by rand()
13、sql语句尽可能简单;一条sql只能在一个cpu运算;大语句拆小语句,减少锁时间;一条大sql可以堵死整个库
14、简单的事务,事务时间尽可能短。不好的例子:上传图片事务
15、避免使用trig/func;触发器、函数不用;客户端程序取而代之
16、不用select * : 消耗cpu,io,内存,带宽,这种程序不具有扩展性
17、OR改写为IN();or的效率是n级别;in的消息时log(n)级别;in的个数建议控制在200以内
18、OR改写为UNION
19、limit高效分页
20、使用union all替代union, union有去重开销
21、使用group by,分组,自动排序
22、请使用同类型比较
23、使用load data导数据
24、load data比insert快约20倍;
25、打散批量更新
26、不要使用 count(列名)或 count(常量)来替代 count(), count()就是 SQL92 定义的

标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
说明: count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
26、count(distinct col) 计算该列除 NULL 之外的不重复数量。注意 count(distinctcol1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0。
27、当某一列的值全是 NULL 时
当某一列的值全是 NULL 时, count(col)的返回结果为 0,但 sum(col)的返回结果为NULL,因此使用 sum()时需注意 NPE 问题。
正例: 可以使用如下方式来避免 sum 的 NPE 问题: SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROMtable;
28、使用 ISNULL()来判断是否为 NULL 值。注意: NULL 与任何值的直接比较都为 NULL。

说明:
1) NULL<>NULL 的返回结果是 NULL,不是 false。
2) NULL=NULL 的返回结果是 NULL,不是 true。
3) NULL<>1 的返回结果是 NULL,而不是 true。
29、在代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句。

30、不得使用外键与级联,一切外键概念必须在应用层解决

说明:(概念解释)学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,则为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据
库更新风暴的风险;外键影响数据库的插入速度。
31、禁止使用存储过程,存储过程难以调试和扩展,更没有移植性
32、IDB 数据订正时,删除和修改记录时,要先 select,避免出现误删除,确认无误才能提交执行
33、in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。
34、打散批量更新考虑全球化需要,所有的字符存储与表示,均以 utf-8 编码,那么字符计数方

说明:
SELECT LENGTH(“九天达”); 返回为 12
SELECT CHARACTER_LENGTH(“九天达”); 返回为 4
如果要使用表情,那么使用 utfmb4 来进行存储,注意它与 utf-8 编码。
35、TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE无事务且不触发 trigger,有可能造成事故,故不建议在开发代码中使用此语句。

说明: TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。

六、模型设计规范
1)范式
绝大部分场景 要遵循第三范式
使用text大类型使用额外表存储
注意1:1,1:N,N:M 设计模式
父子关联树形结构转换成N:M关系

2)适当场景 -> 考虑反范式
适当保留冗余数据,例如存储json
可以避免关联,因为所有的数据几乎都可以在一张表上显示,减少回表

七、脚本与版本的管理
1)使用GIT版本控制提交的方式进行开发的版本记录控制

  1. 每个对表结构的变更均进行记录,提交到SQL变更记录中GIT

  2. 针对表进行数据更新操作,比如:update ,delete,insert 均需要进行SQL 脚本的记录GIT

  3. 项目上线时候,对涉及到的数据表结构,数据表的数据的SQL 进行统一进行提交

2)定期进行生产系统的表结构同步到开发环境

八、行为规范
1、未经公司各级领导审批,禁止导出生产网数据。
2、禁止绕开DBA直接修改生产网数据库中的表结构。
3、数据库个人账号专人专用,禁止共用或借用行为。
4、业务时间使用数据库客户端工具查询大表时,要反复核对where字段的索引效率及提取的数据量。请在业务闲时处理,避免索引效率低及数据量大引起性能问题。
5、大批量数据操作如修复数据等应避开业务高峰期,提交OA工作流给DBA处理。
6、及时处理已下线业务的SQL或业务表。
7、重要项目的数据库方案选型和设计请提前通知DBA参与。

你可能感兴趣的:(数据库)