@yanglfyangl
2018-07-25T09:47:03.000000Z
字数 12180
阅读 918
2.1 基本规范
2.1.1 规则:数据库属于服务内部实现,不允许跨服务共享。
每个服务有自己独立的库(非数据库实例),不允许把数据库表,访问账号等信息直接暴露给其他服务访问。服务的实现者可以选择适合自身业务的数据库类型。
反例:服务A和服务B共用一个库。
2.1.2 规则:禁止在关系数据库中存储图片、文件等大数据。
2.1.3 规则:任何表的设计都要考虑到数据的删除策略,表中的数据不能无止境的增长而不删除。
反例:某服务历史数据没有转储功能,日积月累,出现千万级别的大表。
2.1.4 规则:要考虑数据库一致性问题。
一致性常见两类为强一致性和最终一致性。要从各个维护考虑数据一致性。
分类 举例
表之间一致性 业务拆分为大表和小表,创建/删除操作需要事务保证。
并发写一致性 合法性检测到修改要保证是原子操作,比如限制数量。
数据冗余一致性 表字段冗余,要保证类型一致,修改同步。
数据迁移一致性 数据迁移前后,要进行一致性,保证表数量/记录数/checksum不变。
主从库一致性 因为主从库复制为异步复制,不是强一致性。
数据库和缓存一致性 合理设计程序访问数据库和缓存的访问逻辑,保证数据库与缓存的数据一致性。
微服务之间一致性 微服务之间可能有些共享依赖数据。可选的方案有对流程优化(参考互联网网购),基于租约的定期校验等。
2.1.5 建议:合理控制数据库规模和数量。
建议单表数据量不超过1000w。
建议单库不超过100个表。
建议单表字段数上限控制在20~50个。
反例:某服务出现单库600+表,重要数据和不重要数据全存在一个库中,且有张表达到1000w+。
2.1.6 建议:大规模存储要考虑数据库容量估算。
容量估算
单实例存储空间: 总共多少张表,每张表最大存多少万条记录,每条记录占用多少K空间。三者相乘可以得到大概多少G的数据空间。
预留binlog日志的空间。
备份预留存储空间: 备份保留天数乘以存储空间。
2.1.7 建议:大规模存储要考虑数据库性能估算。
主要是根据服务的SLA指标(吞吐能力,请求时延)来推算。以下是一些参考指标。
Mysql单实例批插吞吐能力最低:1K/s。
Mysql单实例并发读吞吐能力最低:10K/s。
Redis单实例并发读写吞吐能力5w-10w/s。
RedisCluster并发读写吞吐能力: 5w/s*N实例数。三节点集群在实体机可达到100w/s。
为异地容灾的数据复制,估算网络带宽。
可选估算公式:每秒同时变化记录数*每条记录数字节数*8
示例:告警每秒1000条持续上报:1K*1K*8 = 8Mbps/s
2.1.8 建议:提前进行资源估算,估算出对服务器要求。
根据节点硬件资源内存/磁盘/CPU,估算实例数。
类别 说明
服务器 数据库单实例一个节点;
数据库主从2个节点;
RedisCluster/Zookeeper一般要求3或5个节点。
内存 MySQL5.6单实例内存缺省起步价800M。
表数量小于2000张可以调小监控表数量,减小到600M。 (参数:performance_schema_max_table_instances)
关闭性能监控可以降低到300M。(参数:performance_schema )
Redis最大5G内存。考虑全同步。内存至少预留Redis实际内存的2倍)
CPU MySQL是多线程,但Redis单实例是单线程最多只能占满一个核。
磁盘 估算数据实际大小+索引空间+复制日志+备份空间。
(Redis预留磁盘空间最多等于Redis最大内存)
网络带宽 平均包大小*最大并发数。考虑主从复制带宽。
异地容灾还要预留单独带宽。
2.1.9 数据库实例和库拆分规范
1. 分布式场景,考虑节约资源费用和维护成本,资源, 缺省一个产品内服务可以共用数据节点,缺省一个服务组内的服务共用一个RDS实例。
2. 服务组内部,根据数据重要程度,表数量,单表记录数,可进一步拆分成不同实例和不同库。
当复制速度成为瓶颈,单机部署多实例可以间接提高复制能力。当IO成为瓶颈,考虑SSD。
单实例一般10G存储,单库100张表,单表100万记录,单表60个字段,单表5个索引,单记录2K字节。
3.禁止多个微服务共用一个库。
2.1.10 建议:大规模存储要重点考虑数据库存储设计。
以下列出需要重点考虑的因素。
业务模型定义:OLTP还是OLAP, CAP选哪两个,ER关系:一对多还是多对多,最终一致性还是强一致性
垂直分区,解决表数量太多,把业务关系密切的表放到同一个库,间接提高性能和可靠性,实施成本低。
水平分区,解决单表记录数大,单个库存储不下或有性能压力,把单表拆分成多张表,保存到不同库/实例实现可扩展性,选择拆分维度很重要,实施成本高。
最后历史数据要定期转储成文件,或者大数据(如HBase)中,避免耗尽整个磁盘空间。
每张表数据量建议控制在100w以内。
单表数据量过大,无论是SQL查询,一致性校验,备份恢复都带来影响。
读写分离,可以考虑将高热点高吞吐能力要求的数据缓存到Redis。
关系数据库的读能力是有限的,读吞吐能力约1w/s,对于高吞吐率,读多写少的场景,可以把热点数据缓存到高速缓存Redis。具体做法参见: 6 参考资料 缓存最终一致性问题。
2.1.11 建议:数据库实例和库拆分粒度要合适。
分布式场景,考虑节约资源费用和维护成本, 缺省一个产品内服务可以共用数据节点,缺省一个服务组内的服务共用一个RDS实例。
服务组内部,根据数据重要程度,表数量,单表记录数,可进一步拆分成不同实例和不同库。
当复制速度成为瓶颈,单机部署多实例可以间接提高复制能力。当IO成为瓶颈,可以考虑使用实体机,甚至考虑SSD。
单实例一般10G存储,单库100张表,单表100万记录,单记录2K字节。
禁止多个微服务共用一个库。
反例:某服务单库有620张表,其中业务相对不重要的t_log表有1300多万,伴随删除语句导致全表扫描等, 耗资源导致。
delete from t_log where occurtime='2015-12-11 08:48:01'
2.2 部署规范
2.2.1 建议:建议数据库主备部署到不同AZ。
Region之间的网络延迟一般大于50ms(50ms-200ms), 比如深圳和西安的网络延迟就是50ms, 因此不允许跨Region直接访问数据库。
同一个Region下的AZ可用区之间的网络延迟一般小于2ms,因此把数据库主备部署在不同AZ可以提高可用性。
Region和AZ定义:
a) 区域(Region):从地理位置和网络时延维度划分,同一个Region内共享对象存储、VPC网络、弹性IP、镜像等公共服务。
b) 可用区(AZ,Availability Zone):一个AZ是一个或多个物理数据中心的集合,有独立的风火水电,AZ内逻辑上再将计算、网络、存储等资源划分成多个集群。一个Region中的多个AZ间通过高速光纤相连,以满足用户跨AZ构建高可用性系统的需求。
2.2.2 建议:应用节点就近部署到同Region下的数据节点AZ,网络延迟应该小于2ms,禁止跨Region直接访问数据库。
2.2.3 建议:数据节点和应用节点分开部署。
应用节点一般部署无状态服务,而数据节点部署有状态有生命的数据库服务,从可维护和安全性角度都建议分开独立部署。
2.2.4 建议:服务和对应数据库访问的隔离机制。
隔离单位为服务名。即每个数据库实例的库只能唯一归属于一个服务。每个服务只能访问自己的数据库。
2.3 表定义规范
2.3.1 规则:表的设计要考虑扩展性。
业务发展一般都会在表新增一些字段,前期表设计预留一定的扩展字段,可以避免表结构升级带来麻烦。
建议预留X个整形扩展字段,Y个字符串扩展字段,最后预留1个保存JSON格式的字符串扩展字段。
2.3.2 规则:表的设计要考虑兼容性。
只允许新增字段,不允许删除字段。
SQL插入字段必须指定列名。
禁止select *。
预留扩展字段,尽量避免业务发展新增字段带来DDL变更。
说明
char 和varchar 在使用上没有大的区别,访问可变长字符时无需额外的去空格处理,且在接口程序中也能保持一致,存储性能不会比定长有太大差异,而查询处理效率会有大的提升。总的来说,变长的查询性能更好,而定长的存储性能更优。
2.3.3 规则:禁止同一个字段在不同的表之间用不同的数据类型。
说明
不同的数据类型会形成隐式转换,也就是对字段加上了转换函数。这时是不会用到索引的。
2.3.4 规则:Mysql缺省使用INNODB存储引擎,表字符集使用UTF8MB4字符集。
2.3.5 建议:保证不同表中相同的字段名称必须相同。
说明
因此在增加字段时首先到数据库中找是否存在相同含义的字段名, 例如不允许存在表A, 定义了CellName字段,在表B中却定义成Cell_Name字段, 并且字段的大小写都要完全相同。
2.3.6 建议:尽量通过业务代码保证数据的一致性,不要过多依赖主外键来保持数据一致性, 主外键关系会降低更新及插入数据的性能。
2.3.7 建议:表字段原则上不得为NULL,必须初始化默认值(有特殊需要的请另处理)。建议:不建议表中存储过多的null值,要考虑使用not null约束。或者,可以考虑字符串使用NA,数值型用0作为缺省值。
说明:对于字符串型的字段,使用空字符’’作为字段默认值。
对于整型的字段,使用0作为字段默认值。
如果索引字段有为NULL,则影响索引效率。NULL字段会增大存储空间。
说明
空值亦占用存储空间,对程序的逻辑判断非常危险,容易出错,对SQL语句存在隐患,会出现意想不到的结果。
2.3.8 建议:尽可能不使用TEXT、BLOB类型。
2.3.9 建议:存储时间(精确到秒)建议使用TIMESTAMP类型,因为TIMESTAMP使用4字节,DATETIME使用8个字节。
2.3.10 建议:所有表要显示指定主键,类型采用bigint。
2.3.11 建议:表禁止使用外键。
2.3.12 建议:添加一个datachangetime字段,并设置为TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,实时记录表的更新时间。
2.3.13 建议:表的设计要尽量满足第二范式(2NF),基于提升性能的考虑可以适当增加冗余而不必满足第三范式(3NF)。
说明
【第一范式 1NF】
定义:表中每一条记录的每个一个字段值,都是不可再分的最小数据单位。
解释:例如,(工号,姓名,电话号码)组成一个表,由于一个人可能有办公电话和家庭电话,因此该表不符合1NF,规范成为1NF有三种方法:
重复存储工号和姓名,主键只能是电话号码;
工号为主键,把电话号码分解为“单位电话”和“住宅电话”两个属性;
工号为主键,但强制每条记录只能有一个电话号码。
以上三个方法,第一种方法最不可取,按实际情况选取后两种情况。
【第二范式 2NF】
定义:在满足1NF的基础上,每一个非主键字段必须完全依赖于主键。只有在复合字段作主键时,才可能出现不满足2NF的情况。
解释:例如,(学号,课程号,学分,成绩)组成一个表,主键为(学号,课程号)。 在应用中使用该表时可能存在以下问题:
数据冗余:假设同一门课由40个学生选修,学分字段就重复40次;
更新异常:若调整了某课程的学分,学分字段的值都要更新,有可能会出现同一门课学分不同;
插入异常:如果开设新的课程,由于还没人选修(主键中少了学号),只能等有人选修才能把新开设的课程和学分存入。
删除异常:若学生已经结业,从当前数据库删除选修记录。某些门课程新生尚未选修,则此门课程及学分记录无法保留。
存在以上问题的原因是,非主键字段“学分”仅“部分依赖”于主键(学号,课程号),也就是“学分”字段仅函数依赖于“课程号”字段。该表存在部分依赖的字段“学分”。
解决方法:采用纵向分表,将部分依赖的字段抽出来建立一个新表,该表可分解为(学号,课程号,成绩)和(课程号,学分)两个表,两个表之间通过“课程号”作为外键关联。
【第三范式 3NF】
定义:在满足1NF和2NF的基础上,所有非主键字段对任何主键字段都不存在传递依赖。
解释:例如,(学号,学生姓名,系号,系名,系地址)组成一个表,主键为(学号),由于主键是单个字段,因此没有部分依赖的问题,肯定满足2NF。但是,在应用中使用该表时可能存在以下问题:
存在大量的冗余,有关学生所在的几个字段(系号,系名,系地址)将会重复重复存储。
存在以上问题的原因是,存在传递依赖而造成,“学号”能够决定“系号”,“系号”能够决定“系地址”,“学号”不能够直接决定“系地址”,因此“学号”对“系地址”的函数决定是通过传递依赖“系号->系地址”实现的。
解决方法:采用纵向分表,将存在传递依赖的字段抽出来组成新表,该表可分为(学号,学生姓名,系号)和(系号,系名,系地址)两个表,两个表之间通过“系号”关联。
3 安全规范
3.1 规则:数据库中密码和其他敏感数据必须加密存储。
禁止在数据库中存储明文密码,对于客户敏感信息要加密存储,如银行账号。
3.2 规则:防止SQL注入,使用prepared statement。
3.3 规则:SHELL脚本,定时任务,环境变量,文件,操作日志,localmessage等中不能有数据库用户密码和其他的敏感信息。
3.4 规则:为规避外购件贸易风险,服务要支持两种关系数据库。
一方面公司要求针对外购件贸易合规风险,要有外购件替代措施, 因此CloudSOP平台的基础服务都要求支持Mysql和公司自研的Gauss数据库。
MySQL禁止贸易/使用国家:
Any company or national of Cuba, Iran, North Korea, Sudan, and Syria. Licenses to these countries and parties are presumed denied.
3.5 规则:使用命令行工具涉及输入密码的,尽量通过交互式输入密码,避免被ps出密码,或记录到系统日志中。
4 开发规范
4.1 命名规范
4.1.1 规则:命名不能和数据库的关键字重复。
Mysql关键字列表见官方: http://dev.mysql.com/doc/refman/5.6/en/keywords.html
4.1.2 规则:对于对象名称(包括库名,表名、字段名、索引名等)必须使用小写字母, 并采用下划线分割。
4.1.3 规则:对于对象名称(包括库名,表名、字段名、索引名等)长度需要有所限制,所有的对象名称长度不得超过30。
4.1.4 规则:表别名要简短。通常,使用小写的字母作为别名。
4.1.5 规则:遵循对象命名规范。
对象名 前缀 范例 说明
库 无 privilegedb 服务名前缀(或简写)+db。
服务PrivilegeService的库名为privilegedb
表 t_
tbl_ t_tablename
tbl_tablename t_表名
tbl_表名
普通视图 v_ v_viewname v_视图名
索引 idx_ idx_tblname_indexname 索引名(indexname)自定义,可以包含表名,但:
1、索引命名尽量缩短。2、索引名称全局唯一。
主键约束 pk_ pk_tablename 如果表名过长,则用表名的缩写表示,尽量使用通用缩写或去元音的缩写方式。
存储过程 p_ p_procedurename p_存储过程名
函数 f_ f_functionname f_函数名
临时表/临时库 tmp_ tmp_table
tmp_dbname_201160319 以tmp为前缀,并以日期为后缀
备份表/临时库 bak_ bak_table
bak_dbname_201160319 以bak为前缀,并以日期为后缀
4.2 索引规范
4.2.1 规则:表必须有主键。
4.2.2 规则:如果确认索引是唯一的,将它定为唯一索引。
4.2.3 规则:禁止创建冗余索引。避免索引的隐式转换。
不要在已经为表主键的列集合上再创建索引。
合理创建联合索引(a,b,c) 相当于 (a) 、(a,b) 、(a,b,c)。
4.2.4 规则:依照常用的查询条件和数据分布来决定索引的创建。
4.2.5 规则:创建索引必须有明确的使用目的,必须确保会被经常访问,不允许创建无用的索引。
说明
索引的存储需要占用空间,更新索引也需要耗用资源,过多的索引会降低数据库修改操作的性能和浪费存储空间,创建索引的脚本应该注明创建索引的理由。
4.2.6 规则:不要在选择性较低的字段上建立索引。
说明
在选择性较低的字段使用索引,不但不会降低逻辑I/O,相反往往会增加大量逻辑I/O降低性能。除非SQL语句本身具备使用快速全索引扫描的特点,因此,通常不建议在选择性较低的字段上建立索引。
反例:1、比如在用户的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。
2、如果某个列在大部份情况都是取默认值(表定义中的Default值),该列不应该建立索引。
4.2.7 规则:不要在索引列进行数学运算或函数运算。
无法使用索引,导致全表扫描。
反例:select a from table where id + 1 = 100
4.2.8 规则:单张表中索引数量不超过5个。单个索引中的字段数不超过5个。
索引在增加查询定位速度的同时,也会降低插入、删除和修改索引字段的速度,因此单表的索引数目应该进行严格的控制。
4.2.9 建议:尽量不要在较长字符串(长度>50)的字段上建立索引,如char(1000)、varchar(1000)等。
说明
这会导致索引非常庞大,索引高度过高,效率很低。可以考虑使用Mysql的前缀索引来限制长度。
4.3 事务规范
4.3.1 规则:多表写操作要保证事务完整性。
缺省单条SQL语句就是一个事务,但是涉及多表写操作,需要显示开启事务,才能保证数据完整性。
反例:多表操作没开启事务,可能存在A表修改成功,B表修改失败,破坏数据完整性。
4.3.2 规则:多表写操作事务保证表的操作顺序,避免死锁。
多表写操作要保证顺序统一,否则可能会导致死锁。
反例: X线程锁表顺序为AB,Y线程锁表顺序为BA。并发场景会导致死锁。
4.3.3 规则:保持事务短小,事务占有时间越短越好。
事务长时间锁表会严重阻塞其他SQL语句的执行。程序应有捕获SQL异常的处理机制。
要么尽快提交,要么rollback显式回滚。
不事务无关操作放到事务外面, 减少事务相关资源的占用。
4.4 访问/连接池规范
4.4.1 规则:可靠性角度,访问数据库必须使用连接池。
禁止每次与数据库的交互都得进行一次连接。
数据库连接池(如Java的dbcp)一般做了很多可靠性保护,比如网络闪断,自动重连, 连接数限制,定期回收连接,设置超时时间等措施。
反例:某服务直接通过jdbc连接数据库,当出现网络闪断/数据库重启后,访问数据库失败。
4.4.2 规则:数据库连接池必须具有自动重连的功能。
4.4.3 规则:数据库连接池必须设置合理的超时时间。
客户端超时时间设置非常重要,且不同数据库超时单位还有区别。否则会导致程序访问数据库长时间挂死等严重问题。测试可以用iptables来模拟网络挂住的现象。
4.4.4 规则:数据库连接池必须设置最大值。
服务端有总连接数限制,缺省500个连接。客户端则根据单实例可能的并发线程数设置一个上限,同时还要考虑还要考虑多实例部署。
反例: 客户端连接数设置等于服务端连接数,一方面连接数过大使用不当可能影响全局,另外一方面预算要考虑多实例部署场景。
4.4.5 规则:保持连接短小,连接占有时间越短越好。
数据库连接是宝贵的资源,连接池使用完毕要尽快释放,避免连接泄露。
反例:某服务申请连接后没有释放,连接泄露,导致连接耗尽,功能不可用。
4.4.6 规则:避免同一个线程占用两个连接导致死锁。
4.5 SQL规范
4.5.1 规则 SELECT中不要使用*代替所有列,应该显示指明查询列。
一方面按需获取指定列可以节约系统资源,另外从兼容性角度可以减少表变化带来的影响。
4.5.2 规则:INSERT语句必须指定字段列表。
严禁使用insert into tablename values ( … )方式,应使用insert into tablename ( … ) values ( … )方式。
4.5.3 规则:SQL中避免出现now()、rand()、sysdate()、current_user()等不确定结果的函数。
4.5.4 规则:重要SQL必须被索引:update、delete的where条件列、order by、group by、distinct字段、多表join字段。
4.5.5 规则:禁止隐式转换。数值类型禁止加引号;字符串类型必须加引号。
4.5.6 规则:涉及到多表连接的SQL语句中(大于1个表),要为每个表指定别名。
4.5.7 规则:判断是否为“空”只能用is null或is not null,严禁使用比较运算符进行判断。
说明
= null各种数据库解析不一致,会出现意想不到的结果。
4.5.8 规则:尽量避免使用触发器、外键约束、UDF、events等。
让数据库做最擅长的事,降低业务耦合度,为可扩展性(如分片)留有余地,避开BUG。
数据库是有状态的服务,尽量不要把复杂的业务逻辑放到数据库中。
尽量不用外键,由程序保证约束,高并发时容易死锁。
4.6 性能规范
基本思路,当前主流计算机性能指标数据。
从图上可以看到基本上每种设备都有两个指标:
延时(响应时间):表示硬件的突发处理能力;
带宽(吞吐量):代表硬件持续处理能力。
根据数据库知识,我们可以列出每种硬件主要的工作内容:
CPU及内存:缓存数据访问、比较、排序、事务检测、SQL解析、函数或逻辑运算;
网络:结果数据传输、SQL请求、远程数据库访问(dblink);
硬盘:数据访问、数据写入、日志记录、大数据量排序、大表连接。
根据当前计算机硬件的基本性能指标及其在数据库中主要操作内容,可以整理出如下图所示的性能基本优化法则:
这个优化法则归纳为5个层次:
1、 减少数据访问(减少磁盘访问)
2、 返回更少数据(减少网络传输或磁盘访问)
3、 减少交互次数(减少网络传输)
4、 减少服务器CPU开销(减少CPU及内存开销)
5、 利用更多资源(增加资源)
4.6.1 规则:减少与数据库交互次数,尽量采用批量SQL语句。
INSERT INTO VALUES(),(),();
UPDATE … WHERE ID IN(10,20,50,…); 或者建临时表批量更新。
反例:某服务查询表信息通过for循环遍历单个查,交互次数多导致性能效率低。
4.6.2 规则:获取大量数据时, 缺省要有where条件,必须分页分批次获取数据。
大表操作要慎重,否则可能会把系统资源耗尽,数据库服务端一般对socket包都有大小限制。
反例:select * from tbl等没有where限制。
4.6.3 规则:禁止对varchar(2000)之类的大字段值进行order by、distinct、group by、union等会引起排序的操作。
说明
此类操作将消耗大量的CPU和内存资源。
4.6.4 规则:使用IN代替OR。SQL语句中IN包含的值不应过多,应少于1000个。
4.6.5 规则:使用EXPLAIN判断SQL语句是否合理使用索引,尽量避免extra列出现:Using File Sort、Using Temporary。
特别是对于重要和调用频繁的SQL。
反例:某个服务占用数据库CPU高,通过EXPLAIN发现频繁查询使用的字段没加索引,导致全表扫描。
4.6.6 规则 不要在SQL中进行复杂的运算或业务逻辑。
4.6.7 规则 删除全表数据时,请使用TRUNCATE代替DELETE。
说明:Delete 会为删除的每一条记录都写日志,Truncate只写一次日志。
反例:某服务出现上千万表的垃圾数据,用delete from table清空表非常耗时,因为是个大事务,对数据库压力非常大。
4.6.8 规则:含有多表的SQL语句,必须明确指定各表的连接条件,以避免产生笛卡尔积。
4.6.9 规则:单条SQL语句的长度原则上不允许超过5K大小。
4.6.10 规则:单条SQL语句引用的表(包括视图引用的表)原则上不允许超过6个。
4.6.11 规则:单条SQL语句的执行时间原则上一般在50毫秒内返回,最长不超过1秒。
说明
主要针对传统的事务型(OLTP)SQL语句,分析型系统(OLAP)有些时候根据业务复杂度及查询的数据量可能3秒无法返回结果。
4.6.12 建议:尽量不要使用负向查询,避免全表扫描。
说明:
使用负向查询是指使用负向运算符,如:NOT、!=、<>、NOT EXISTS、NOT IN以及NOT LIKE等等。因为通过索引有顺序的结构,可以有效的利用二分查找法,快速找到对等的数据,但若使用负向查询,则无法利用索引结构做二分查找,只好全表扫描。
4.6.13 建议:WHERE条件比较中避免使用函数或表达式,字符串LIKE避免使用前缀模糊查询’%...’。
使用前缀模糊查询会导致全表扫描。
4.6.14 建议:避免频繁对大表进行COUNT操作,对大数据量表进行COUNT操作非常耗时,一般都是秒级响应速度。
说明:如果业务需要,可以单独保存记录数。
需要不精准结果时,可以直接SHOW TABLE STATUS …获得。如
SHOW TABLE STATUS FROM test LIKE 'City'\G
4.6.15 建议:IN子查询中避免使用返回数据量大的SQL。
4.6.16 建议:大表查询要限制范围,建议使用LIMIT,以便可以快速返回第一页数据。
4.7 可读性规范(适用于SQL脚本)
4.7.1 规则:不允许把多个SQL语句写在一行中,即一行只写一条语句。
4.7.2 规则:在逻辑上相对独立的程序块之间必须用空行隔开,并加以注释。
4.7.3 规则:过长的表达式应在低优先级操作符处换行书写,操作符放在新行之首,划出的新行要左对齐,不同运算符混合使用时要加以括号标识优先级。
4.7.4 规则:if后的条件要用括号括起来。
说明
为让if语句更加整齐,程序的可读性强,不论含有几个条件都需要用括号括起来。另外,建议括号内的条件个数最多不要超过三个条件。
4.7.5 规则:不同类型的操作符混合使用时,使用括号进行隔离。
4.7.6 建议:对于过长的嵌套语句,应在关键字处换行缩进,一行的长度不要超过110列,同一嵌套层次的关键字左对齐。
4.8 目录规范
4.8.1 规则:数据库脚本在配置库上要有独立的目录来存放,不能和其他程序文件混合存放。
4.8.2 规则:数据库脚本文件的扩展名要统一,例如:“.sql”。
5 维护规范/行为规范
5.1 规则:线上数据库高危操作必须要经过层层审批。
详见:数据库高危操作文档。举例如下:
高危操作 影响
删除类:实例删除/库删除 数据被销毁。要提前做好备份。
对数据库相关目录/文件的增删改。 要提前做好备份。
kill -9 数据库进程 可能导致数据库自身异常。
数据库重启/节点重启 主从环境,可能触发倒换。节点重启有顺序要求,或者设置防倒换忽略节点。
数据库参数配置修改(.cnf/.conf) 数据库参数比较重要。
数据库修改,DDL/DML操作 修改类表操作对系统影响很大,操作前必须要备份,制定预案措施,备份粒度包含实例和表。修改完后要尽快提交或回滚事务,否则长时间锁表会影响业务功能不可用。
使用数据库超户连接数据库 危险操作。
数据库密码作为命令行参数传递 密码泄露。
使用os非dbuser用户进行数据库连接 Mysql数据库客户端操作时,会将执行过的数据库命令记录在当前操作系统用户的~/.mysql_history文件中,如果使用操作系统的非dbuser用户在命令行连接mysql,可能会导致信息泄露。因此,禁止使用操作系统的非dbuser用户进行数据库连接操作。
大表select操作不加where limit 对系统资源消耗比较大。
5.2 规则:线上数据库密码修改遵守规范,避免撞库攻击。
系统初始安装后必须修改缺省密码。根据公司安全红线定期要修改密码。为避免影响业务,修改密码建议避开业务高峰。
为避免撞库攻击,缺省每个实例每个用户要设置不同密码。比如Mysql和Redis的密码要不同,数据库不同权限用户的密码要设置不同,不同服务组设置不同,多套系统(TLF/DT)要设置不同。
5.3 规则:服务组DBA要定期巡检线上数据库实例健康状态。
服务组DBA主要巡检内容:
基本指标:数据库实例健康状态,复制状态,定时备份是否正常,开启相关监控和告警。
业务指标: 通过纵向对比服务表的数量/表记录数等,确定是否符合业务预期。特别是重要的业务表。
性能指标:内存/CPU/磁盘空间,连接数,吞吐能力,慢日志等。
安全加固:重点需要做iptables和白名单控制检查,避免失效,定期修改数据库密码。
5.4 规则:涉及数据库的表设计,SQL,DDL操作等必须经过服务组DBA审核。
5.5 规则:涉及可能影响数据库功能/性能的操作,必须经过服务组DBA审核。
主要有实例管理,备份恢复,数据迁移,导入导出,垃圾数据清理,数据误删除修复,性能优化等。
5.6 规则:访问数据库账号保持权限最小原则。
账号分类 说明
只读用户(readdbuser) 健康检查/监控类操作缺省使用。
定位问题时原则缺省使用。
普通用户(ossdbuser) 应用的读写。
迫不得已涉及修改规避才用应用缺省账号。
复制用户(rplUser) 主备复制用专门的复制用户。
超户(dbuser) 数据库管理类操作。
5.7 规则:禁止使用超户进行写操作规避问题,可能把主备关系写坏。
Mysql控制非超户不能对Slave写操作,会报只读错误,但是超户可以修改Slave数据。 应该优先使用只读用户,其次使用普通用户。
5.8 规则:禁止在线上做数据库压力测试。禁止从测试、开发环境直连现网数据库。
5.9 建议:尽量避免直接远程登录到数据节点。
数据节点比较重要,为避免误操作,尽量不要远程登录,而是通过数据库客户端访问。
6 参考资料
去哪儿MySQL开发规范
赶集网 MySQL数据库开发的三十六条军规
高性能MySQL(第三版)
uNetBuilder跨域mysql数据库设计规范
缓存最终一致性问题