一、数据库设计的原则
1、表设计原则
(1)规范化与反规范化
(3)数据表分类说明
a.基本数据表:描述业务实体的基本信息。例如,人员基本信息、单位基本信息等
b.标准编码表:描述属性的列表值。例如,职称、民族、状态等
c.业务数据表:记录业务发生的过程和结果。例如,人员调动登记、变更通知单等
d.系统信息表:存放与系统操作、业务控制有关的参数。例如,用户信息、权限、用户配置信息等
e.统计数据表:存放业务数据统计值。例如,通知单统计、人员类别统计等
f.临时处理表:存放业务处理过程中的中间结果
g.其他类型表:存放应用层的日志、消息记录等
2、字段设计原则[DataStatus] [smallint] 数据状态,默认为1,0:为删除 [STAT] [smallint] 单据状态 [AddTime] [datetime] 添加时间 [AddUser] [varchar](20) 添加人 [AddUserName] [varchar](20) 添加人姓名 [AddIP] [varchar](20) 添加人IP [UpdateTime] [datetime] 修改时间 [UpdateUser] [varchar](20) 修改人 [UpdateUserName] [varchar](20) 修改人姓名 [UpdateIP] [varchar](20) 修改人IP
3、键设计原则
(1)禁止使用外键与级联,一切外键概念必须在应用层解决。
(2)所有的键都必须唯一,用于唯一标识表的数据。
(3)尽量避免使用复合键。
(4)外键(应用层)总是关联唯一的键字段。
(5)尽量使用系统生成(如自增列产生)的主键。
(6)一个表中组合主键的字段个数尽可能少,最多不要超过5个字段。
4、索引设计原则
(1)如果一列出现在表达式或函数中,不会使用该列上的索引。
(2)要索引外键(应用层)。
(3)HASH索引只适用于相等比较。
(4)禁止使用大型字段作为索引(建议超过100个字符的字段就不要建索引)。
(5)禁止对常用的小型表创建索引(数据行数不超过1000行的表)。
(6)区分度最高的列放在联合索引的最左侧,尽量把字段长度小的列放在联合索引的最左侧,使用最频繁的列放在联合索引的最左侧。
(7)限制每张表上的索引数量,建议单张表索引不超过5个。
(8)禁止给表中的每一列都建立单独的索引。
(9)常见索引列建议:(在哪些列上建立索引):
(10)Select,update,delete语句的where从句中的列
(11)包含在order by,group by,distinct中的字段(联合索引)
(12)多表join的关联列
(13)禁止建立冗余索引和重复索引。
5、禁用项
(1)禁止在程序中使用游标
(2)禁止使用外键约束
二、完整性设计
采用数据库系统实现数据的完整性。这不但包括通过标准化实现的完整性而且还包括数据的功能性。
1、主键约束
(1)每个表要求必须有主健,主健字段必须满足非空属性和唯一性要求。
(2)主键设计必须采用不更新列。
(3)尽量使用自增数字列作为主键。
(4)尽量使用单字段主键。
(5)禁止使用UUID,MD5,HASH,字符串列作为主键(不能保证递增),选择使用自增ID值。
2、外键约束
禁止使用外键与级联。
3、NULL值
由于NULL值在参加任何运算时,结果均为NULL,所以必须利用ISNULL()函数把可能为NULL值得字段或变量转换为非NULL的默认值。(1)新加的表,所有字段禁止NULL。
(2)旧表新加字段,需要允许为NULL,避免全表数据更新 ,长期持锁导致阻塞。
4、CHECK条件
对于字段有检查性约束,必须指定CHECK规则。5、触发器
尽量避免使用触发器,因为会带来维护和性能的问题,对必须使用触发器的使用原则需要列明清楚,并写在触发器的代码中。
6、 视图
为了在数据库和应用程序之间提供另一层抽象,可以为应用程序建立专门的视图而不必非要应用程序直接访问表。这样做还在处理数据库变更时提供了更多的自由。视图是虚拟的数据库表,在使用时要遵循以下原则:
(1)为简化查询,将复杂的检索或子查询通过视图实现。
(2)提高数据的安全性,只将需要查看的数据信息显示给权限有限的人员。
(3)视图中禁止嵌套视图,视图只允许使用物理表进行处理。
(4)由于视图中只能固定条件或没有条件,所以对于数据量较大或随时间的推移逐渐增多的表,不宜使用视图,可以采用索引视图代替。
(5)除特殊需要,避免类似SELECT * FROM [TableName] 而没有检索条件的视图。
(6)视图中尽量避免出现数据排序的SQL语句。
三、 命名规范
1、总则
(1)所有命名采用26个英文大小写字母和0-9这十个自然数,加上下划线_组成。不能出现其他字符(注释除外)。
(2)对象名尽量短,长度不能超过30个字符。
(3)实际名字尽量描述实体的内容,由英文单词、单词组合或单词缩写组成,不能以数字和_开头。
(4)命名中禁止使用SQL关键字。
(5)禁止出现中文,拼音。
(6)除去前缀,不能超过三个单词。
(7)必须要有扩展属性,增加注释。
2、库
备份库,备份表必须以bak_为前缀并以日期为后缀。
3、表
(1)表以单数形式名词或名词短语命名。
(2)如果表名仅有一个单词,那么建议不使用缩写,而是用完整的单词。
(3)表名全部小写
数据表 t_inf_<系统标识>_<表标识> 编码表 t_cod_<系统标识>_<表标识> 系统表 t_sys_<系统标识>_<表标识> 统计表 t_sta_<系统标识>_<表标识> 临时表 t_tmp_<系统标识>_<表标识> 日志表 t_log_<系统标识>_<表标识>
4、字段
(1)采用有意义的字段名,应该是易于理解,能表达字段功能的英文单词或单词缩写,一般不超过三个英文单词。
(2)字段名全部小写
(3)系统中所有属于内码的字段(仅用于表示唯一性和程序内部用到的标识性字段),名称取为:ID。
(4)系统中属于是业务范围内的编号的字段,其代表一定的业务信息,这样的字段建议命名为CODE,其数据类型为VARCHAR,该字段需加唯一索引。
(5)字段名禁止与表名重复
(6)禁止在列的名称中包含数据类型。
(7)必须要有扩展属性,增加注释
5、主键
pk_<表名>
6、唯一索引
uk_<表名>_<构成索引的字段名>
7、普通索引
idx_<表名>_<构成索引的字段名>
如果复合索引的构成字段较多,则只包含第一个字段,并添加序号。
8、视图
v_<系统标识>_<视图标识>
9、存储过程
proc_<系统标识>_<存储过程标识>
存储过程标识最好能直接表达存储过程内容,例如proc_sys_InsertUser
10、函数
f_<系统标识>_<函数标识>
函数标识最好能直接表达函数内容,例如fn_oms_GetOrdePrice
11、触发器
tr_<表名>_<i、u、d的任意组合>
触发顺序:after、before
触发动作:i、u、d的任意组合
示例:tr_t_inf_goods_after_i
12、变量
@<变量标识>
13、游标(禁用)
局部游标:l_cur_<变量标识>
全局游标:g_cur_<变量标识>
14、存储过程或函数定义中的参数
@<变量标识>
四、行为规范
1、禁止在生产库做数据库压力测试。
2、DBA在执行checkdb等高IO检查操作时应于业务闲时操作,检查前应评估被检查数据库所在硬盘其他数据库的业务影响。
3、禁止从开发环境,测试环境直连生产环境数据库。
4、批量导入、导出数据必须提前通知DBA协助观察。
5、禁止生产库上执行后台管理和统计类查询。
6、产品出现非数据库导致的故障时及时通知DBA协助排查。
7、推广活动或上线新功能必须提前通知DBA进行流量评估。
8、数据库数据丢失,及时联系DBA进行恢复。
9、对单表的多次alter操作必须合并为一次操作。
10、重大项目的数据库方案选型和设计必须提前通知DBA参与。
11、对特别重要的库表,提前与DBA沟通确定维护和备份优先级。
12、禁止在业务高峰期批量更新、查询数据库。
13、所有关于表结构变更升级事项,设计阶段必须要有数据部门人员参与,实施过程及结果必须要知会数据部门相关人员。
14、提交生产库建表改表需求,必须详细注明所有相关SQL语句,且必须经过测试库严格测试,并附相关测试结果,且需有回滚计划,防止失败后无法恢复,通过数据部门确认后交由相关管理人员进行操作。
五、可用性设计
1、备份策略
(1)因为数据库备份是一个I/O密集型操作,因此自动备份时间必须安排在业务不繁忙时进行操作。
(2)一般备份计划:每天一个完全备份 + 每六个小时一个差异备份 + 每小时一个日志备份。(具体计划可视业务及设备情况而定)
2、定期备份还原演练
(1)每月做一次还原演练,保证备份文件有效性。
a.根据公司安全要求,搭建灾备环境,制定灾备演练计划,并定期演练,保证遇到不可抗力灾难可保证系统在较短时间恢复。
b.定期对数据库所有脚本备份逻辑结构备份。
c.数据与日志分离。
d.对于大数据量的表,选用合适字段进行分区。
e.根据数据的使用频繁度,对大表定期分库归档,分离冷热数据,主库/归档库物理分离;归档计划及脚本必须经过数据部门审核,并做好备份还原计划,实施过程及结果需要知会数据部门相关人员。非特殊情况下热、温、冷数据的定义(具体根据业务实际情况进行定义区分):
(2)3个月内业务数据定义为热数据
(3)3到18个月的业务数据定义为温数据
(4)18月之前的业务数据定义为冷数据
a.单表行数超过1000万行或者单表容量超过5GB,才推荐进行分库分表。(说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。)
b.当 Transact-SQL 语句在单个表或索引的引用上获取至少 5,000 个锁时,或在表已分区的情况下,在单个表分区或索引分区的引用上获取至少 5,000 个锁时,会触发锁升级,因此建议在一个事务操作中尽量不要超过5000行的数据修改或删除操作。
c.对于核心业务大表需单独建立文件组,并存放不同磁盘阵列,避免I/O争用。
d.新购买的数据库服务器,要对其进行全面的压力测试,收集相关性能数据。
e.其他高可用技术根据实际情况进行选择(例如AlwaysOn等)。
f.规范数据字典的编写
g.所有新增或修改字段必须维护到数据字典中
h.一个完整的数据字典至少应当包括以下内容:
数据集(系统)名称
|
数据库名称
|
表名称
|
字段
|
数据类型
|
数据说明
|
数据存储长度
|
数据的测量单位
|
代码说明
|
精密度
|
准确度
|
数据的下限
|
数据的上限
|
获得数据的手段
|
时间和/或环境
|
备注
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
六、SQL语句编写
1、SQL中的字符类型数据应该统一使用单引号。特别对纯数字的字符串,必须用单引号,否则会导致内部转换而引起性能问题或索引失效问题。利用TRIM(),LOWER()等函数格式化匹配条件。
2、对于非常复杂的SQL(特别是有多层嵌套,带子句或相关子查询的),应该先考虑是否设计不当引起的。对于一些复杂SQL可以考虑使用程序实现。
3、使用 IN 或 NOT IN 子句时,特别是当子句中有多个值且表数据较多时,速度会明显下降。可以采用连接查询或外连接查询来提高性能。(这里指出:in和or都是效率较低的运算,但是in的效率:O(logn)仍然比or的效率:O(n)高的多,尤其当运算列不是索引的时候尤为明显)。
4、避免使用SELECT * 语句,如果不必要取出所有数据,不要用 * 来代替,应给出字段列表。
5、避免不必要的排序,不必要的数据排序大大的降低系统性能。
6、使用INSERT语句一定要给出插入值的字段列表,这样即使表加了字段也不会影响现有系统的运行。
7、做多表操作时,应该给每个表取一个别名,每个表字段都应该标明其所属哪个表。
8、尽量避免where中包含子查询。
9、where条件中,过滤量最大的条件放在where子句之后。
10、where条件中,尽可能让字段顺序与索引顺序一致
11、Where从句中禁止对列进行函数转换和计算。
12、用Where子句替换HAVING子句。
13、采用绑定变量有助于提高效率。
14、在索引列上使用计算、改变索引列的类型、在索引列上使用!=将放弃索引。
15、运算符效率:exists高于in高于or,(not exists高于not in)。
16、避免在索引列上使用is null和is not null。
17、使用索引的第一个列。
18、用union-all替代union。
19、like ‘text%’使用索引,但like ‘%text’不使用索引。
20、超过五个表禁止join。需要join的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引。
21、如果有order by的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现file_sort的情况,影响查询性能。
22、利用覆盖索引来进行查询操作,避免回表。
23、利用延迟关联(即先将子查询进行临时表存储)或者子查询优化大查询场景。
24、防止因字段类型不同造成的隐式转换,导致索引失效。
25、避免数据类型的隐式转换。
26、禁止使用不含字段列表的insert语句。
27、拆分复杂的大SQL为多个小SQL。
28、超100万行的批量写操作,要分批多次进行操作。
29、关闭影响的行计数信息返回。
30、除非必要SELECT语句都必须加上NOLOCK。
31、查询大量数据使用分页或TOP。
33、Schema解耦,禁止跨库JOIN。
34、尽量使用EXISTS代替 SELECT COUNT(1)判断是否存在记录,COUNT函数只有在统计表中所有行数时使用,而且COUNT(1)比COUNT(*) 更有效率
35、尽量避免使用游标,如果一定要做循环,使用While循环来代替
36、避免使用不必要的DISTINCT,ORDER BY ,GROUP BY
37、存储过程、函数中的编写:
(1)头部注明创建人、创建时间、功能描述。如有修改注明修改人、修改时间、修改内容及原因。
(2)过程如有修改,原代码注释,做好说明,注明修改人、修改时间及修改原因
(3)传入参数有注释说明
(4)传入参数做合法性检查
(5)业务逻辑合法性检查
(6)涉及更新、删除、插入要有异常捕获机制
(7)过程涉及多个表更新、删除、插入要在一个事务内完成
(8)有数据更新过程,过程要有返回值
38、注释示例如下:
/* 目的: 作者: 创建日期: */
/* 修改顺序号: 修改者: 修改日期: 修改原因:(具体原因详细描述) */
七、建模管理方法
1、建模工具
统一使用PDManer 数据库建模软件。
2、建模步骤
(1)逻辑建模
根据数据库设计和命名规范先在PowerDesigner中建立逻辑模型(LDM)文件。要求每个表和字段都要有注释说明;Check Model不能出现错误。
(2)根据逻辑模型文件创建对应数据库的物理模型文件。
(3)生成数据库结构及其相应的SQL脚本。
3、模型维护
(1)所有关于数据库的表、字段及关系、说明等均以物理模型文件为准。
(2)由开发人员将变更需求提交项目负责人审批。
(3)项目负责人同意变更后由相应开发人员负责编写变更脚本提交DBA。
(4)DBA更新数据库及其相关文档,并维护所有部分的一致性。
八、其他设计技巧
1、保存常用信息
让一个表专门存放一般数据库信息非常有用。在这个表里存放数据库当前版本、最近检查/修复、关联设计文档的名称、客户等信息。这样可以实现一种简单机制跟踪数据库。
2、包含版本机制
在数据库中引入版本控制机制来确定使用中的数据库的版本。时间一长,用户的需求总是会改变的。最终可能会要求修改数据库结构。把版本信息直接存放到数据库中更为方便。
3、编制文档
对所有的命名规范、限制、数据字典、存储过程、函数都要编制文档。采用给表、列、触发器等加注释的数据库工具。对开发、支持和跟踪修改非常有用。对数据库文档化也会大大减少犯错的机会。
4、测试、测试、反复测试
建立或者修订数据库之后,必须用用户新输入的数据测试修改的字段。最重要的是,让用户进行测试并且同用户一起保证选择的数据类型满足要求。测试需要在把新数据库投入实际服务之前完成。
5、检查设计
在开发期间检查数据库设计的常用技术是通过其所支持的应用程序原型检查数据库。换句话说,针对每一种最终表达数据的原型应用,保证检查了数据模型并且查看如何取出数据。