数据库设计规范

386 阅读11分钟

一、数据库设计规范(设计之初,需要DBA介入)

规范前言:

1.保证数据库高可用:制定3重高可用(容灾)架构,主备架构、同城容灾、异地容灾(可以采用mha、pxc等高可用架构,注意数据可能丢失风险)

2.根据业务了解热点数据:制定相应热点数据是否需要redis等缓存,来减少直接对数据库访问的请求,以及根据不同情况制定相应的redis与mysql的数据同步机制,如果选择redis,则可采用哨兵或cluster高可用架构

3.根据业务了解数据分布及读写情况:制定是否需要分库分表及读写分离,以及此策略对应采用哪种中间件

4.根据业务了解数据分析对应数据需求:制定是否需要采用不影响生产库性能的同时,通过binlog进行实时同步数据来做数据分析

5.根据业务对数据保留需求:制定相应的备份策略,并制定定期备份验证策略

6.根据业务了解活跃dml相关表:制定定期整理分析表策略

7.根据业务了解大表数据留存需求:制定历史数据的清理机制

8.部署夜鹰:对数据库服务器进行合理监控,可以采用zabbix、pmm、openfalcon等监控工具

9.不在数据库做运算:cpu计算移至业务层

10.控制单表数据量:单表记录控制在1000w

11.控制列数量:字段数控制在20以内

12.平衡范式与反范式:为提高效率牺牲范式设计,冗余数据

13.拒绝3B:拒绝大sql,大事物,大批量




表设计规范:

1.库名、表名、字段名尽量使用小写字母,"_"分割。

2.库名、表名、字段名尽量不超过26个字符。

3.库名、表名、字段名见名知其意,建议使用名词而不是动词。

4.非唯一索引按照“idx_表简写_字段简写”命名。

5.唯一索引必须按照“uniq_表简写_字段简写”命名。

7.每张表及每个字段必须有注释。

8.mysql建议使用innodb为默认存储引擎,建表采用默认存储引擎即可。

9.字符集建议默认采用utf8或utf8mb4(mysql5.7),表使用默认字符集即可。

10.innodb表一定要有主键,在不分库分表的情况下,不要使用有实际意义的字段做主键,且随机主键索引,插入导致大量的页面分割,建议使用自增id,尽量不要使用联合主键,长主键索引使所有相应的二级索引变得更长、更慢(注意:主键建立聚簇索引、主键不应该被修改、字符串不应该做主键、如果不指定主键,innodb会使用唯一且非空值索引代替)。

当然,如果一个字段有助于在各个方面的数据分组或者这个字段被频繁的在查询中使用,可以作为主键。

11.建议不要采用外键,尽量程序端实现其逻辑来保证约束。

12.存储精确数值必须使用decimal,禁止使用float和double。

13.建议使用unsigned存储非负数值。

14.整形定义中不添加长度,如int,而不是int(8)

15.varchar(n),n表示的是字符数而不是字节数,如varchar(100),可以最大存储100个汉字,需要根据实际需要的宽度来选择n。

16.存储日期建议使用date、DATETIME类型。

17.需要 join 的字段,数据类型两边保持绝对一致。 不一致会有隐式转换的风险。

18.尽量的了解数据类型,会运算的能用int的不用string,即便如此,也要选择合适的int类型达到更快的查询或运算。

19.在设计时建议包含两个日期字段:created_time(创建日期),updated_time(修改日期)且非空。

20.尽可能使用简单数据类型,不要使用如blob、clob、long等大字段类型。

21.不允许字段默认null,可以采用如0、1、""等


字段类型设计规范:

1.用好数值类型,最小化规则

tinyint(1Byte)、smallint(2Byte)、mediumint(3Byte)、int(4Byte)、bigint(8Byte)

不应该采用的用法:int(1)/int(11)

2.字符转化为数字

用int而不是char(15)存储ip

3.优先使用enum或set

如:`sex` enum (‘F’, ‘M’)

4.避免使用NULL字段

NULL字段很难查询优化、NULL字段的索引需要额外空间、NULL字段的复合索引无效

错误如:`name` char(32) default null

正确如:`age` int not null default 0

5.少用text/blob

varchar的性能会比text高很多,实在避免不了blob,请拆表

6.不在数据库里存图片,应使用专门的图片处理方式



索引设计规范:(索引是一把双刃剑,谨慎合理使用索引,索引改善查询、减慢更新,索引一定不是越多越好,能不加就不加,要加的一定得加,要衡量好索引带来的收益与损耗)

1.只使用普通索引或唯一索引,其他需要DBA参与衡量,如一些字符字段建前缀索引。

2.索引名称必须使用小写

3.非唯一索引按照“idx_表简写_字段简写”命名。

4.唯一索引必须按照“uniq_表简写_字段简写”命名。

5.索引中的字段数建议不要超过5个,索引是昂贵的,更新索引常常是数据库写操作的主要开销,为关键性能查询集建立索引,整体取审视,而不是一个个看,最好所有的查询条件和联表条件都使用索引(起码区分度最高的部分是)。

6.修改索引时,切记验证对性能的影响。

7.不要创建冗余或者无效的索引,如(a),(a,b)创建索引属于重复索引。

8.多数情况下,联合索引比添加一个新的索引要好,当然要衡量利弊,做出取舍,注意:建立联合索引要按能支持更多查询的顺序建立索引,把所有都是点查询的字段放到索引的首位,

9.在联合索引中,MySQL在遇到返回查询(<,>,BETWEEN)条件时,将停止中止剩余部分索引的使用;但是使用IN(…)的范围查询则可以继续往右使用索引。

10.尽量使用索引进行排序,不使用索引将进行非常昂贵的filesort操作(external sort),常常使用联合索引进行高效排序(注意:不能对两个字段进行不同顺序的排序,对非ORDER BY部分的字段只能使用点查询(=),IN()也不行)。

11.索引可以帮助优化 MIN()/MAX() 这类的统计函数,如:SELECT MAX(ID) FROM tab;使用key(a)

SELECT MAX(b) FROM tab GROUP BY a;使用KEY(a,b)

12.MySQL使用嵌套循环(Nested Loops)进行联表查询(小结果集驱动大的结果集),使每个关联的表(关联字段)都使用上索引显得非常的重要,小表(驱动表)关联字段索引可能是不必要的,但大表(被驱动表)关联字段索引是必要的。

13.不要创建过多的索引,尽量不要添加非性能关键查询的索引,太多的索引会使MYSQL慢下来,如一个表超过10个以上索引,可能会影响dml的性能(5%左右),一些情况会导致应用程序相应dml非常慢。

14.不在索引做列运算。

15.常见的不合适的索引:

a.过于理想,索引过宽,导致索引维护代价高,并发dml高了之后会出现性能抖动。

b.索引筛选性不强,走上索引也不够快,并发高了之后对db冲击很大。

c.并不是所有索引都比全表扫描快,如果获取的数据超过30%,则不走索引,如果超过20%,则可能走全表更好。


sql开发规范:

1.在代码中不允许出现任何ddl语句,ddl统一由dba执行。

2.除非特殊情况,sql语句一定要加上where条件或limit。

3.不允许写select * from 这样的代码,一定要指定需要的字段,来减少无用数据的查询请求(消耗多余cpu,io,内存,带宽)。

4.慎用count(*),如大概查看表数据量,可以通过统计信息查看,如查具体,可以采用count(主键id)。

5.尽量避免在where子句中对字段使用函数或表达式,且字段的值一定要与字段类型匹配,否则会导致索引失效。

6.有表连接时,设计的时候要尽量使两个表的相应字段类型一致,如果不一致,则必须在一边加上类型转换函数(注意mysql的日期和字符是相同的,所以不需要另外的转换)。

7.全模糊查询无法使用索引,应尽量避免,如%a%,可以使用半模糊查询a%。

8.sql中直接使用表名,不使用schema作为前缀,应在连接时直接连到改schema或采用use。

9.sql语句尽可能简单,不要使用复杂sql,复杂逻辑尽量在代码中实现(一条sql只能在一个cpu运算,大语句拆小语句,减少锁时间,一条大sql可以堵死整个库)。

10.简单的事务,使事务时间尽可能短,避免如上传图片等事务。

11.原则上一般禁止使用<>、!=和not in,而应该转换成相应的=和in查询条件,如有特殊需要无法完成相应的转换,必须征求dba。

12.原则上一般不允许使用exists和not exists查询,应转换为相应的等连接和外连接来查询,如有特殊需要无法完成相应的转换,必须征求dba。

13.不要有太多的join,核心操作表尽量不要join,join控制在3个以内,让每个查询sql尽量简洁和高性能。

14.所有非外连接sql(inner join),把关联表统一写到from子句里面,关联条件和过滤条件统一写到where子句中。

15.出于代码可读性,所有的外连接sql语句,统一使用left join。

16.Query语句中的ORDER BY、GROUP BY的时候,尽可能利用已有的索引来避免实际的排序计算,可以很大幅度的提升ORDER BY、GROUP BY操作的性能。

17.避免使用trig/func,由客户端程序取而代之。

18.OR改写为IN(),or的效率是n级别,in的效率是log(n)级别,in的个数建议控制在200以内。

如:select id from t where a=1 or a= 2;

=>select id from t where a in (1, 2);

19.OR改写为UNION,mysql的索引合并不太友好

如:select id from t where a = 1 or b = 'ds';key(a),key(b)

=>select id from t where a = 1

union all或union(看具体情况,union有去重开销)

select id from t where b= 'jonh'


20.使用load data导数据,load data比insert快约20倍,但要注意自增主键问题导致主从不一致与空洞问题;

21.应用程序端尽量采用Prepared Statements(是一种运行在后台的SQL语句集合),在性能方面,当一个相同的查询被使用多次的时候,会带来可观的性能优势



数据库权限分配规范


生产库:

DBA:有所有权限,超级管理员权限

应用程序:分配insert、delete、update、select、execute、events、jobs权限。

测试人员:无权限

开发人员:无权限

原则:所有对线上表的操作,除了应用程序之外,都必须经由DBA来决定是否执行、已经什么时候执行等。



测试库:

DBA:所有权限。

测试人员:有insert、delete、update、select、execute、jobs,ddl等权限。

数据分析人员:只有select查询权限

开发人员:有select权限。

原则:DBA有所有权限,而且严格控制表结构的变更,不允许除了qa之外的人对测试环境的库环境进行修改,以免影响测试人员测试。所有对测试库的表结构进行的修改必须由测试人员和DBA一起审核过后才能操作。


开发库:

DBA:所有权限

测试人员:有库表结构以及数据的所有操作权限。

开发人员:有库表结构以及数据的所有操作权限。

数据分析人员:有库表结构以及数据的所有操作权限。