原则
|
对于涉及数据库的项目,需要构建数据库逻辑模型图,逻辑模型图是项目组成员之间在数据库层面沟通交互的依据,必须规范画图(表,主键,外键,关系)
|
衡量
|
对于表的个数在20个以上的模型,需要数据组参与设计,并需DBA作最终审核
|
方法
|
对于OLTP系统,采用范式化思想进行模型设计,对于OLAP系统,采用面向问题及多级颗粒度的思想进行模型设计
|
实施
|
采用主流的模型设计软件工具PowerDesigner,ERStudio,ERWin
|
原则
|
所有实体型间的业务逻辑关系,除了语义上保留其原有的业务关系外,本质上都要转化成关系数据库的三种关系(1:1)(1:N)(N:M)
|
衡量
|
对于3个及以上实体型之间的“多元关系”,需要数据组参与设计
|
方法
|
比如实体型A和实体型B之间的关系,可以通过问两个问题来确定他们之间的关系:一个A可以对应几个B?一个B可以对应几个A?
(1)一个A对应一个B,相反一个B对应一个A,那么A对B就是1:1关系;
(2)一个A对应多个B,相反一个B对应一个A,那么A对B就是1:N关系;
(3)一个A对应多个B,相反一个B对应对个A,那么A对B就是N:M关系;
|
实施
|
(1)1:1关系选取任何一个表的主键到另一个表中作为外键来体现;
(2)1:N关系将1表的主键在N表中以外键形式存在来体现;
(2)N:M关系采用“关系表”来体现,该关系表的主键是由相关实体表的主键组成的符合主键,各实体表主键不但组成了该关系表的主键,同时也被看作外键在该关系表中存在;
(4)对于三个以上表之间的“多元关系”常需要和反范式化冗余字段结合起来设计,以保证查询速度;
|
原则
|
OLTP系统的模型,需要符合第三范式
|
衡量
|
对于表在20个以上的模型,需要数据组参与设计
|
方法
|
范式化要求:
INF:列是访问的最小单位,具有原子性,不可再被分割;
|
实施
|
依据具体情况对相应属性进行拆分或者合并。
范式化1NF常见现象:
现象一:同一个属性值的不同细度把握,比如,常见的“姓名”这个属性,设计一:“姓名”是一个列,设计二:“姓”是一个列,“名”是一个列,两个列的值组合起来才表达一个“姓名”语义。两种设计方法,在不同的系统中都有应用,这主要是依据需求的细度来确定,灵活把握;
现象二:把多个属性值错误的作为一个属性值存储,比如:常见的OA系统要存储员工的各种属性,包括技能信息,技能范围:Oracle,JAVA,.NET,C#,Perl,UNIX等等,一种常见的错误设计是:设计一张员工表,其中有一个技能属性字段,然后某员工所掌握的多种技能用逗号(,)间隔,然后将这个字符串存储到这个员工表的技能属性字段中。这里的错误在于将多个属性值作为一个属性值存储在一个字段中,不能满足直接遍历员工对某个技能掌握情况,而且如果再要求说明员工对个技能的掌握程度(精通,熟悉,一般等等),则再增加字段,里面的对应关系将很容易错乱,这是严重违反1NF的情况。正确的设计应该是:两个实体表:一张是员工表,一张是技能字典表,一个员工可以掌握多个技能,也就是(1:N)关系,相反一个技能可以被多个员工掌握,也是(1:N)关系,双向都是(1:N)关系,那么综合起来员工和技能之间就是“多对多关系(N:M)”,依据前述规范,应该设计一张“关系表”来存储“多对多关系”,主键为复合主键(员工主键+技能主键),该关系有一个属性“技能掌握程度”。
|
原则
|
OLTP系统的模型,需要符合第三范式
|
衡量
|
对于表在20个以上的模型,需要数据组参与设计
|
方法
|
范式化要求:
2NF:满足1NF,不存在非主键属性对主键属性的部分依赖;
|
实施
|
范式化2NF常见现象:
实体表中一般不会出现违反2NF的情况,因为都是“一个”主键列,而关系表是两个以上列的“复合”主键,故而关系表容易出现违反2NF的情况。主要是该关系表非主键外的属性,本该属于相关的某个实体表的,却放到了该关系表中,这使得该属性不能通过该关系表的复合主键唯一确定,DML操作会发生错误;如果违反了2NF,那么应该把这个属性从关系表中拆分,也许会单独形成一个表,绝大部分情况下是将该属性归并到某个相关的实体表中;
违反2NF的例子:学生考试情况中,有两个实体表:学生表和学科表,学生与学科之间的考试关系就是N:M的关系,就要创建一张关系表存储该多对多的考试关系,表的主键为学生编号和学科编号,属性为考试分数;那么“任课老师”该放在那里呢?如果放到考试关系表中,那么安排任课老师,必须先进行考试,这显然不符合实际,也就是任课老师不该依赖于学生编号和学科编号,只是依赖于学科编号,也就是说任课教师信息应该放在学科表中;
|
原则
|
OLTP系统的模型,需要符合第三范式
|
衡量
|
对于表在20个以上的模型,需要数据组参与设计
|
方法
|
范式化3NF要求:
3NF:满足2NF,不存在非主键属性对主键属性的传递依赖;
|
实施
|
范式化3NF常见现象:
违反3NF的情况,绝大多数是在含有外键的表中;比如A表中的外键字段Bkey是B的主键,那么依赖于Bkey的属性应当属于B表的属性,而不是A表,如果放入A表,则这些对A表的主键Akey的依赖,首先是依赖于A(BKey),而后通过A(BKey)对A(AKey)的依赖,传递依赖于A(Akey);三种关系(1:1,1:N,N:M)都含有外键,都很可能发生违反3NF的情况。违反3NF的后果:会导致那些问题属性插入异常,或者被误删。
违反3NF的例子:
教师和学科之间,存在着上课关系,假设一个教师上一门课而且一门课只有一个教师上,那么该关系为1:1关系,将教师表的主键教师编号在学科表中以外键形式存在就表达了该1:1关系,那么教师的“联系电话”属性该放哪里呢?如果看到“教师编号”出现在了学科表中,就将联系电话放入学科表中,那么联系电话首先是对表中的教师编号依赖,再依据教师编号对学科的依赖,达到了学科编号的依赖,那么联系电话对学科编号的依赖就是传递依赖,违反了3NF,应该将其从学科表中拆出来放入教师表中,不然的话,会发生操作异常,比如,假设一个教师已经存在但是还没有为其分配科目,那么他的电话就无法存入库中。
|
原则
|
OLTP系统中在完成范式化工作之后,对某些表,可以适当反范式化增加冗余字段以提高数据访问性能;在OLAP中采用的是面向问题的设计思想,应该大量使用反范式化冗余信息
|
衡量
|
当SQL关连查询涉及到4张表时可考虑采用冗余字段
|
方法
|
常用在两个地方:(1)关系表中的冗余:在关系表中增加相关实体表的相关属性,以达到关连查询时减少表的关联数量的目的(2)层次关系中的冗余:在多层次的子父表关系中,将父表的属性存储在“子表”或者“孙子表”或者“重孙表”中;
反范式化冗余字段实例:
(1)关系表中的冗余:比如在考试关系中,原本在学科表中的学分信息,可以冗余添加到考试关系表中,这样,每个学生得了多少学分,就可以直接从考试表得到,而无需关联学科表来得到;
(2)多层关系中的冗余:假设为之范畴从大到小有国家表,省份表,城市表,城区表,社区表,它们之间的层次关系是通过上一级的主键在下一级中以外键形式存在来体现的,但是,如果需要问:某个设计属于哪个国家?这样就要关连查询所有的5张表,性能会很差,这时可以将国家编号以外键形式放入到社区表中做冗余,这样直接关联国家表和社区表即可得到答案。一般的,每间隔一级增加一个冗余外键,比如将国家编号放入城市表中,将城市编号放入社区表中
|
实施
|
如何保证冗余字段数据的正确性(一致性)是反范式化的关键,需要对冗余字段详细添加注释,说明冗余了什么,以及该字段的维护方法,常用维护方法如下:
(1)如果在程序开发前设计的冗余字段,可以在正常的业务逻辑程序中一并处理(2)如果是程序完成之后增加的冗余字段,可以使用触发器维护(3)对于OLAP中大量存在冗余字段,可能需要使用单独的处理任务进行维护
|
原则
|
每个表,每个字段都要有注释,说明其含义,对于冗余字段还要特别说明其维护方法,外键字段说明参照与那个表
|
衡量
|
原则上谁设计谁注释
|
方法
|
查询字典表user_tab_comments和user_col_comments可知道表和字段的注释信息
|
实施
|
对表添加注释:
SQL>comment on table <table_name> is 'xx';
对字段添加注释:
SQL>comment on column<table_name>.<col_name> is 'xx';
|
原则
|
一个表中的所有字段,应当能存储在一个数据块中(BLOCK),也即:表的单行字段总长度<db_block(减去pctfree)
|
衡量
|
对不含有大对象数据类型字段的表,字段数大于50个的,请数据组参与设计
|
方法
|
查询字典表USER_TAB_COLUMNS中的字段DATA_LENGTH得到表中所有字段的总长度,再依据db_block和表的pctfree参数可以判断是否一个数据行可以存储在一个数据块(BLOCK)中
|
实施
|
如果所有字段的总长度超出了一个数据块,那么需要将该表拆分成两个(甚至多个)表,拆分的依据是字段的频繁使用程度,也就是频繁使用的字段在一个表中,很少被使用的字段放在另一个表中,他们之间使用相同的主键值,用主外键关联。这点就是“一个表所含字段访问频繁度的规范”
|
原则
|
一个表中的各字段的访问频繁度应该基本一致
|
衡量
|
如果一个表的字段数超过50个, 请数据组参与审核
|
方法
|
如果一个表的字段数过多超过50个,并且依据业务逻辑确定该表中一些字段频繁被访问,另一些字段则很少被访问,则该表需要做拆分处理,这在OLAP系统中比较常见;
目的:这样可以避免读取频繁信息时多读取很少被访问的信息,可以提高IO性能,减少内存耗费;
|
实施
|
将访问频繁度相差太远的字段拆分到两个表中,一个表存频繁访问的字段,另一个表存很少被访问的字段;
|
原则
|
一个非分区表中的数据量不要超过500万
|
衡量
|
一个非分区表中的数据量超过500万, 请数据组参与设计成分区表,如果该表数据量超过5000万,请DBA参与设计
|
方法
|
在系统上线前,通过对业务分析,判断一个表的数据量;在系统上线后,可以通过exp的日志,Top性能SQL,count(1)来发现数据量大的表
|
实施
|
将这些表进行分区,具体方法请参看分区表的设计规范
|
原则
|
存储图片,视频,音频,文件,500字节以上文本等占用太多空间的字段(大对象字段),不能和其他字段存储在一个表中
|
衡量
|
含有大对象(BLOB,CLOB)字段的表设计和存储请数据组参与设计
|
方法
|
方法一:数据库存储,可以重新建一个表专门存储该大对象字段,该表基本为两个字段,一个为大对象编号ID为主键,一个为大对象内容本身,并将该主键在原表中作外键关联,该大对象表存储在单独的表空间中;方法二:操作系统存储,将这些文件存储在操作系统空间中,大对象字段存储该文件的全路径名
比较:如果该大对象字段常被修改,那么采用方法一;
如果该大对象信息为静态,加载后基本不变,那么可以采用方法二,它有一个致命缺点就是信息存储在数据库外部,不安全,容易丢失。
|
属性名
|
类型
|
取值
|
说明
|
Status
|
Char(1)
|
Y/N:Y为激活N为作废,默认为Y
|
标识该行是否使用。用于软删除,软删除需将主键和唯一约束列添加随机数后缀。
|
Create_time
|
Date
|
默认为sysdate
|
创建时间
|
Update_time
|
Date
|
默认为sysdate
|
最后修改时间
|
原则
|
依据表的DML频度而使用不同的表空间
|
衡量
|
表空间的规划由建库人员完成
|
方法
|
为了减少空间碎片问题,(1)将很少被DML(增删改)的静态表,放在一组表空间中;(2)将只发生INSERT的表放到一组表空间中,(3)而将常发生两种以上DML操作的动态表放在另一组表空间中,这三组表空间不能相交
|
实施
|
在上线前,依据需求分析确定动态表和静态表,将他们做最分离;上线运行之后,依据数据库性能分析得到的信息来区分动态表和静态表
|
原则
|
表和索引原则上应该使用不同的表空间存储,并且不同DML频度的表的索引,放在不同的表空间中
|
衡量
|
表空间的规划有数据组或者DBA实施
|
方法
|
绝大多数情况下,动态表的索引必然是动态的,静态表的索引必然是静态的,依据对表的分析来确定索引的动静性
|
实施
|
将动态表的索引放在一组表空间中,静态表的索引放在另一个表空间中,两组不相交。而且和表所在的表空间也不相交。
|
原则
|
大数据量的表需进行分区化
|
衡量
|
当表的数据量超过500万, 请数据组参与设计成分区表,当表的数据量超过5000万,请DBA参与设计
|
方法
|
SQL常依据某列的范围访问表,则对表使用RNAGE分区,常见情况是SQL根据时间范围进行查新,则使用时间字段作为分区关键字进行RANGE分区;
将对表的多种访问结合考虑来确定分区的细度:
1.大多数SQL操作的分区关键字值的范围;
2.数据维护的需要,比如以月为单位删除历史数据;
3.数据访问的性能,以操作范围确定的分区数据量还是过大,比如大于500万,则还需要进行细分;
4.一个分区的数据量要小于500万,这是一个硬性的尺度,但从技术上来看,每个分区10万数据量的情况比每个分区20万数据量的情况要快很多,所以需要灵活掌握;
|
实施
|
1.当各个分区中的数据能均等划分时性能最好,如果相差太大,则考虑采用其它分区,或者将大数据量的分区再进行HASH子分区;
2.各分区采用各自的表空间存储,使用user_tab_partitions字典来查看确定每个分区的表空间位置;
3.分区表的索引采用本地索引,因为常会根据分区关键字(比如时间)进行分区维护(比如删除1年前的数据,也就是删除1年前的分区),分区维护时全局索引会失效,而本地索引不会失效,这能保证访问表时索引正常可用;
|
原则
|
大数据量的表需进行分区化
|
衡量
|
当表的数据量超过500万, 请数据组参与设计成分区表,当表的数据量超过5000万,请DBA参与设计
|
方法
|
SQL常居于某列的散列值访问表,则对表使用LIST分区,LIST分区不支持多列分区关键字;常见情况针对某个地区或者某个业务进行数据访问,那么就使用地区编号或者业务编号作为分区关键字;
将对表的多种访问结合考虑来确定分区的细度:
1.一般使用一个分区关键字的值来划定一个分区;
2.可以把分区关键字的值相对应数据比较少的几个分区合并作一个分区;
3.如果一个分区关键字值所对应的数据量过大,比如大于500万,则应该对表采用RANGE分区,对该值的分区再采用HASH子分区;也就是说,一个可以采用LIST分区的表,肯定可以转化成RANGE分区(可带子分区),反之不然;
4.一个分区的数据量要小于500万,这是一个硬性的尺度,但从技术上来看,每个分区10万数据量分区方法比每个分区20万数据量的分区方法要快很多,所以需要灵活掌握;
|
实施
|
1.各分区采用各自的表空间存储,使用user_tab_partitions字典来确定每个分区的表空间;
2.分区表的索引采用本地索引
|
原则
|
大数据量的表需进行分区化
|
衡量
|
当表的数据量超过500万, 请数据组参与设计成分区表,当表的数据量超过5000万,请DBA参与设计
|
方法
|
SQL访问表不按照某列的范围进行,也不按某列离散值进行,而且对该表的数据不会依据某列的值范围或者离散值进行定期维护,那么使用HASH分区;HASH分区是不知道应该选择何种分区时的选择;HASH分区的各分区都可能存有各种情况的数据,故而不能用于依据分区清理数据的情况;
对确定分区细度的考虑:
1.依据分区的数据量规划和表的最大数据量来确定分区数;
2.一个分区的数据量要小于500万,这是一个硬性的尺度,但从技术上来看,每个分区10万数据量分区方法比每个分区20万数据量的分区方法要快很多,所以需要灵活掌握;
|
实施
|
1.各分区采用各自的表空间存储,使用user_tab_partitions字典来确定每个分区的表空间;
2.对于HASH分区表,大多数情况下依然要求采用本地索引,但是如果分区过细,也可以采用全局索引,因为根据HASH分区表的特征(各分区无业务区分,都有数据),该表很少会发生分区维护的工作;
|
原则
|
大数据量的表需进行分区化
|
衡量
|
当表的数据量超过500万, 请数据组参与设计成分区表,当表的数据量超过5000万,请DBA参与设计
|
方法
|
SQL访问表时,既依据某列值的范围,又依据其他列的离散值或者范围,这种情况下采用RANGE-LIST复合分区,常用于语表中的数据需要依据一个时间字段做周期性删除等维护,并且正常业务SQL访问既依据时间字段,又依据其他字段的散列值进行访问的情况;
比如:电信增值业务计费表,既有时间又有业务属性列,统计的时候,会选择时间范围和业务属性,所以可以以时间列为分区关键字建立RANGE分区,以业务属性列为关键字建立LIST子分区;
分区划分的方法:
1.就按照大多数范围访问的范围值来划定RANGE分区的范围,依据单个LIST子分区关键字的值来划分子分区;
2.如果LIST子分区中数据量较小而且又常被一起访问的子分区可以合并成一个子分区;
3.如果LIST子分区中一个子分区关键字值对应的子分区数据量还是很大,超过500,影响性能,那么可以通过细分RANGE分区来达到减少LIST子分区数据量的目的,这点和LIST分区在该情况下的处理方法(转化成RANGE-HASH)不同;
|
实施
|
1.各子分区应该尽量分散到不同的表空间中存储,使用user_tab_subpartitions字典来确定每个子分区的表空间;
2.RANGE-LIST大多数情况采用本地索引,因为常根据RANGE分区关键字的来进行分区维护;
|
原则
|
大数据量的表需进行分区化
|
衡量
|
当表的数据量超过500万, 请数据组参与设计成分区表,当表的数据量超过5000万,请DBA参与设计
|
方法
|
SQL访问表时,主要依据某个列的范围进行访问,即访问特征符合RANGE分区的要求,或者数据维护特征符合RANGE分区的要求,但是以SQL或者维护的数据范围来划定分区,分区数据量又很大,对性能有影响,需再进行子分区,由于分区中的数据都会被访问到,所以子分区采用HASH方法,整个表就是RANGE-HASH分区;
划定分区的方法:
先按照大多数范围访问的范围值来划定RANGE分区的范围,再依据性能情况来确定HASH子分区的数据量;
|
实施
|
1.各子分区应该尽量分散到不同的表空间中存储,使用user_tab_subpartitions字典来确定每个子分区的表空间;
2.RANGE-HASH大多数情况采用本地索引,因为常根据RANGE分区关键字的来进行分区维护;
|
原则
|
对数据量表应该先在主键列建唯一索引,再建主键约束
|
衡量
|
分区表的主键必须采用该方法
|
方法
|
主键上隐含索引,drop或disable主键时,索引会丢失,为保证性能不变,为了对主键约束和相应索引有更多的控制,对大表(分区表)的索引采用如下方式建立:
(1)在准备建主键的列上建立唯一索引(UNIQUE INDEX): CREATE UNIQUE INDEX Index_Name ON Table_Name(Column_Name) TABLESPACE TBS_INDEX;
(2)再加上主键约束:
ALTER TABLE Table_Name ADD (PRIMARY KEY(Column_Name) USING INDEX TABLESPACE TBS_INDEX );
Oracle会在指定的列上加上主键约束,并且使用该索引
|
实施
|
分区表的主键默认索引是全局索引,所以主键索引的分区方法:先建立分区化的唯一索引,再建主键约束,
|
原则
|
针对大数据量表应该先在唯一约束列上建立普通索引,再添加唯一性约束
|
衡量
|
分区表的唯一约束必须采用该方法
|
方法
|
删除或禁用唯一性约束通常同时使相关联的唯一索引失效,因而降低了数据库性能。要避免这样问题,可以采取下面的步骤:(a)在唯一性约束的列上创建非唯一性索引(普通索引);(b)添加唯一性约束;
|
原则
|
无论表的大小,外键列都要建立索引
|
原因
|
其一是为了子父表关联查询的性能考虑,其二是为了避免父子表修改而发生死锁
|
实施
|
普通表的外键列建立普通索引即可,如果表是分区表,则依据表的情况建立本地索引或者全局索引
|
原则
|
复合索引只有在该种复合常被和该表相关的大多数SQL使用时才建立
|
衡量
|
复合索引的列数不能超过3个,否则该索引很少会被使用
|
实施
|
(1)复合索引的第一列,可以通过不使用该种复合的SQL来确定。假设一些SQL的WHERE中复合使用列为ABC,而其他一些SQL的WHERE中常使用的是C列,那么该复合索引可以按照CAB的顺序建立,这样上述两种SQL都能使用该索引;
(2)对于不能把握好的复合索引,请在选择性大的列上分别建立单列索引;
(3)切忌不能将表相关的所有SQL中WHERE涉及到的列复合起来建立复合索引;
|
原则
|
由于使用形式需和创建形式一致,尽量避免使用函数索引
|
衡量
|
如果想要使用函数索引,请尽量进行转化
|
方法
|
由于函数索引在使用时,使用形式必须和创建形式一致,故应该尽量避免使用函数索引,尽量采用如下方法转化SQL以避免函数索引的使用:
原本在WHERE中列上添加函数的,取函数的反意义函数添加到“=”另一侧的常数项上,这样只需要在列上建立普通索引即可,比如常见的日期转化函数:
TO_CHAR(CREATE_TIME)=’2010-07-07’采用TO_DATE()转化为CREATE_TIME=TO_DATE(‘2010-07-07’,’yyyy-mm-dd’)
|
原则
|
静态表中的低基数列可以使用位图索引
|
衡量
|
在事务型数据库(OLTP)中禁止使用位图(bitmap)索引,在报表型数据库(OLAP)中的静态表,可以适当使用;
|
方法
|
对于常发生DML操作的表,不能建立位图索引,请建立普通的索引即可,否则该表的相关操作很容易造成锁等待,使系统性能大受影响;其次,索引列需要低基数,只有几个数值,比如性别列(男,女,保密)和学历列(大专,本科,研究生,博士生)
|
原则
|
列值顺序增加的列,其上的WHERE运算是<>或者=而不是范围(between and或者 < and >)检索时,可以采用反向函数
|
衡量
|
一般创建反向索引的列为NUMBER类型,值由SEQUENCE生成
|
方法
|
|
实施
|
原则
|
对分区表的索引,需要做分区维护的,必须使用局部索引
|
衡量
|
一般情况下,HASH分区表可以采用全局索引,其他分区,包括RANGE-HASH也应该采用本地索引,主要是由于HASH分区表不常进行分区维护;
|
方法
|
|
实施
|
原则
|
重建索引使用ALTER INDEX REBUILD方式,禁止采用DROP INDEX & CREATE INDEX方式;
|
衡量
|
分区表等大数据量表的索引必须采用ALTER INDEX REBUILD方式重建
|
方法
|
ALTER INDEX IDX_NAME REBUILD [TABLESPACE TBSP_NAME]
|
实施
|
可以在现网上直接实施
|