MySQL命名、设计及使用规范--------来自标点符的《MySQL命名、设计及使用规范》

原文地址:http://www.biaodianfu.com/mysql-best-practices.htmlphp

 

 

 

 

 

最近在看MySQL相关的内容,整理以下规范,做为一名刚刚学习MySQL的菜鸟,整理的内容很是的基础,中间可能涉及到有错误的地方,欢迎批评指正,看到有错误的地方指望看官留言。html

数据库环境

  • dev:开发环境,开发可读写,可修改表结构。开发人员能够修改表结构,能够随意修改其中的数据可是须要保证不影响其余开发同事。
  • qa:测试环境,开发可读写,开发人员能够经过工具修改表结构。
  • sim:模拟环境,开发可读写,发起上线请求时,会先在这个环境上进行预执行,这个环境也可供部署上线演练或压力测试使用。
  • real:生产数据库从库(准实时同步),只读环境,不容许修改数据,不容许修改表结构,供线上问题查找,数据查询等使用。
  • online:线上环境,开发人员不容许直接在线上环境进行数据库操做,若是须要操做必须找DBA进行操做并进行相应记录,禁止进行压力测试。

这些环境的机器,必定要作到权限划分明确,读写账号分离,而且有辨识度,能区分具体业务。例如用户名w_account,r_ account 分别表明读、写帐号,account是读写帐号。mysql

命名规范

基本命名原则git

  • 使用有意义的英文词汇,词汇中间如下划线分隔。(不要用拼音)
  • 只能使用英文字母,数字,下划线,并以英文字母开头。
  • 库、表、字段所有采用小写,不要使用驼峰式命名。
  • 避免用ORACLE、MySQL的保留字,如desc,关键字如index。
  • 命名禁止超过32个字符,须见名之意,建议使用名词不是动词
  • 数据库,数据表一概使用前缀
    • 临时库、表名必须以tmp为前缀,并以日期为后缀
    • 备份库、表必须以bak为前缀,并以日期为后缀

为何库、表、字段所有采用小写?github

在 MySQL 中,数据库和表对就于那些目录下的目录和文件。于是,操做系统的敏感性决定数据库和表命名的大小写敏感。sql

  • Windows下是不区分大小写的。
  • Linux下大小写规则:
    • 数据库名与表名是严格区分大小写的;
    • 表的别名是严格区分大小写的;
    • 列名与列的别名在全部的状况下均是忽略大小写的;
    • 变量名也是严格区分大小写的;

若是已经设置了驼峰式的命名如何解决?须要在MySQL的配置文件my.ini中增长 lower_case_table_names = 1便可。数据库

表命名后端

  • 同一个模块的表尽量使用相同的前缀,表名称尽量表达含义。全部日志表均以 log_ 开头

字段命名缓存

  • 表达其实际含义的英文单词或简写。布尔意义的字段以“is_”做为前缀,后接动词过去分词。
  • 各表之间相赞成义的字段应同名。各表之间相赞成义的字段,以去掉模块前缀的表名_字段名命名。
  • 外键字段用表名_字段名表示其关联关系。
  • 表的主键通常都约定成为id,自增类型,是别的表的外键均使用xxx_id的方式来代表。

索引命名网络

  • 非惟一索引必须按照“idx_字段名称_字段名称[_字段名]”进行命名
  • 惟一索引必须按照“uniq_字段名称_字段名称[_字段名]”进行命名

约束命名

  • 主键约束:pk_表名称。
  • 惟一约束:uk_表名称_字段名。(应用中须要同时有惟一性检查逻辑。)

触发器命名

  • trg_表名_操做。

函数过程命名

  • 采用动词+名词的形式表达其含义。

序列命名

  • seq_表名

表设计规范

一、表引擎取决于实际应用场景;日志及报表类表建议用myisam,与交易,审核,金额相关的表建议用innodb引擎。如无说明,建表时一概采用innodb引擎。myisam与innodb的区别

二、默认使用utf8mb4字符集,数据库排序规则使用utf8mb4_general_ci,(因为数据库定义使用了默认,数据表能够再也不定义,但为保险起见,建议都写上)。

为何字符集不选择utf8,排序规则不使用utf8_general_ci?

采用utf8编码的MySQL没法保存占位是4个字节的Emoji表情。为了使后端的项目,全面支持客户端输入的Emoji表情,升级编码为utf8mb4是最佳解决方案。对于JDBC链接串设置了characterEncoding为utf8或者作了上述配置仍旧没法正常插入emoji数据的状况,须要在代码中指定链接的字符集为utf8mb4。

三、全部表、字段均应用 comment 列属性来描述此表、字段所表明的真正含义,如枚举值则建议将该字段中使用的内容都定义出来。

四、如无说明,表中的第一个id字段必定是主键且为自动增加,禁止在非事务内做为上下文做为条件进行数据传递。禁止使用varchar类型做为主键语句设计。

五、如无说明,表必须包含create_time和modify_time字段,即表必须包含记录建立时间和修改时间的字段

六、如无说明,表必须包含is_del,用来标示数据是否被删除,原则上数据库数据不容许物理删除。

七、用尽可能少的存储空间来存数一个字段的数据

  • 能用int的就不用char或者varchar
  • 能用tinyint的就不用int
  • 使用UNSIGNED存储非负数值。
  • 不建议使用ENUM、SET类型,使用TINYINT来代替
  • 使用短数据类型,好比取值范围为0-80时,使用TINYINT UNSIGNED
  • 存储精确浮点数必须使用DECIMAL替代FLOAT和DOUBLE
  • 时间字段,除特殊状况一概采用int来记录unix_timestamp
    • 存储年使用YEAR类型。
    • 存储日期使用DATE类型。
    • 存储时间(精确到秒)建议使用TIMESTAMP类型,由于TIMESTAMP使用4字节,DATETIME使用8个字节。
  • 建议使用INT UNSIGNED存储IPV4。
  • 尽量不使用TEXT、BLOB类型
  • 禁止在数据库中使用VARBINARY、BLOB存储图片、文件等。建议使用其余方式存储(TFS/SFS),MySQL只保存指针信息。
  • 单条记录大小禁止超过8k(列长度(中文)*3(UTF8)+列长度(英文)*1)

datetime与timestamp有什么不一样?

相同点:TIMESTAMP列的显示格式与DATETIME列相同。显示宽度固定在19字符,而且格式为YYYY-MM-DD HH:MM:SS。
不一样点:

  • TIMESTAMP
    • 4个字节储存,时间范围:1970-01-01 08:00:01 ~ 2038-01-19 11:14:07
    • 值以UTC格式保存,涉及时区转化 ,存储时对当前的时区进行转换,检索时再转换回当前的时区。
  • datetime
    • 8个字节储存,时间范围:1000-01-01 00:00:00 ~ 9999-12-31 23:59:59
    • 实际格式储存,与时区无关

如何使用TIMESTAMP的自动赋值属性?

  • 将当前时间做为ts的默认值:ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP。
  • 当行更新时,更新ts的值:ts TIMESTAMP DEFAULT 0 ON UPDATE CURRENT_TIMESTAMP。
  • 能够将1和2结合起来:ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP。

如何使用INT UNSIGNED存储ip?

使用INT UNSIGNED而不是char(15)来存储ipv4地址,经过MySQL函数inet_ntoa和inet_aton来进行转化。Ipv6地址目前没有转化函数,须要使用DECIMAL或者两个bigINT来存储。

八、如无备注,全部字段都设置NOT NULL,并设置默认值;

九、禁止在数据库中存储明文密码

十、如无备注,全部的布尔值字段,如is_hot、is_deleted,都必须设置一个默认值,并设为0;

十一、如无备注,排序字段order_id在程序中默认使用降序排列;

十二、整形定义中不添加长度,好比使用INT,而不是INT[4]

INT[M],M值表明什么含义?

注意数值类型括号后面的数字只是表示宽度而跟存储范围没有关系。不少人他们认为INT(4)和INT(10)其取值范围分别是 (-9999到9999)和(-9999999999到9999999999),这种理解是错误的。其实对整型中的 M值与 ZEROFILL 属性结合使用时能够实现列值等宽。无论INT[M]中M值是多少,其取值范围仍是 (-2147483648到2147483647 有符号时),(0到4294967295无符号时)。

显示宽度并不限制能够在列内保存的值的范围,也不限制超过列的指定宽度的值的显示。当结合可选扩展属性ZEROFILL使用时默认补充的空格用零代替。例如:对于声明为INT(5) ZEROFILL的列,值4检索为00004。请注意若是在整数列保存超过显示宽度的一个值,当MySQL为复杂联接生成临时表时会遇到问题,由于在这些状况下MySQL相信数据适合原列宽度,若是为一个数值列指定ZEROFILL, MySQL自动为该列添加UNSIGNED属性。

1三、使用VARBINARY存储大小写敏感的变长字符串

何时用CHAR,何时用VARCHAR?

CHAR和VARCHAR类型相似,但它们保存和检索的方式不一样。它们的最大长度和是否尾部空格被保留等方面也不一样。CHAR和VARCHAR类型声明的长度表示你想要保存的最大字符数。例如,CHAR(30)能够占用30个字符。

  • CHAR列的长度固定为建立表时声明的长度。长度能够为从0到255的任何值。当保存CHAR值时,在它们的右边填充空格以达到指定的长度。当检索到CHAR值时,尾部的空格被删除掉。在存储或检索过程当中不进行大小写转换。
  • VARCHAR列中的值为可变长字符串。长度能够指定为0到65,535之间的值。(VARCHAR的最大有效长度由最大行大小和使用的字符集肯定。总体最大长度是65,532字节)。

同CHAR对比,VARCHAR值保存时只保存须要的字符数,另加一个字节来记录长度(若是列声明的长度超过255,则使用两个字节)。VARCHAR值保存时不进行填充。当值保存和检索时尾部的空格仍保留,符合标准SQL。

char适合存储用户密码的MD5哈希值,它的长度老是同样的。对于常常改变的值,char也好于varchar,由于固定长度的行不容易产生碎片,对于很短的列,char的效率也高于varchar。char(1)字符串对于单字节字符集只会占用一个字节,可是varchar(1)则会占用2个字节,由于1个字节用来存储长度信息。

索引设计规范

MySQL的查询速度依赖良好的索引设计,所以索引对于高性能相当重要。合理的索引会加快查询速度(包括UPDATE和DELETE的速度,MySQL会将包含该行的page加载到内存中,而后进行UPDATE或者DELETE操做),不合理的索引会下降速度。MySQL索引查找相似于新华字典的拼音和部首查找,当拼音和部首索引不存在时,只能经过一页一页的翻页来查找。当MySQL查询不能使用索引时,MySQL会进行全表扫描,会消耗大量的IO。索引的用途:去重、加速定位、避免排序、覆盖索引。

什么是覆盖索引?

InnoDB存储引擎中,secondary index(非主键索引)中没有直接存储行地址,存储主键值。若是用户须要查询secondary index中所不包含的数据列时,须要先经过secondary index查找到主键值,而后再经过主键查询到其余数据列,所以须要查询两次。覆盖索引的概念就是查询能够经过在一个索引中完成,覆盖索引效率会比较高,主键查询是自然的覆盖索引。合理的建立索引以及合理的使用查询语句,当使用到覆盖索引时能够得到性能提高。好比SELECT email,uid FROM user_email WHERE uid=xx,若是uid不是主键,适当时候能够将索引添加为index(uid,email),以得到性能提高。

索引的基本规范

一、索引数量控制,单张表中索引数量不超过5个,单个索引中的字段数不超过5个。

  • 综合评估数据密度和分布
  • 考虑查询和更新比例

为何一张表中不能存在过多的索引?

InnoDB的secondary index使用b+tree来存储,所以在UPDATE、DELETE、INSERT的时候须要对b+tree进行调整,过多的索引会减慢更新的速度。

二、对字符串使用前缀索引,前缀索引长度不超过8个字符,建议优先考虑前缀索引,必要时可添加伪列并创建索引。

  • 不要索引blob/text等字段,不要索引大型字段,这样作会让索引占用太多的存储空间

什么是前缀索引?

前缀索引说白了就是对文本的前几个字符(具体是几个字符在创建索引时指定)创建索引,这样创建起来的索引更小,因此查询更快。 前缀索引能有效减少索引文件的大小,提升索引的速度。可是前缀索引也有它的坏处:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前缀索引,也不能把它们用做覆盖索引(Covering Index)。

创建前缀索引的语法:ALTER TABLE table_name ADD KEY(column_name(prefix_length));

三、主键准则

  • 表必须有主键
  • 不使用更新频繁的列
  • 尽可能不选择字符串列
  • 不使用UUID MD5 HASH
  • 默认使用非空的惟一键
  • 建议选择自增或发号器

四、 重要的SQL必须被索引,核心SQL优先考虑覆盖索索引

  • UPDATE、DELETE语句的WHERE条件列
  • ORDER BY、GROUP BY、DISTINCT的字段
  • 多表JOIN的字段

五、区分度最大的字段放在前面

  • 选择筛选性更优的字段放在最前面,好比单号、userid等,type,status等筛选性通常不建议放在最前面
  • 索引根据左前缀原则,当创建一个联合索引(a,b,c),则查询条件里面只有包含(a)或(a,b)或(a,b,c)的时候才能走索引,(a,c)做为条件的时候只能使用到a列索引,因此这个时候要肯定a的返回列必定不能太多,否则语句设计就不合理,(b,c)则不能走索引
  • 合理建立联合索引(避免冗余),(a,b,c) 至关于 (a) 、(a,b) 、(a,b,c)

六、索引禁忌

  • 不在低基数列上创建索引,例如“性别”
  • 不在索引列进行数学运算和函数运算
  • 不要索引经常使用的小型表

七、 尽可能不使用外键

  • 外键用来保护参照完整性,可在业务端实现
  • 对父表和子表的操做会相互影响,下降可用性
  • INNODB自己对online DDL的限制

MYSQL 中索引的限制

  • MYISAM 存储引擎索引长度的总和不能超过 1000 字节
  • BLOB 和 TEXT 类型的列只能建立前缀索引
  • MYSQL 目前不支持函数索引
  • 使用不等于 (!= 或者 <>) 的时候, MYSQL 没法使用索引。
  • 过滤字段使用函数运算 (如 abs (column)) 后, MYSQL没法使用索引。
  • join语句中join条件字段类型不一致的时候MYSQL没法使用索引
  • 使用 LIKE 操做的时候若是条件以通配符开始 (如 ‘%abc…’)时, MYSQL没法使用索引。
  • 使用非等值查询的时候, MYSQL 没法使用 Hash 索引。

语句设计规范

一、使用预编译语句

  • 只传参数,比传递SQL语句更高效
  • 一次解析,屡次使用
  • 下降SQL注入几率

二、避免隐式转换

  • 会致使索引失效

三、充分利用前缀索引

  • 必须是最左前缀
  • 不可能同时用到两个范围条件
  • 不使用%前导的查询,如like “%ab”

四、不使用负向查询,如not in/like

  • 没法使用索引,致使全表扫描
  • 全表扫描致使buffer pool利用率下降

五、避免使用存储过程、触发器、UDF、events等

  • 让数据库作最擅长的事
  • 下降业务耦合度,为sacle out、sharding留有余地
  • 避开BUG

六、避免使用大表的JOIN

  • MySQL最擅长的是单表的主键/二级索引查询
  • JOIN消耗较多内存,产生临时表

七、避免在数据库中进行数学运算

  • MySQL不擅长数学运算和逻辑判断
  • 没法使用索引

七、减小与数据库的交互次数

  • INSERT … ON DUPLICATE KEY UPDATE
  • REPLACE INTO、INSERT IGNORE 、INSERT INTO VALUES(),(),()
  • UPDATE … WHERE ID IN(10,20,50,…)

八、合理的使用分页

  • 限制分页展现的页数
  • 只能点击上一页、下一页
  • 采用延迟关联

如何正确的使用分页?

假若有相似下面分页语句:SELECT * FROM table  ORDER BY id LIMIT 10000, 10

因为MySQL里对LIMIT OFFSET的处理方式是取出OFFSET+LIMIT的全部数据,而后去掉OFFSET,返回底部的LIMIT。因此,在OFFSET数值较大时,MySQL的查询性能会很是低。可使用id > n 的方式进行解决:

使用id > n 的方式有局限性,对于id不连续的问题,能够经过翻页的时候同时传入最后一个id方式来解决。

这种方式比较大的缺点是,若是在浏览中有插入/删除操做,翻页不会更新,而总页数可能仍然是根据新的count(*) 来计算,最终可能会产生某些记录访问不到。为了修补这个问题,能够继续引入当前页码以及在上次翻页之后是否有插入/删除等影响总记录数的操做并进行缓存

其余变种方式:

 

九、拒绝大SQL,拆分红小SQL

  • 充分利用QUERY CACHE
  • 充分利用多核CPU

十、使用in代替or,in的值不超过1000个

十一、禁止使用order by rand()

十二、使用EXPLAIN诊断,避免生成临时表

EXPLAIN语句(在MySQL客户端中执行)能够得到MySQL如何执行SELECT语句的信息。经过对SELECT语句执行EXPLAIN,能够知晓MySQL执行该SELECT语句时是否使用了索引、全表扫描、临时表、排序等信息。尽可能避免MySQL进行全表扫描、使用临时表、排序等。详见官方文档。

1三、用union all而不是union

union all与 union有什么区别?

union和union all关键字都是将两个结果集合并为一个,但这二者从使用和效率上来讲都有所不一样。

union在进行表连接后会筛选掉重复的记录,因此在表连接后会对所产生的结果集进行排序运算,删除重复的记录再返回结果。如:

这个SQL在运行时先取出两个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,若是表数据量大的话可能会致使用磁盘进行排序。

而union all只是简单的将两个结果合并后就返回。这样,若是返回的两个结果集中有重复的数据,那么返回的结果集就会包含重复的数据了。

从效率上说,union all要比union快不少,因此,若是能够确认合并的两个结果集中不包含重复的数据的话,那么就使用union all,以下:

 

1四、程序应有捕获SQL异常的处理机制

1五、禁止单条SQL语句同时更新多个表

1六、不使用select * ,SELECT语句只获取须要的字段

  • 消耗CPU和IO、消耗网络带宽
  • 没法使用覆盖索引
  • 减小表结构变动带来的影响
  • 由于大,select/join 可能生成临时表

1七、UPDATE、DELETE语句不使用LIMIT

1八、INSERT语句必须显式的指明字段名称,不使用INSERT INTO table()

1九、INSERT语句使用batch提交(INSERT INTO table VALUES(),(),()……),values的个数不超过500

20、统计表中记录数时使用COUNT(*),而不是COUNT(primary_key)和COUNT(1) 备注:仅针对Myisam

2一、数据更新建议使用二级索引先查询出主键,再根据主键进行数据更新

2二、禁止使用跨库查询

2三、禁止使用子查询,建议将子查询转换成关联查询

2四、针对varchar类型字段的程序处理,请验证用户输入,不要超出其预设的长度;

分表规范

单表一到两年内数据量超过500w或数据容量超过10G考虑分表,需提早考虑历史数据迁移或应用自行删除历史数据,采用等量均衡分表或根据业务规则分表都可。要分表的数据表必须与DBA商量分表策略

  • 用HASH进行散表,表名后缀使用十进制数,下标从0开始
  • 按日期时间分表需符合YYYY[MM][DD][HH]格式
  • 采用合适的分库分表策略。例如千库十表、十库百表等
  • 禁止使用分区表,分区表对分区键有严格要,分区表在表变大后执行DDL、SHARDING、单表恢复等都变得更加困难。
  • 拆分大字段和访问频率低的字段,分离冷热数据

行为规范

  • 批量导入、导出数据必须提早通知DBA协助观察
  • 禁止在线上从库执行后台管理和统计类查询
  • 禁止有super权限的应用程序帐号存在
  • 产品出现非数据库致使的故障时及时通知DBA协助排查
  • 推广活动或上线新功能必须提早通知DBA进行流量评估
  • 数据库数据丢失,及时联系DBA进行恢复
  • 对单表的屡次alter操做必须合并为一次操做
  • 不在MySQL数据库中存放业务逻辑
  • 重大项目的数据库方案选型和设计必须提早通知DBA参与
  • 对特别重要的库表,提早与DBA沟通肯定维护和备份优先级
  • 不在业务高峰期批量更新、查询数据库其余规范
  • 提交线上建表改表需求,必须详细注明全部相关SQL语句

其余规范

日志类数据不建议存储在MySQL上,优先考虑Hbase或OceanBase,如须要存储请找DBA评估使用压缩表存储。

相关文章
相关标签/搜索