现总结工作与学习中关于数据库的规范设计与优化技巧
1.规范背景与目的
MySQL数据库与 Oracle、 SQL Server 等数据库相比,有其内核上的优势与劣势。我们在使用MySQL数据库的时候需要遵循一定规范,扬长避短。
本规范旨在帮助或指导RD、QA、OP等技术人员做出适合线上业务的数据库设计。
在数据库变更和处理流程、数据库表设计、SQL编写等方面予以规范,从而为公司业务系统稳定、健康地运行提供保障。
2.设计规范
2.1数据库设计
禁止使用的sql语句
【高危】
- 禁用update | delete t1 ... where a = xx limit xxx; 这种带limit的更新语句。因为会导致主从不一致,导致数据错乱。
- 禁止使用关联子查询,如update t1 set ... where name in(select...) 效率极其低下
- 禁用procedure、function\trigger\views\ event\外键约束。因为他们消耗数据库资源,降低数据库实例可扩展性。推荐都在程序端实现。
- 禁用insert into .. on duplicate key update.. 在高并发环境下,会造成主从不一致。
- 禁止连表更新语句
2.1.1 库的创建
- 【强制】库的名称必须控制在32个字符以内,库名称格式:业务系统名称_子系统名称,同一模块使用的表名尽量使用统一前缀
- 【强制】创建数据库时必须显式指定字符集,并且字符集只能是utf8或者utf8mb4.创建数据库SQL举例:create database db1 default character set utf8;
2.1.2 表结构
- 【强制】
- 对于关系表,表名和表名之间尽量体现join的关系,如user和group表直接的关系:x_user_group_xx
- 表和列的名称必须控制在32个字符以内,表名只能使用字母、数字和下划线,一律小写
- 表名与模块名强相关,如渠道系统采用“qd_”作为前缀
- 创建表时必须显式指定字符集为utf8或者utf8mb4
- 创建表时必须显式指定表存储引擎类型,如无特殊需求,一律为InnoDB。当需要使用除InnoDB/MyISAM/Memory以外的存储引擎时,必须通过审核才能使用。(InnoDB表支持事务、行锁、宕机恢复、MVCC等关系型数据库重要特性,为业界使用最多的MySQL引擎。)
- 建表必须使用comment 添加注释
- 中间表用于保留中间结果集,名称必须以tmp_开头。备份表名称必须以bak_开头。
- 对于超过100W行的数据量大的表进行alter table时,必须在业务低峰期执行。因为alter table 会产生表锁,期间会阻塞对于该表的所有写入,对于业务可能会产生极大影响
- 【建议】
- 核心表须有创建时间和更新时间
- 表中所有字段加上NOT NULL属性,业务可以根据需要定义DEFAULT值。因为使用NULL值会存在每一行都占用额外存储空间、数据迁移容易出错、聚合函数计算结果偏差等问题
- 表中有blob、text等大字段,垂直查分到其他表中。
- 有些字段可以适当冗余,比如user_name,不仅仅存user_id,减少join查询
2.1.3 列数据类型优化
- 【建议】
- 表中数据值很少的状态status、类型type等字段使用tinytint或者smallint类型节省存储空间
- 不推荐使用blob,text等类型,比较浪费硬盘和内存空间。
- 文本数据尽量用varchar存储。因为varchar是变长存储,比char更省空间。一般verchar类型字段树不要超过2700.
- 时间类型尽量选取timestamp。因为datetime占用8个字节,timestamp占用4个字节
2.1.4 索引设计
- 【强制】
- InnoDB表必须主键为id int/bigint auto_increment,且主键禁止被更新。
- 单个索引中每个索引记录的长度不能超过64k
- 【建议】
- 主键的索引以“pk_”开头,唯一索引以“uk_”开头,普通索引以“idx_”开头,一律使用小写格式,以表名/字段的名称或缩写作为后缀。
- 单个表的索引个数不能超过7个
- 在建立索引时,多考虑建立联合索引,并把区分度最高的字段方在前面。
- 在多表关联的join中,保证驱动表的连接列上有索引,这样join执行效率会更高
- 保证表中互相不存在冗余的索引。
2.1.5 程序DAO设计
1.【建议】
- 对于log或者history类型的表,岁时间增长容易越来越大,因此上线钱DBA必须简历表数据清理或归档方案。
- 主表机芯增、删、改,从表进行查操作
- 对于单表读写比大于10:1的数据行或单个列,可以将热点数据放在缓存里(如mechache或redis),加快访问速度,降低MySQL压力。
2.1.6 规范的建表语句示例
CREATE TABLE user (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(11) NOT NULL COMMENT ‘用户id’
`username` varchar(45) NOT NULL COMMENT '真实姓名',
`email` varchar(30) NOT NULL COMMENT ‘用户邮箱’,
`nickname` varchar(45) NOT NULL COMMENT '昵称',
`avatar` int(11) NOT NULL COMMENT '头像',
`birthday` date NOT NULL COMMENT '生日',
`sex` tinyint(4) DEFAULT '0' COMMENT '性别',
`short_introduce` varchar(150) DEFAULT NULL COMMENT '一句话介绍自己,最多50个汉字',
`user_resume` varchar(300) NOT NULL COMMENT '用户提交的简历存放地址',
`create_time` timestamp NOT NULL COMMENT ‘用户记录创建的时间’,
`update_time` timestamp NOT NULL COMMENT ‘用户资料修改的时间’,
`user_review_status` tinyint NOT NULL COMMENT ‘用户资料审核状态,1为通过,2为审核中,3为未通过,4为还未提交审核’,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_id` (`user_id`),
KEY `idx_username`(`username`),
KEY `idx_create_time`(`create_time`,`user_review_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='网站用户基本信息';
2.2 SQL编写规范
2.2.1 DML语句
- 【强制】
- select 语句必须指定具体字段名称,禁止写成*。因为select *会将不该读取的数据从mysql里读取处理,造成网卡的压力。且表字段一旦更新,单model没有来得及更新的话,系统会报错。—————sql优化
- where条件里等号左右字段类型必须一致,否则无法利用索引。—————sql优化
- 【建议】
- insert into ... value(xxx), (xxx) ...这里的(xxx)不要超过5000个。值过多虽然上线很快,但会引起主从同步延迟。
- select语句中不要使用union,推荐使用union all,并且union子句个数限制在5个以内。因为union all不需要去重,节省数据库资源,提高性能。—————sql优化
- in值列表限制在500以内,这么做事围栏减少底层扫描,减轻数据库压力从而加速查询
- 事务里批量更新数据需要控制数量,进行必要的sleep,做到少量多次。—————优化
- where子句中尽量少使用圈模糊的like条件查询,必须有其他等值或范围查询条件,否则无法利用索引。—————sql优化
- 索引列不要使用函数或表达式,否则无法利用索引****—————sql优化
- 分页查询,当limit起点较高时,可先用过滤条件进行过滤。如select a,b,c from t1 limit 10000,20;优化为: select a,b,c from t1 where id>10000 limit 20;
2.2.1 多表连接
- 【强制】
- 禁止跨db的join语句,因为这样可以减少模块间的耦合,为数据库拆分奠定坚实的基础
- 禁止在业务的更新类SQL语句中使用join,比如update t1 join t2..
- 【建议】
- 不建议使用子查询,建议将子查询SQL拆开结合程序多次查询,或使用join来代替子查询—————sql优化
- 线上环境,多表join不要超过3个表
- 多表连接查询推荐使用别名,且SELECT列表中要用别名引用字段
- 在多表join中,尽量选取结果集较小的表作为驱动表,来join其他表
2.2.3 事务
- 【强制】
- 程序设计必须考虑“数据库事务隔离级别”带来的影响,包括脏读、不可重复度和幻读。线上建议事务隔离级别为repeatable-read
- 【建议】
- 事务中INSERT | UPDATE | DELETE | REPLACE 语句操作的行数控制在2000以内,以及WHERE子句中IN列表的传参个数控制在500以内。
- 批量操作是,需要控制事务处理隔离时间,进行必要的sleep,一般建议值5-10秒
- 对于有auto_increment属性字段的表的插入操作,并发需要控制在200以内
- 事务里包含SQL不超过5个(支付业务除外)。因为过长的事务会导致锁数据较久,MySQL内部缓存、连接消耗过多等雪崩问题。
- 对于MySQL主从延迟严格敏感的select语句,请开启事务强制访问主库
2.2.4 排序和分组
- 【建议】
- 减少使用order by,和业务沟通能不排序就不排序,或将排序放到程序端去做。order by、group by、distinct 这些语句较为耗费CPU,数据库的CPU资源是极其宝贵的。—————sql优化
- order by、group by、distinct这些SQL尽量利用索引直接检索出排序好的数据。如where a=1 order by可以利用key(a,b)。
- 包含了order by、group by、distinct这些查询的语句,where条件过滤出来的结果集请保持在1000行以内,否则SQL会很慢。
不断积累中,未完待续...