转载 | 机器之心
编辑 | 雷课小雷
最近涉及数据库相关操作较多,公司现有规范也不是太全面,就根据网上各路大神的相关规范,整理了一些自用的规范用法,万望指正。
数据库环境
开发可读写,开发人员可以通过工具修改表结构。
开发人员不允许直接在线上环境进行数据库操作,如果需要操作必须找DBA进行操作并进行相应记录,禁止进行压力测试。
重点的问题,各个环境的mysql服务器对应的用户权限,一定要做到权限划分明确,有辨识度,能具体区分业务场景等。
命名规范
使用有意义的英文词汇,词汇中间以下划线分隔。(不要用拼音)
只能使用英文字母,数字,下划线,并以英文字母开头。
库、表、字段全部采用小写,不要使用驼峰式命名。
避免用ORACLE、MySQL的保留字,如desc,关键字如index。
命名禁止超过32个字符,须见名之意,建议使用名词不是动词
数据库,数据表一律使用前缀
临时库、表名必须以tmp为前缀,并以日期为后缀
备份库、表必须以bak为前缀,并以日期为后缀
在 MySQL 中,数据库和表对就于那些目录下的目录和文件。因而,操作系统的敏感性决定数据库和表命名的大小写敏感。
Linux下大小写规则
表命名
同一个模块的表尽可能使用相同的前缀,表名称尽可能表达含义。所有日志表均以 log_ 开头字段命名
各表之间相同意义的字段应同名。各表之间相同意义的字段,以去掉模块前缀的表名_字段名命名。
外键字段用表名_字段名表示其关联关系。
表的主键一般都约定成为id,自增类型,是别的表的外键均使用xxx_id的方式来表明。
索引命名
非唯一索引必须按照“idx_字段名称_字段名称[_字段名]”进行命名
主键约束:pk_表名称。
表引擎取决于实际应用场景;日志及报表类表建议用myisam,与交易,审核,金额相关的表建议用innodb引擎。如无说明,建表时一律采用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,用来标示数据是否被删除,原则上数据库数据不允许物理删除。
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),以获得性能提升。
索引的基本规范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));
主键准则表必须有主键
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的限制
MYISAM 存储引擎索引长度的总和不能超过 1000 字节
BLOB 和 TEXT 类型的列只能创建前缀索引
MYSQL 目前不支持函数索引
使用不等于 (!&#61; 或者 <>) 的时候, MYSQL 无法使用索引。
过滤字段使用函数运算 (如 abs (column)) 后, MYSQL无法使用索引。
join语句中join条件字段类型不一致的时候MYSQL无法使用索引
使用 LIKE 操作的时候如果条件以通配符开始 (如 ‘%abc…’)时, MYSQL无法使用索引。
使用非等值查询的时候, MYSQL 无法使用 Hash 索引。
只传参数&#xff0c;比传递SQL语句更高效
一次解析&#xff0c;多次使用
降低SQL注入概率
会导致索引失效
必须是最左前缀
不可能同时用到两个范围条件
不使用%前导的查询&#xff0c;如like “%ab”
无法使用索引&#xff0c;导致全表扫描
全表扫描导致buffer pool利用率降低
限制分页展示的页数只能点击上一页、下一页采用延迟关联
如何正确的使用分页&#xff1f;假如有类似下面分页语句&#xff1a;SELECT * FROM table ORDER BY id LIMIT 10000, 10由于MySQL里对LIMIT OFFSET的处理方式是取出OFFSET&#43;LIMIT的所有数据&#xff0c;然后去掉OFFSET&#xff0c;返回底部的LIMIT。所以&#xff0c;在OFFSET数值较大时&#xff0c;MySQL的查询性能会非常低。可以使用id > n 的方式进行解决&#xff1a;使用id > n 的方式有局限性&#xff0c;对于id不连续的问题&#xff0c;可以通过翻页的时候同时传入最后一个id方式来解决。http://example.com/page.php?last&#61;100select * from table where id<100 order by id desc limit 10
//上一页http://example.com/page.php?first&#61;110select * from table where id>110 order by id desc limit 10
这种方式比较大的缺点是&#xff0c;如果在浏览中有插入/删除操作&#xff0c;翻页不会更新&#xff0c;而总页数可能仍然是根据新的count(*) 来计算&#xff0c;最终可能会产生某些记录访问不到。为了修补这个问题&#xff0c;可以继续引入当前页码以及在上次翻页以后是否有插入/删除等影响总记录数的操作并进行缓存
select * from table where id >&#61; (select id from table order by id limit #offset#, 1)
拒绝大SQL&#xff0c;拆分成小SQL
充分利用QUERY CACHE
充分利用多核CPU
使用in代替or&#xff0c;in的值不超过1000个
禁止使用order by rand()
使用EXPLAIN诊断&#xff0c;避免生成临时表
EXPLAIN语句(在MySQL客户端中执行)可以获得MySQL如何执行SELECT语句的信息。通过对SELECT语句执行EXPLAIN&#xff0c;可以知晓MySQL执行该SELECT语句时是否使用了索引、全表扫描、临时表、排序等信息。尽量避免MySQL进行全表扫描、使用临时表、排序等。详见官方文档。
用union all而不是union
union和union all关键字都是将两个结果集合并为一个&#xff0c;但这两者从使用和效率上来说都有所不同。
union在进行表链接后会筛选掉重复的记录&#xff0c;所以在表链接后会对所产生的结果集进行排序运算&#xff0c;删除重复的记录再返回结果。如&#xff1a;select * from test_union1union select * from test_union2
这个SQL在运行时先取出两个表的结果&#xff0c;再用排序空间进行排序删除重复的记录&#xff0c;最后返回结果集&#xff0c;如果表数据量大的话可能会导致用磁盘进行排序。而union all只是简单的将两个结果合并后就返回。这样&#xff0c;如果返回的两个结果集中有重复的数据&#xff0c;那么返回的结果集就会包含重复的数据了。
从效率上说&#xff0c;union all要比union快很多&#xff0c;所以&#xff0c;如果可以确认合并的两个结果集中不包含重复的数据的话&#xff0c;那么就使用union all&#xff0c;如下&#xff1a;select * from test_union1 union all select * from test_union2
程序应有捕获SQL异常的处理机制
禁止单条SQL语句同时更新多个表
不使用select * &#xff0c;SELECT语句只获取需要的字段
消耗CPU和IO、消耗网络带宽
无法使用覆盖索引
减少表结构变更带来的影响
因为大&#xff0c;select/join 可能生成临时表
UPDATE、DELETE语句不使用LIMIT
INSERT语句必须显式的指明字段名称&#xff0c;不使用INSERT INTO table()
INSERT语句使用batch提交(INSERT INTO table VALUES(),(),()……)&#xff0c;values的个数不超过500
统计表中记录数时使用COUNT(*)&#xff0c;而不是COUNT(primary_key)和COUNT(1) 备注&#xff1a;仅针对Myisam
数据更新建议使用二级索引先查询出主键&#xff0c;再根据主键进行数据更新
禁止使用跨库查询
禁止使用子查询&#xff0c;建议将子查询转换成关联查询
针对varchar类型字段的程序处理&#xff0c;请验证用户输入&#xff0c;不要超出其预设的长度&#xff1b;
分表规范
单表一到两年内数据量超过500w或数据容量超过10G考虑分表&#xff0c;需提前考虑历史数据迁移或应用自行删除历史数据&#xff0c;采用等量均衡分表或根据业务规则分表均可。要分表的数据表必须与DBA商量分表策略日志类数据不建议存储在MySQL上&#xff0c;优先考虑Hbase或OceanBase&#xff0c;如需要存储请找DBA评估使用压缩表存储。
END
往期精选
Python代码转Latex公式
一行 Python 代码实现并行
Python小插件堪称论文必备神器
Python下载文件的11种方式
18个Python高效编程技巧&#xff01;
数据可视化的基本流程总结
关注雷课
学习干货