Skip to content

Latest commit

 

History

History
404 lines (223 loc) · 14 KB

mysql.md

File metadata and controls

404 lines (223 loc) · 14 KB

[TOC]

MySQL 编码规范

1 前言

2 代码风格

3 设计规范

  3.1 表 

4 使用规范

  4.1 查询  

1 前言

本规范适用于 MYSQL 数据库设计,对其他数据库设计也有参考价值。

2 命名规范

[强制] 库名、表名、字段名必须使用小写字母,并采用下划线分割
[强制] 库名、表名、字段名尽可能简短,禁止超过 32 个字符

为了统一规范、易于辨识以及减少传输量。

[强制] 库名、表名、字段名禁止使用 MySQL 保留字

当库名、表名、字段名等属性含有保留字时,SQL语句必须用反引号引用属性名称,这将使得SQL语句书写、SHELL脚本中变量的转义等变得非常复杂。

常见关键字,如:name,time ,datetime password 等。

2.1 表命名规范

简短、有意义、易于理解的英文单词。 谨慎使用缩写。 必要的注释。

[建议] 表名加前缀

表的前缀一般用系统或模块的名称缩写,而不是没有意义的t(table)。

[建议] 临时库、表名必须以tmp为前缀,并以日期为后缀

例如 tmp_test01_20130704。

[建议] 备份库、表必须以bak为前缀,并以日期为后缀

例如 bak_test01_20130704。

[建议] 表名不可以太长,最好不要超过3个英文单词长度(22个字母)。
[强制] 使用英文单词的单数形式

字段命名规范

[建议] 简短、有意义、易于理解的英文单词

例如 UI_UserID(表tb_user_info-list)

系统中属于是业务内的编号字段,代表一定业务信息,建议字段命名为code , 如工作单编号wf_code .

[建议] 不要在字段中包含数据类型,如:datetime

不要在数据库表字段(列名)命名时重复表名,可以使用表名首字母(不包含数据库表名前缀)

2.2 视图命名规范

视图的命名请遵循以下命名规范:UV _ + 系统模块缩写(与表前缀类似)+_ + 功能标识 + 代表视图查询的主要表名(不带前缀)或功能的英文单词或英文单词缩写。 如果一个视图只对一个表进行查询,建议视图的名称就用视图所查询的表的表名(不带前缀)。这样有利于根据表名找到相应的视图。 注:UV是userView缩写

存储过程命名规范

存贮过程的命名请遵循以下命名规范:USP_ + 系统模块缩写(与表前缀类似)+_ + 功能标识 + 代表存贮过程操作的主要表名(不带前缀)或功能的英文单词或英文单词缩写。 如果一个存贮过程只对一个表进行操作,建议存贮过程的名称就用存贮过程所操作的表的表名(不带前缀)。这样有利于根据表名找到相应的存贮过程。例如: 用于新增的存贮过程USP_MESSAGE_Add_Model 用于修改的存贮过程USP_ MESSAGE_Upt_Model 用于删除的存贮过程USP_ MESSAGE_Del_ Modele 注:USP是user stored procedure

触发器命名规范

3 设计规范

3.1 表设计规范

[建议] 使用INNODB存储引擎

INNODB引擎是MySQL5.5版本以后的默认引擘,支持事务、行级锁,有更好的数据恢复能力、更好的并发性能,同时对多核、大内存、SSD等硬件支持更好,支持数据热备份等,因此INNODB相比MyISAM有明显优势。

[建议] 建议使用 UNSIGNED 存储非负数值

同样的字节数,非负存储的数值范围更大。如TINYINT有符号为 -128-127,无符号为0-255。

[建议] 强烈建议使用TINYINT来代替ENUM类型

ENUM类型在需要修改或增加枚举值时,需要在线DDL,成本较大;ENUM列值如果含有数字类型,可能会引起默认值混淆。

[建议] 根据第三范式(3NF)来设计数据库,再根据效率做必要反范式设计
[建议] 避免使用NULL字段

原因:

  1. NULL字段很难查询优化;
  2. NULL字段的索引需要额外空间;
  3. NULL字段的复合索引无效;

建议用 0、空串、当前时间或特殊值代替 NULL 值。

[建议] 合理选择字段类型

INT 类型占存储空间小,速度快。

例如:用UNSIGNED INT 而不是 char(15) 存储 IPv4。

通过MySQL函数inet_ntoa和inet_aton来进行转化IPv4。

SELECT INET_ATON('209.207.224.40'); 3520061480
 
SELECT INET_NTOA(3520061480); 209.207.224.40
[建议] 用好字段类型

整型:

  • tinyint(1Byte)最大255
  • smallint(2Byte)最大 3 万多,无符号最大 6 万多
  • mediumint(3Byte)最大 800 多万,无符号最大 1600 多万
  • int(4Byte)最大 21 亿多,无符号最大 42 亿多
  • bigint(8Byte)

建议:

  • 无符号整型除非超过了某个级别的最大值,否则不允许使用更大范围的类型。比如储存的整型是无符号的,且最大不超过 1600 万,就用 mediumint 而不是 int。
  • 自增序列类型的字段只能使用unsigned int,防止超出违反(比如用户大批量导入数据)。

浮点型:

建议:

  • 使用 DECIMAL(M,D) 而不是 DECIMAL;
  • 建议乘以固定倍数转换成整数存储,可以节省存储空间,且不会带来任何附加维护成本。

字符型:

  • text(2Byte)最大 65,535
  • MEDIUMTEXT 最大长度为16,777,215。
  • LONGTEXT 最大长度为4,294,967,295

建议:

  • varchar的性能会比text高很多。
  • 尽量不要用text。
  • 禁用blob,实在避免不了blob,请拆表。

时间型:

建议:

  • 如果时间字段只需要精确到天,那就用date类型。
  • 需要精确(年月日时分秒)的时间字段,可以使用datetime,timestamp

优先使用enum或set 例如:sex enum (‘F’, ‘M’)。

(4)字段的描述 a.字段必须填写描述信息(注释)

(5)加索引规则 a.表建好后数据库自动为表生成一个索引(为 自动增长的列生成唯一索引),如果在对这列添加索引,数据库会给一个警告,内容大概是,已经为这列添加了索引,建议修改索引名称和自动增长列名保持一致,为了方便使用。 b.如果在添加索引时,建议索引名称和数据库列名保持一致,为了方便使用 c.如果字段事实上是与其它表的关键字相关联而未设计为外键引用,需建索引。 d.如果字段与其它表的字段相关联,需建索引。 e.如果字段需做模糊查询之外的条件查询,需建索引。 f.除了主关键字允许建立簇索引外,其它字段所建索引必须为非簇索引。

[强制] 请为每一张表设计一个与业务无关的主键字段。

主键是不可修改的,当业务变更时,与业务有关的主键会造成很多问题。比如你用int型做文章的id,但是如果在未来某一天文章数超过了无符号整形的最大值,你将没法将主键修改成bigint。或者为了给用户起一个唯一id用了自增主键,但是如果未来有其他的项目用户要合并进来,他也是这么做的。这时候为了区分不同的项目可能要在这个用户id前加一个前缀,这时候也没法修改主键的值。主键之所以叫做主键就是到什么时候都不能改,所以最好的方案就是使用自增数字id做主键,并且不要给这个主键赋予一个业务相关的意义。

根据数据量,可以采用自增 INT 主键或者其他类型的主键。

典型错误例子: 使用学号作为学生表的主键;使用用户名作为用户表的主键。

[建议] 不用联合主键

应尽量避免在库表中引入与业务逻辑相关的主键关系。 复合主键的引入,很大程度上意味着业务逻辑已经侵入到数据存储逻辑之中。

给原来的“复合主键”做唯一索引。

[建议] 使用自增主键

如果是非互联网应用,数据量不是很大,优先考虑使用自增主键。

  • 占用存储空间小。
  • insert等操作执行效率高。

互联网等数据量大的应用,因为要分库分表,不推荐使用自增主键。

[建议] varchar 主键不建议使用 UUID

原因:

  • InnoDB 采用聚合索引(按照主键的顺序来排放数据),而 UUID 是无序的,会造成插入时的排序和数据移动,带来很大的I/O,会影响效率。
  • 效率不高。数据在索引的时候效率会随着体积的增加而降低。
  • uuid主键id显得冗长,不够友好。
  • 存储一个UUID要花费更多的空间。

制定合适的主键生成策略,保证主键的唯一性,有序性和简短。

自增 id 作为主键(或者是改用有特定顺序的键),uuid作为唯一键。

索引

[建议] 谨慎合理使用索引

改善查询、减慢更新; 索引一定不是越多越好能不加就不加,要加的一定得加; 覆盖记录条数过多不适合建索引,例如“性别”;

[建议] 为搜索字段建索引
[建议] 不在低基数列上建立索引

例如“性别”。

大部分场景下,低基数列上建立索引的精确查找,相对于不建立索引的全表扫描没有任何优势,而且增大了IO负担。

[强制] 禁止重复索引。

重复索引增加维护负担、占用磁盘空间,且没有任何好处。

[强制] 字符字段必须使用前缀索引(MySQL)

视图设计规范

在视图中必须说明以下内容:

  1. 目的:说明此视图的作用。
  2. 创建者:首次创建此视图的人的姓名。在此请使用中文全名,不允许使用英文简称。
  3. 修改者、修改日期、修改原因:如果有人对此视图进行了修改,则必须在此视图的前面加注修改者姓名、修改日期及修改原因。
  4. 对视图各参数及变量的中文注解

建议:在数据库中创建一个文本文件保存创建脚本

存储过程设计规范

在存贮过程中必须说明以下内容:

  1. 目的:说明此存贮过程的作用。
  2. 作者:首次创建此存贮过程的人的姓名。在此请使用中文全名,不允许使用英文简称。
  3. 创建日期:创建存贮过程时的日期。
  4. 修改记录:

修改记录需包含修改顺序号、修改者、修改日期、修改原因,修改时不能直接在原来的代码上修改,也不能删除原来的代码,只能先将原来的代码注释掉,再重新增加正确的代码。修改顺序号的形式为:log1,log2,log3。。。,根据修改次数顺序增加,同时在注释掉的原来的代码块和新增的正确代码块前后注明修改顺序号。 5. 对存贮过程各参数及变量的中文注解。

建议:在数据库中创建一个文本文件保存创建脚本

安全

[强制] 禁止在数据库中存储明文密码

采用加密字符串存储密码,并保证密码不可解密,同时采用随机字符串加盐保证密码安全。防止数据库数据被公司内部人员或黑客获取后,采用字典攻击等方式暴力破解用户密码。

4 使用规范

4.1 查询

[强制] 不用 select *

SELECT 只获取必要的字段。

消耗cpu,io,内存,带宽;这种程序不具有扩展性;

减少网络带宽消耗;

能有效利用覆盖索引;

表结构变更对程序基本无影响。

[建议] 用 IN 代替 OR

IN是范围查找,MySQL内部会对IN的列表值进行排序后查找,比OR效率更高。

or的效率是n级别; in的效率是log(n)级别;

/* good */
select id from t where phone in ('159', '136');

/* bad */
select id from t where phone='159' or phone='136';
[建议] 用 UNION 代替 OR

MySQL 的索引合并很弱智。

/* good */
select id from t where phone='159'
union
select id from t where name='jonh'

/* bad */
select id from t where phone = '159' or name = 'john';
[建议] SQL 语句中 IN 包含的值不应过多,控制在 200 个以内,最多不得超过300
[建议] 平衡范式与冗余,效率优先,往往牺牲范式
[强制] 禁止在数据库中存储图片、文件等大数据

数据库通常存储的是文件的路径(多用相对路径)。

[建议] 高并发环境不要使用外键

高并发环境不要使用外键,太容易产生死锁,应由程序保证约束。

[建议] 合理控制单表数据量

int型不超过1000w,含char则不超过500w。

mysql在处理大表(char的表>500W行,或int表>1000W)时,性能就开始明显降低,所以要采用不同的方式控制单表容量

A:根据数据冷热,对数据分级存储,历史归档

B:采用分库/分表/分区表,横向拆分控制单表容量

C:对于OLTP系统,控制单事务的资源消耗,遇到大事务可以拆解,采用化整为零模式,避免特例影响大众

[建议] 限制单库表数量在300以内,最多不要超过500个表
[建议] 单表字段数不要太多,字段数建议在20以内,最多不要大于50个
[建议] sql语句尽可能简单

拆分复杂 SQL 为多个小 SQL,避免大事务,减少锁时间。

  • 一条 sql 只能在一个 cpu 运算,拆分后可以使用多核 CPU。
  • 简单的 SQL 容易使用到 MySQL 的 QUERY CACHE。
  • 减少锁表时间特别是 MyISAM。
[建议] 事务时间尽可能短

bad case: 上传图片事务

[强制] 保证冗余字段数据的一致性

确保数据更新的同时冗余字段也被更新。

[建议] 合理分离冷热数据

将大字段、访问频率低的字段拆分到单独的表中存储,分离冷热数据。

有利于有效利用缓存,防止读入无用的冷数据,较少磁盘IO,同时保证热数据常驻内存提高缓存命中率。

[建议] 不在数据库做运算

不在索引列上进行数学运算或函数运算。

cpu计算务必移至业务层。

[建议] 当只要一行数据时使用 LIMIT 1
[强制] 禁止在线上做数据库压力测试
[建议] 减少与数据库交互次数,尽量采用批量SQL语句
[建议] 获取大量数据时,建议分批次获取数据,每次获取数据少于 2000 条,结果集应小于 1M