当我们在OLAP数据库的世界中说起Process的时候,它至少可以分为两类:维度的处理跟Cube的处理,本部分只讨论cube的处理及优化,维度的处理优化会在另一部分讨论。
首先我们应该明确所谓处理(Process)这个概念,它可以简单的理解为将数据从一个或多个数据源加载、搬移到分析服务对象中的过程,对Cube处理来说就是加载到度量值组分区中的过程,所以Cube处理的优化其实归根结底是分区的处理优化。
处理过程简介
如下图所示,分区的处理过程可以分为两步:
1. 处理事实数据
处理事实数据简单的说就是将数据从数据源搬到分区对象的过程,因为分区中的事实数据需要关联到维度,所以中间还有一步是查找与事实数据对应的已经处理的维度键值。下面三步并行执行已完成事实处理:
- 发送SQL查询来从源中抽取数据,好像是每次10万行
- 为上面读入的1万行数据在分析服务的维度对象中查找响应的维度键值,然后填充到处理缓存中
- 当处理缓存满的时候,将数据写入到磁盘中
2. 构建聚合及位图索引
在事实数据被处理到分区对象之后,处理就开始构建聚合及位图索引。聚合的处理是在内存中进行的,如果内存不足以构建所有的聚合,那么就会一块一块的先写入到磁盘的临时文件,在处理的最后做合并。
在设计OLAP数据库的时候我们应该在聚合上做均衡的考虑,因为大量的聚合肯定会提高查询的效率,但是同时会降低处理的效率。
分区的处理是通过向OLAP数据库发出分区处理命令进行的,主要有以下的分区处理命令:
ProcessFull
全处理会首先删除现有的存储内容(事实数据、聚合及位图索引),然后首先执行ProcessData过程,紧接着执行ProcessIndexes
ProcessData
处理数据命令也是首先删除现有的存储内容(事实数据、聚合及位图索引),然后处理事实数据,但是不会构建聚合及位图索引
ProcessIndexes
该命令执行的时候假设事实数据已经存在了,它在事实数据上构建新的聚集及位图索引
ProcessAdd
该命令创建一个临时分区,在其上执行完全执行,最后将临时分区Merge到目标分区中。该命令式一个XMLA命令,在UI上名为ProcessIncremental
ProcessClear
删除分区中现有的存储内容,它是XMLA的命令,在UI上名为UnProcess
ProcessClear Indexes
仅删除分区中的聚合及位图索引
处理过程的监控
我们可以使用SQL Server自带的Profiler来对OLAP数据库的处理做监控,首先我们需要知道Profiler中哪些Events是用来监控OLAP处理的,其实这个问题比较显而易见,所有与之相关的事件都处于Process Reports这个目录下面,包括:
- Process Report Begin
事件ID为5,它表示所有OLAP数据库相关的处理报告的开始事件
- Process Report Current
事件ID为7,它表示所有进度报告当前状态事件。例如,在处理期间,当前报表包含有关被处理的对象(维度、分区、多维数据集等)的处理信息
- Process Report End
事件ID为6,它表示所有OLAP数据库相关的处理报告的开始事件
- Process Report Error
事件ID为8,它表示所有处理报告错误事件
从我个人的使用情况来说,我一般只使用Process ReportEnd事件,因为所有的事件都是以Begin开始,中间经历Current最后以End或Error结束。因为我使用Profiler只是用来调优而不是找错,所以不需要Error事件;而任何出现在ProcessReport Begin的事件肯定最后会反应在Process Report End事件中(应该是这样的吧?),所以ProcessReport Begin和Process Report End之间我们只需要Process Report End就可以了;对于ProcessReport Current,本人不是太明白这个事件是干什么的,不过它有一个其他事件无法代替的作用:假如度量值组有1千万数据,AS肯定是没办反一次性都读入内存然后处理的,所以它会每次读取比如10万数据来处理,然后再处理10万直至全部处理完,ProcessReport Current能捕获到现在为止处理的数据行数。
接下来我们要知道的是关于Profiler捕获到的数据都有哪些信息,说白了就是有哪些数据列。我们在此只列出最主要的数据列:
- EventSubClass– OLAP数据库处理事件类,下面是一些主要的事件
- Process
- Merge
- Delete
- DeleteOldAggregations
- Rebuild
- Commit
- Rollback
- CreateIndexes
- CreateTable
- InsertInto
- Transaction
- Initialize
- Discretize
- Query
- CreateView
- WriteData
- ReadData
- GroupData
- GroupDataRecord
- BuildIndex
- Aggregate
- BuildDecode
- WriteDecode
- BuildDMDecode
- ExecuteSQL
- NowExecutingSQL
- ExecuteModifiedSQL
- Connecting
- BuildAggsAndIndexes
- MergeAggsOnDisk
- BuildIndexForRigidAggs
- BuildIndexForFlexibleAggs
- WriteAggsAndIndexes
- WriteSegment
- DataMiningProgress
- ReadBufferFullReport
- ProactiveCacheConversion
- Backup
- Restore
- Synchronize
- TextData– 与事件相关的文本数据,大部分情况下是对事件的一种描述。比如对于Process事件在结束时会描述为“Finishedprocessing the XXX partition”
- DatabaseName– 事件发生的数据库
- ObjectName& ObjectPath 表示事件发生的对象以及该对象的路径
- StartTime– 事件开始时间
- Duration- 包含所报告的事件开始到结束的时间长度(只存在于ProcessReport End事件中)
- EndTime- 包含所报告事件的结束时间(只存在于ProcessReport End事件中)
- IntegerData– 包含与发生的事件关联的整形数据,例如已处理行数的当前计数(只存在于ProcessReport End和Process Report Current事件中)
下面我们以实际的例子来描述一下Profiler到底捕获了什么样的处理数据。对一个只有一个度量值组一个分区的Cube进行处理,Profiler捕获的事件如下:
BuildProcessing Schedule
Cube ProcessBegin
Measure Group Process Begin
PartitionProcess Begin
WriteData for Partition Begin
ExecuteSQL to get data from Source
ReadData Begin
ReadData Current – 到现在为止处理的数据行
ReadData Current – 到现在为止处理的数据行
ReadData End
WriteData for Partition End
BuildAggregation and Indexes Begin
BuildIndex
BuildAggregate
BuildAggregation and Indexes End
PartitionProcess End
Measure Group Process End
Cube ProcessEnd
处理过程的优化
毋庸置疑地有繁多的优化措施可以帮助我们提升Cube的处理效率,比如通过添加索引提升取元数据的速度,提升IO子系统,排除数据库锁等,但是在我们试图应用这些细的优化措施之前,我们应该首先确保我们应用了对Cube处理作用最大的技术:分区+增量处理。
毋庸置疑地有繁多的优化措施可以帮助我们提升Cube的处理效率,比如通过添加索引提升取元数据的速度,提升IO子系统,排除数据库锁等,但是在我们试图应用这些细的优化措施之前,我们应该首先确保我们应用了对Cube处理作用最大的技术:分区+ 分区合并+ 增量处理。
恰当的分区是提升查询机处理效率最有效的方式,假如我们一个销售事实表中包含了近10年的近亿数据,并且还在以每天几万条数据的方式在增长,那如果该度量值组只有一个分区,我们每天不得不对该度量值组做FullProcess,也就是每天都需要处理上亿的数据;但是如果我们是一天一个分区,那么我们每天只需要处理几万数据就可以了。所以分区让我们可以将历史的、固定不变的数据与最近的数据隔离开来,这样就只需要处理最近的少量数据就可以了。
不过实际中每天一个分区会造成管理的混乱,所以有时候我们可以只创建少量的分区,比如只有两个分区,一个表示历史分区,一个表示当前月数据的分区,这样每天我们只在当前月分区上做增量处理(给只处理一天的数据差不多,只是多了Merge的花费),然后在下月的第一天我们将当前月分区的数据Merge到历史分区中。
如果每天的数据量不是特别大(比如千万甚至上亿),该方式能够满足绝大部分的处理性能需求。
毋庸置疑地有繁多的优化措施可以帮助我们提升Cube的处理效率,比如通过添加索引提升取元数据的速度,提升IO子系统,排除数据库锁等,但是在我们试图应用这些细的优化措施之前,我们应该首先确保我们应用了对Cube处理作用最大的技术:分区+ 分区合并+ 增量处理。
恰当的分区是提升查询机处理效率最有效的方式,假如我们一个销售事实表中包含了近10年的近亿数据,并且还在以每天几万条数据的方式在增长,那如果该度量值组只有一个分区,我们每天不得不对该度量值组做FullProcess,也就是每天都需要处理上亿的数据;但是如果我们是一天一个分区,那么我们每天只需要处理几万数据就可以了。所以分区让我们可以将历史的、固定不变的数据与最近的数据隔离开来,这样就只需要处理最近的少量数据就可以了。
不过实际中每天一个分区会造成管理的混乱,所以有时候我们可以只创建少量的分区,比如只有两个分区,一个表示历史分区,一个表示当前月数据的分区,这样每天我们只在当前月分区上做增量处理(给只处理一天的数据差不多,只是多了Merge的花费),然后在下月的第一天我们将当前月分区的数据Merge到历史分区中。
如果每天的数据量不是特别大(比如千万甚至上亿),该方式能够满足绝大部分的处理性能需求。
如果应用了上面简单的处理方式后还不能满足处理性能的要求,那么我们可能需要参考下面一些比较琐碎的优化方式:
源数据端的优化
所谓数据源端的优化其实就是关系型数据库的优化以及OLAP分区所对应的数据查询语句的优化,这主要有以下的方式:
- 如果可能的话为分区查询语句构建恰当的索引,不过我们必须同时考虑到索引对数据插入的负面影响
- 是每个度量值组的分区对应于至多一个关系型数据库表的分区,这样每个分区的数据查询语句只会访问一个表分区
- 尽量从分区的数据查询语句中去除JOIN,被JOIN得出的列应该被物理化到事实表中
- 如果有页面切分,我们可以重构索引来提升填充因子,同时可以采用数据压缩来降低IO
- 通过使用NOLOCK来降低数据库锁开销
- 事实表的设计遵循以下的最优设计
Surrogatekeys | tinyint, smallint, int, bigint |
Datekey | int inthe format yyyyMMdd |
Integermeasures | tinyint, smallint, int, bigint |
Numericmeasures |
smallmoney, money, real, float (Note that decimaland vardecimal require more CPUpower to process than money and float types) |
Distinctcount columns |
tinyint, smallint, int, bigint (If your count column is char, consider either hashing or replacing with surrogate key) |
OLAP端优化
待续….
处理选项的优化
待续….