一、DBA操作规范
1、涉及业务上的修改/删除数据,在得到业务方、CTO的邮件批准后方可执行,执行前提前做好备份,必要时可逆。
2、所有上线需求必须走工单系统,口头通知视为无效。
3、在对大表做DDL操作时,业务低峰时操作。MySQL 5.6版本之前使用pt-osc工具,5.6版本开始支持online DDL操作。
4、所有线上业务库均必须搭建MHA高可用架构,避免单点问题。
5、给业务方开权限时,密码要用MD5加密,至少16位。权限如没有特殊要求,均为select查询权限,并做库表级限制。
6、删除默认空密码账号。
7、汇总库开启Audit审计日志功能,出现问题时方可追溯。
二、行为规范
1、禁止一个MySQL实例存放多个业务数据库,会造成业务耦合性过高,一旦出现问题会殃及池鱼,增加了定位故障问题的难度。通常采用多实例解决,一个实例一个业务库,互不干扰。
2、禁止在主库上执行后台管理和统计类的功能查询,这种复杂类的SQL会造成CPU的升高,进而会影响业务。
3、批量清洗数据,需要开发和DBA共同进行审查,应避开业务高峰期时段执行,并在执行过程中观察服务状态。
4、促销活动等应提前与DBA当面沟通,进行流量评估,比如提前一周增加机器内存或扩展架构,防止DB出现性能瓶颈。
5、禁止在线上做数据库压力测试。
三、基本规范
1、禁止在数据库中存储明文密码。
2、使用InnoDB存储引擎。支持事务,行级锁,更好的恢复性,高并发下性能更好。
3、表字符集最好统一使用UTF8MB4(UTF8超集)。不会产生乱码风险。
4、所有表和字段都需要添加中文注释。方便他人、方便自己。
5、不在数据库中存储图片、文件等大数据。
图片、文件更适合于GFS分布式文件系统,数据库里存放超链接即可。
6、避免使用存储过程、视图、触发器、事件。
MySQL是OLTP应用,最擅长简单的增、删、改、查操作,但对逻辑计算分析类的应用,并不适合,所以这部分的需求最好通过程序上实现。
7、避免使用外键,外键用来保护参照完整性,可在业务端实现。
外键会导致父表和子表之间耦合,十分影响SQL性能,出现过多的锁等待,甚至会造成死锁。
8、对事务一致性要求不高的业务,如日志表等,优先选择存入MongoDB。
MongoDB其自身支持的sharding分片功能,增强了横向扩展的能力,开发不用过多调整业务代码。
9、禁止使用保留字
要保证你的字段名没有和保留词、数据库系统或者常用访问方法冲突,比如,写的一个ODBC连接程序里有个表,其中就用了DESC作为说明字段名。后果可想而知!DESC 是DESCENDING缩写后的保留词。表里的一个SELECT *语句倒是能用,但我得到的却是一大堆毫无用处的信息。
10、命名规范
禁止使用中文命名。命名都不得超过30个字符的系统限制,变量名的长度限制为29(不包括标识字符@)。命名都采用英文字符,每个单词的首个字母尽量要大写。
对象命名,如存储过程以SP_为前缀;触发器以TR_为前缀;函数以FN_为前缀;主键以PK_为前缀;索引以IX_为前缀。
四、库表设计规范
1、表必须有主键,例如自增主键。
这样可以保证数据行是按照顺序写入,对于SAS传统机械式硬盘写入性能更好,根据主键做关联查询的性能也会更好,并且还方便了数据仓库抽取数据。从性能的角度来说,使用UUID作为主键是个最不好的方法,它会使插入变得随机。
2、慎用分区表。
分区表的好处是对于开发来说,不用修改代码,通过后端DB的设置,比如对于时间字段做拆分,就可以轻松实现表的拆分。但这里面涉及一个问题,查询的字段必须是分区键,否则会遍历所有的分区表。所以应采用切表的形式做拆分,如程序上需要对历史数据做查询,可通过union all的方式关联查询。另外随着时间的推移,历史数据表不再需要,只需在从库上dump出来,即便捷地迁移至备份机上。
五、字段设计规范
1、用DECIMAL代替FLOAT和DOUBLE存储精确浮点数。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mysql> CREATE TABLE t3 (c1 float(10,2),c2 decimal(10,2)); Query OK, 0 rows affected (0.05 sec) mysql> insert into t3 values (999998.02, 999998.02); Query OK, 1 row affected (0.01 sec) mysql> select * from t3; +-----------+-----------+ | c1 | c2 | +-----------+-----------+ | 999998.00 | 999998.02 | +-----------+-----------+ 1 row in set (0.00 sec) |
可以看到c1列的值由999998.02变成了999998.00,这就是float浮点数类型的不精确性造成的。因此对货币等对精度敏感的数据,应该用定点数表示或存储。
2、使用TINYINT来代替ENUM类型。
采用enum枚举类型,会存在扩展的问题,例如用户在线状态,如果此时增加了:5表示请勿打扰、6表示开会中、7表示隐身对好友可见,那么增加新的ENUM值要做DDL修改表结构操作了。
3、字段长度尽量按实际需要进行分配,不要随意分配一个很大的容量。
选择字段的一般原则是保小不保大,能用占用字节少的字段就不用大字段。比如主键,强烈建议用int整型,不用uuid,为什么?省空间啊。空间是什么?空间就是效率!按4个字节和按32个字节定位一条记录,谁快谁慢太明显了。涉及几个表做join时,效果就更明显了。更小的字段类型占用的内存就更少,占用的磁盘空间和磁盘I/O也会更少,而且还会占用更少的带宽。
有不少开发人员在设计表字段时,只要是针对数值类型的全部用int,但这不一定合适,就比如用户的年龄,一般来说,年龄大都在1~100岁之间,长度只有3,那么用int就不适合了,可以用tinyint代替。又比如用户在线状态,0表示离线、1表示在线、2表示离开、3表示忙碌、4表示隐身等,其实类似这样的情况,用int都是没有必要的,浪费空间,采用tinyint完全可以满足需要,int占用的是4字节,而tinyint才占用1个字节。
int整型有符号(signed)最大值是2147483647,而无符号(unsigned)最大值是4294967295,如果你的需求没有存储负数,那么建议改成有符号(signed),可以增加int存储范围。
int(10)和int(1)没有什么区别,10和1仅是宽度而已,在设置了zerofill扩展属性的时候有用。
4、字段定义为NOT NULL要提供默认值。
从应用层角度来看,可以减少程序判断代码,比如你要查询一条记录,如果没默认值,你是不是得先判断该字段对应变量是否被设置,如果没有,你得通过java把该变量置为”或者0,如果设了默认值,判断条件可直接略过。
NULL值很难进行查询优化,它会使索引统计更加复杂,还需要MySQL内部进行特殊处理。
5、尽可能不使用TEXT、BLOB类型。
五、索引设计规范
1、索引不是越多越好,按实际需要进行创建。
索引是一把双刃剑,它可以提高查询效率但也会降低插入和更新的速度并占用磁盘空间。适当的索引对应用的性能至关重要,而且在MySQL中使用索引它的速度是极快的。遗憾的是,索引也有相关的开销。每次向表中写入时(如INSERT、UPDATEH或DELETE),如果带有一个或多个索引,那么MySQL也要更新各个索引,这样索引就增加了对各个表的写入操作的开销。只有当某列被用于WHERE子句时,才能享受到索引的性能提升的好处。如果不使用索引,它就没有价值,而且会带来维护上的开销。
2、不在低基数列上建立索引,例如‘性别’。
有时候,进行全表浏览要比必须读取索引和数据表更快,尤其是当索引包含的是平均分布的数据集是更是如此。对此典型的例子是性别,它有两个均匀分布的值(男和女)。通过性别需要读取大概一半的行。在种情况下进行全表扫描浏览要更快。
3、查询的字段必须创建索引。
如:1、SELECT、UPDATE、DELETE语句的WHERE条件列;2、多表JOIN的字段。
4、避免冗余或重复索引
联合索引IX_a_b_c(a,b,c) 相当于 (a) 、(a,b) 、(a,b,c),那么索引 (a) 、(a,b) 就是多余的,还有一种就是主义主键不需要再次独立建立索引。
六、SQL设计规范
1、不使用SELECT *,只获取必要的字段。
消耗CPU和IO、消耗网络带宽;无法使用覆盖索引。
2、用IN来替换OR。
低效查询
1 |
SELECT * FROM t WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30; |
高效查询—–>
1 |
SELECT * FROM t WHERE LOC_IN IN (10,20,30); |
3、避免数据类型不一致。
会带来隐式转换问题,并且无法使用索引。
1 2 3 |
SELECT * FROM t WHERE id = '19'; #-----> SELECT * FROM t WHERE id = 19; |
4、减少与数据库的交互次数。
1 2 3 4 5 6 7 |
INSERT INTO t (id, name) VALUES(1,'Bea'); INSERT INTO t (id, name) VALUES(2,'Belle'); INSERT INTO t (id, name) VALUES(3,'Bernice'); #-----> INSERT INTO t (id, name) VALUES(1,'Bea'), (2,'Belle'),(3,'Bernice'); Update … where id in (1,2,3,4); Alter table tbl_name add column col1, add column col2; |
5、拒绝大SQL,拆分成小SQL。
低效查询
1 2 3 4 |
SELECT * FROM tag JOIN tag_post ON tag_post.tag_id = tag.id JOIN post ON tag_post.post_id = post.id WHERE tag.tag = 'mysql'; |
可以分解成下面这些查询来代替
高效查询
1 2 3 |
SELECT * FROM tag WHERE tag = 'mysql' SELECT * FROM tag_post WHERE tag_id = 1234 SELECT * FROM post WHERE post_id in (123, 456, 567, 9098, 8904); |
6、禁止使用order by rand()
1 2 3 |
SELECT * FROM t1 WHERE 1=1 ORDER BY RAND() LIMIT 4; #----> SELECT * FROM t1 WHERE id >= CEIL(RAND()*1000) LIMIT 4; |
7、慎用union或union all
慎用union或union all,特别是合并后再进行排序操作的SQL,碰到数据量比较大时,进行优化会非常困难。
8、禁止使用count(*)
InnoDB表避免使用COUNT(*)操作,因内部没有计数器,需要一行一行累加计算,计数统计实时要求较强可以使用memcache或者Redis。如果非要使用最好使用count(主键)。
9、关联查询字段类型一致并建立索引
首先对于查询条件字段只要不是低选择性的一般都需要建立索引,但是在进行联合查询时,如果联合查询字段类型不统一是无法使用到索引的,要注意。
10、关联查询字段字符类型一致
对于关联查询,如果查询条件的字段字符集不一致也会导致无法使用索引的情况,比如utf8和utf8mb4字符集。
11、不使用反向查询,如not in / not like。
无法使用索引,导致全表扫描。
12、不使用%前导的查询,如like ‘%xxx’
无法使用索引,导致全表扫描。
1 2 3 4 5 |
# 低效查询; SELECT * FROM t WHERE name LIKE '%de%'; # 高效查询; SELECT * FROM t WHERE name LIKE 'de%'; |
13、不在索引列进行数学运算和函数运算。
无法使用索引,导致全表扫描。
由于MySQL不像Oracle那样支持函数索引,即使d字段有索引,也会直接全表扫描。例如:
1 |
SELECT * FROM t WHERE YEAR(d) >= 2016; |
应改为—–>
1 |
SELECT * FROM t WHERE d >= '2016-01-01'; |
<转载>
http://mp.weixin.qq.com/s/PtIaqAjs298uH6edEYb2xg
https://mp.weixin.qq.com/s/SQlJrfTWk0CeftXk2DNY2A
然后自己又添加了一些。