
关注本公众号,一天一个知识点,持续精进!


00、引言
数据库无处不在,几乎是现在所有软件系统的标配。为此,数据库技能实乃我们程序员居家出行必备良药。总结一个可以不断增长的经验清单,让我们能够站在前人的肩膀上,大大减少掉坑场景的发生频次。
本文以MySQL数据库系统为例进行说明,其他数据库系统可对比参考。
01、避坑指南
一、基础规范
【1】必须使用InnoDB存储引擎
InnoDB存储引擎支持事务、行级锁、并发性能更好、CPU及内存缓存页优化使得资源利用率更高。
【2】新库默认使用utf8mb4字符集
标准,万国码,无需转码,无乱码风险。
【3】数据表、数据字段必须加入中文注释
注释需准确,可读性强,尽量做到见名便知义。
【4】禁止使用存储过程、视图、触发器、Event
该规则主要适用于高并发访问、海量数据的应用场景。数据库擅长存储与索引,属于I/O操作密集型;业务逻辑属于CPU操作密集型,不建议放在数据库,这样可以减少数据库运算量,降低数据库压力;否则,在高并发、海量数据的应用场景很容易将数据库系统冲垮。
【5】禁止存储大容量的文件或二进制多媒体数据
将大容量的文件和二进制多媒体数据(如图片、音频、视频等)存储在文件系统中,数据库里可存放相应的大容量文件/数据的URI。
二、命名规范
【6】只允许使用内网域名,而不是IP连接数据库。
如果对应IP的数据库服务器发生宕机,只需要修改DNS的配置即可,而不需要修改程序的源代码,这对运维或运行环境发生改变的时候都会带来极大的方便。除了数据库之外,缓存(memcache、redis)的连接,服务(service)的连接都必须使用内网域名。
【7】线上环境、开发环境、测试环境数据库内网域名遵循命名规范。
比如:业务名称:abc
线上环境:com.abc.db
开发环境:com.abc.rdb
测试环境:com.abc.tdb
从库在名称后加-s标识,备库在名称后加-ss标识
线上从库:com.abc-s.db
线上备库:com.abc-sss.db
【8】库名、表名、字段名:小写,下划线风格,不超过32个字符,必须见名知意,禁止拼音英文混用。
【9】表名t_xxx,非唯一索引名idx_xxx,唯一索引名uniq_xxx
三、表设计规范
【10】单实例表数目必须小于500
【11】单表列数目必须小于30
【12】表必须有主键,例如自增主键
a)主键递增,数据行写入可以提高插入性能,可以避免page分裂,减少表碎片提升空间和内存的使用
b)主键要选择较短的数据类型, Innodb引擎普通索引都会保存主键的值,较短的数据类型可以有效的减少索引的磁盘空间,提高索引的缓存效率
【13】禁止使用外键,如果有外键完整性约束,需要应用程序控制
该规则主要适用于高并发访问、海量数据的应用场景。外键会导致表与表之间耦合,update与delete操作都会涉及相关联的表,十分影响sql 的性能,甚至会造成死锁。
四、字段设计规范
【14】必须把字段定义为NOT NULL并且提供默认值
a)NULL的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化
b)NULL 这种类型MySQL内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多
c)NULL值需要更多的存储空间,无论是表还是索引中每行中的NULL的列都需要额外的空间来标识
d)对NULL 的处理时候,只能采用is null或is not null,而不能采用=、in、<、<>、!=、not in这些操作符号。如:where name != ’User1’,如果存在name为null值的记录,查询结果就不会包含name为null值的记录
【15】禁止使用TEXT、BLOB类型
上述类型会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,导致内存命中率急剧降低,影响数据库性能
【16】禁止使用小数存储货币
可采用整数存储货币。以人民币为例,如果将货币单位设定为分即可实现将货币转换为整数。
【17】必须使用varchar(20),而非整数存储手机号。理由如下:
a)涉及到区号或者国家代号,可能出现+-()
b)手机号没有做数学运算的需求
c)varchar可以支持模糊查询,例如:like“138%”
【18】禁止使用ENUM,可使用TINYINT代替
a)增加新的ENUM值要做DDL操作
b)ENUM的内部实际存储就是整数
五、索引设计规范
【19】查询条件的字段必须要有索引,可根据数据特点并非要求所有查询的字段都建立索引
没有索引的话,mysql 会全表扫描,那样会锁定整张表所有的记录;此时其他事务不能修改不能删除不能添加,进而影响数据库系统的执行性能
【20】单表索引建议控制在5个以内
【21】单索引字段数不允许超过5个
字段超过5个时,实际已经起不到有效过滤数据的作用了
【22】禁止在更新十分频繁、区分度不高的属性上建立索引
a)更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能
b)“性别”这种区分度不大的属性(值域太小),建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类似
【23】建立组合索引,必须把区分度高的字段放在前面
能够更加有效的过滤数据
六、SQL使用规范
【24】禁止使用SELECT *,只获取必要的字段,需要显式说明列属性
a)读取不需要的列会增加CPU、IO、Network消耗
b)不能有效的利用覆盖索引
c)使用SELECT *容易在增加或者删除字段后出现程序BUG
【25】禁止使用INSERT INTO t_xxx VALUES(xxx),必须显式指定插入的列属性
容易在增加或者删除字段后出现程序BUG
【26】禁止使用属性隐式转换
SELECT uid FROM t_user WHERE phone=13812345678 会导致全表扫描,而不能命中phone索引(phone字段为varchar类型,由于不存在该记录,必然需要全表扫描;修改为phone='13812345678'即可)
【27】禁止在WHERE条件的属性上使用函数或者表达式
SELECT uid FROM t_user WHERE from_unixtime(day)>='2018-05-19' 会导致全表扫描。正确的写法是:SELECT uid FROM t_user WHERE day>= unix_timestamp('2018-05-19 00:00:00')
【28】禁止单条件的负向查询,以及%开头的模糊查询
a)负向查询条件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,会导致全表扫描
b)%开头的模糊查询,会导致全表扫描。
【29】禁止大表使用JOIN查询,禁止大表使用子查询
会产生临时表,消耗较多内存与CPU,极大影响数据库性能
【30】禁止使用OR条件,必须改为IN查询
旧版本Mysql的OR查询不能利用索引机制
【31】应用程序必须捕获SQL异常,并有相应处理
七、行为规范
【32】禁止使用应用程序配置文件内的帐号手工访问线上数据库
【33】禁止非DBA对线上数据库进行写操作,修改线上数据需要提交工单,由DBA执行,提交的SQL语句必须经过测试
【34】分配非DBA以只读帐号,必须通过VPN+跳板机访问授权的从库
【35】开发、测试、线上环境隔离
八、架构设计
【36】对软件可扩展性的过度追求,促使将对象设计的时候,拆得过于离散,造成系统中有大量复杂的join语句;MySQL等数据库系统由于锁的机制,主要优势在于处理简单逻辑的查询,如果有大量复杂语言,会降低数据库性能
【37】避免对数据库的过度依赖,将一些业务逻辑也放在数据库中进行处理
【38】避免过度理想化系统的用户体验,使大量非核心业务消耗过多的资源,如对大量不需要实时更新的数据做了实时统计计算
02、小结
综上,本文以MySQL数据库系统为研究对象,结合了包括《58到家数据库30条军规》在内的互联网多篇数据库优化的技巧与经验,总结了数据库避坑指南,其目的是为了掌握20%的最常用的技能,能够解决我们在数据库方面的80%问题。本文是数据库系列的第一篇,后续我们会对数据库的事务与锁机制、设计问题等进行拆解,敬请期待。





