介绍
联机分析处理系统都需要快速的查询响应和及时的数据更新,以提供高效的数据分析。传统的OLAP系统使用层次组织和汇总数据,层次为有效地分析提供合理的数据结构。但严格的层次结构又限制了用户自由的组织和分析数据。
为了提供更自由和更弹性的数据分析,Microsoft® SQL Server&S482; Analysis Services (SSAS) 2005既包含了传统层次分析的优点,又有新一代的更弹性的属性层次。属性层次允许用户在查询时自由的组织数据,而不限于设计好的导航路径。要支持这样的弹性化分析,Analysis Services OLAP构架经过了特殊的设计,以应用属性和层次分析,同时还保持传统OLAP数据库的快速查询性能。
你需要理解OLAP构架是如何支持属性和层次构架,理解如何有效使用这种构架满足分析需求,以及如何让构架充分利用系统资源。
注意 要使用此白皮书中讨论的性能调校技术,你必须安装了SQL Server 2005 Service Pack 2。
为了满足各种OLAP设计方案的性能需要,此文档提供了广泛的指导,指导你使用更多手段优化Analysis Services性能。因为Analysis Services性能调校是非常宽泛的话题,此白皮书的内容按如下的四个章节组织。
增强查询性能 – 查询性能直接影响终端用户的体验,也是衡量OLAP是否成功的主要标准。Analysis Services提供了多种机制加速查询性能,包括聚合、缓存、数据索引,而且你可以通过优化维度属性、cube、MDX查询语句来提升性能。
调校处理性能 – 处理是更新Analysis Services数据库的操作。处理速度越快,用户能更及时地获取更新后的数据。Analysis Services提供了多种机制让你影响处理性能,包括有效的维度设计、合适的聚合、分区、节制的处理策略(例如用增量更新代替完全更新,主动缓存技术等)。
为特殊应用优化设计 – 复杂的设计场景要求特殊的性能调校技术,以确保OLAP能够成功的应用,特别是复杂的设计再加上大数据量。例如,在OLAP中包含了特殊聚合函数、父子层次、复杂的维度关系、近实时地更新数据。
调校服务器资源 – Analysis Services的操作受服务器资源的限制,理解Analysis Services如何使用内存、CPU和磁盘资源可以帮助你更有效的管理服务器,优化查询和处理性能。
文档的三个附录提供了更多相关信息。
增强查询性能
查询是指Analysis Services依据多维表达式(MDX)将数据提供给客户端应用程序。因为查询的性能直接影响到客户的体验,这部分将详述改进查询性能的几种手段。下面就是这部分的主要内容:
理解查询构架 - Analysis Services查询构架支持三种主要操作:会话管理、MDX查询执行和返回数据。若要优化查询性能就需理解这三种操作如何协调工作实现查询。
优化维度设计 - 经过良好调校的维度设计可能会是提高Analysis Services性能最重要的因素。创建属性关系和在层次中使用属性会影响到聚合设计、MDX计算、维度数据存储效率和磁盘读取数据的性能。
最大化聚合数据 - 通过预汇总计算,聚合数据能够提高查询性能。为了最大化聚合数据,确保你有一种合适聚合设计以满足你特定的需求。
使用分区增强查询性能 - 分区是一种将度量值组存储到独立物理单元的机制,这种机制能提高查询、处理性能,使管理更简便。而且分区能实现并发查询,可以通过聚合设计选项和服务端属性设置优化分区性能。
编写高效的MDX语句 – 这部分详述如何编写高效的MDX语句,例如:1)在MDX语句中使用更精确和更窄的计算空间。2)设计多用户可重用的计算成员。3)用最简洁的方式编写计算成员表达式,使查询执行引擎能最有效的选择执行路径。
理解查询构架
为了使终端用户能有尽可能快速的查询体验,Analysis Services使用了若干组件协同工作实现高效计算和返回数据。图1标明了在查询发生时三个主要操作:会话管理、MDX查询执行、返回数据,以及参与每部分操作的服务组件。
图 1 Analysis Services查询构架
会话管理
客户端应用程序通过TCP IP或HTTP,使用XML for Analysis (XMLA)与Analysis Services通讯。Analysis Services使用XMLA监听组件管理所有的XMLA通讯。会话管理决定客户端连接到Analysis Services实例的方式。通过Windows认证,且拥有相关权限的用户才能连接到Analysis Services。在用户连接到Analysis Services后,安全管理依据用户在Analysis Services中的角色决定用户的权限。基于客户端应用程序构架和连接安全权限,当客户端应用程序连接到Analysis Services时,为客户应用程序创建一个会话,用户的所有查询请求都会重用这个会话,直到客户端应用程序关闭会话或在服务端终止会话。在查询执行引擎执行用户的请求时,会话提供了上下文。关于会话的生命周期,参考文档中“监控超时的空闲会话”章节。
MDX查询执行
查询执行引擎的主要工作是执行MDX查询,这部分概述查询执行引擎如何执行查询。关于优化MDX的细节,参考文档中“编写高效的MDX语句”章节。
实际的查询执行过程分多步执行,从性能方面考虑,查询执行引擎必须考虑两种基本需求:找到数据和产生结果集。
<!--[if !supportLists]-->1. <!--[endif]-->找到数据—为了找到查询请求的数据,查询执行引擎将MDX查询分解成多个数据请求。又在与存储引擎通讯时,将这些数据请求翻译成存储引擎能够理解的子立方体请求,子立方体的数量依赖于查询的粒度和复杂度。子立方体代表了查询、缓存、查找数据的逻辑单元。注意,这里的子立方体是泛指,不要与MDX语句CREATE SUBCUBE所指的子立方体搞混淆。
<!--[if !supportLists]-->2. <!--[endif]-->产生结果集—为了处理从存储引擎查找到的数据,查询执行引擎使用两种执行计划来计算结果:可以批量计算整个子立方,或者计算单独的单元。通常,子立方体赋值路径更高效,但查询执行引擎依靠查询的复杂程度选择合适的执行计划。注意,同一查询的会分解成多个查询部分,因而产生多个子执行计划,而且这些子执行计划可以独立的选择两种执行计划中的一种,所以没有单一的全局计划方式。例如,要查询年际利润大于10%的分销商,查询执行引擎使用一个执行计划来计算每个分销商的年际利润,另一个执行计划用来筛选利润大于10%的分销商。
当你执行一个MDX计算,查询执行引擎需要计算的单元数量可能远超出你的想象。比如,你使用MDX查询年初至今销售最大的五个区域。看起来你只需要五个单元值,但要决定哪五个区域销售最大,及计算年初至今销售值,Analysis Services必须计算更多单元。一个常用的优化手段是在MDX语句中将查询执行引擎所需计算的数据量最小化。要了解更多MDX优化手段,参考此文档中“指明计算空间”章节。
当查询执行引擎计算单元,它会使用查询执行引擎缓存和储存引擎缓存保存计算结果。缓存的好处就是优化计算和支持计算结果重用。为了优化缓存重用,查询执行引擎可管理三种范围的缓存:全局范围、会话范围、查询范围。关于缓存共享和重用的信息,参考“利用查询执行引擎缓存”章节。
数据查找:维度
在数据查找期间,存储引擎必须选择最佳的查找机制以满足维度和度量数据请求。
为了满足维度数据请求,存储引擎从属性和层次存储中抽取数据。当查找所需的数据时,存储引擎使用动态即需缓存,仅将所需的成员存入内存,而不是将所有维度成员静态保存在内存中。维度数据结构可以寄存于磁盘上、Analysis Services的内存中、或者Windows操作系统的文件缓存中,这由系统内存装载方式决定。
顾名思义,维度属性存储包含维度属性的所有信息。维度属性存储的各部分如图 2所示。
图 2 维度属性存储
如图中那样,维度属性存储中每个维度属性包含下面部分:
<!--[if !supportLists]-->· <!--[endif]-->键存储—键存储包含属性键成员值和一个称为DataID的内部唯一标识。Analysis Services为每一个属性成员分配DataID。
<!--[if !supportLists]-->· <!--[endif]-->参数存储—参数存储用来记录属性的各种附带参数,包括成员名称和翻译。DataID是从零开始连续分配的,参数存储对应到DataID。为了能不使用额外的索引或哈希表实现快速的随机访问,参数存储和关系存储一样按照DataID的顺序物理排列。
※注:为了不跟维度中的Attribute搞混,Property我只好翻译成“参数”。
<!--[if !supportLists]-->· <!--[endif]-->哈希表—为了在查询和处理过程中,便捷的找到属性,会为每一个属性在磁盘上保存两个哈希表。键哈希表通过唯一键索引成员;名称哈希表通过名称索引成员。
<!--[if !supportLists]-->· <!--[endif]-->关系存储—关系存储包含了属性与其他属性的关系。确切地说,关系存储保存了带有DataID的原记录与其他属性的对应关系。考虑下面的产品维的例子:在一个维度中产品是键属性,它与颜色属性和尺寸属性有直接关系。“运动头盔”是产品属性的成员之一,它的DataID是1001;“黑色”是颜色属性的成员之一,它的DataID是25;“大号”是尺寸属性的成员之一,它的DataID是5。这些属性成员组成了一个维度实例集——“运动头盔,黑色,大号尺寸”,这在关系存储中保存为1001, 25, 5。注意如果一个属性与其他属性没有属性关系,则不会为特殊的属性创建关系存储。关于属性关系更多信息,参考“识别属性关系”章节。
<!--[if !supportLists]-->· <!--[endif]-->位图索引—为了在查询时快速在关系存储中找到属性数据,存储引擎在处理时创建位图索引。对属性的每一个DataID(成员),位图索引描述数据页中是否包含带有此DataID的纪录。如果属性有非常多的DataID,既此属性有很多成员,那么在处理时将花费大量时间。在大部分场合,位图索引能显著的改进查询性能。但在设计时,第一次创建位图索引的代价超出了查询时的益处。可以通过将AttributeHierarchyOptimizedState设置为Not Optimized,删除指定属性的位图索引。关于这种设计方案,参考“减少属性负担”章节。
除了属性存储,层次存储为终端用户将属性排列成导航路径,如图 3所示。
图 3 层次存储
层次存储由下面几个主要部分组成:
<!--[if !supportLists]-->· <!--[endif]-->集合存储—集合存储将DataID从第一级排列到当前级别,来为每一个成员构建路径。例如,“山地自行车500系列”的DataID是6,它的父成员是“山地自行车”;“山地自行车”的DataID是5,它的父成员是“自行车”;“自行车”的DataID是2,它的父成员是“所有产品”;“所有产品”的DataID是1。那么“山地自行车500系列”的结构存储就是1,2,5,6。
<!--[if !supportLists]-->· <!--[endif]-->结构存储—对级别中每个成员,结构存储包含父成员的DataID,第一个子成员的DataID和子成员的数量。结构存储中的每个成员都按照其级别索引排序,级别索引是由维度排序设置决定的成员在级别中的位置。为了更好的理解结构存储,考虑下面的例子:如果成员自行车有3个子成员,那么它的结构存储为1,5,3;1是自行车父成员的DataID,5是自行车第一个子成员的DataID,3是子成员的数量。
注意,只有自然层次才在层次结构中物化存储。关于设计层次最佳实践,参考“有效使用层次”章节。
数据查找:度量值组
对数据请求,存储引擎在分区中查找度量数据。分区包含两类度量数据:事实和聚合数据。为了适应不同的数据存储结构,每个分区可使用不同的存储模式。MOLAP存储模式提供了最快的查询性能,MOLAP分区会以压缩的多维格式存储事实和聚合数据。大部分情况下,都应该使用MOLAP存储模式。如果你想了解更多分区模式的信息,参看“附录 B”。如果你考虑“近实时”部署,参考“近实时数据更新”章节。
在MOLAP分区中,事实和聚合数据结构是一样的。关系型数据库中事实表的数据被高度压缩后,存储到MOLAP分区中。记录(通俗的讲,就是二维表的行)是最小存储单位,每个记录存储度量值组的所有度量值和一组内部的DataID用于关联维度的粒度属性。256个记录组成一个页,256个页组成一个段。
为了有效地满足数据请求,存储引擎应用三种机制优化查询请求:缓存、聚合、事实数据。如图 4。
图 4 满足数据请求
图 4标示了对{(Europe, 2005), (Asia, 2005)}的数据请求,存储引擎选择了下面的途径实现请求:
<!--[if !supportLists]-->1. <!--[endif]-->储存引擎缓存—存储引擎首先试图使用存储引擎缓存满足数据请求。存储引擎缓存永远是居于内存中的,使用存储引擎缓存提供了最好的性能。关于管理存储引擎缓存的信息,参考“查询时内存需求”章节。
<!--[if !supportLists]-->2. <!--[endif]-->聚合—如果缓存中没有相关数据,存储引擎查询预计算的聚合数据。在某些场合,聚合恰好能满足数据请求。例如,当按产品类别和年来查询销售数据时,就可以使用聚合。存储引擎也可以使用低级别的聚合数据,例如按月或季度查询数据。关于如何设计聚合来提供性能,参考“最大化聚合”章节。
<!--[if !supportLists]-->3. <!--[endif]-->事实数据—如果没有合适的聚合数据能满足查询,存储引擎必须从分区中查找事实数据。存储引擎使用多种内部优化手段从磁盘查找数据,包括增强索引和聚类相关记录。聚合和事实数据都可以将数据的不同部分存储到磁盘或Windows操作系统文件缓存,这依赖于系统的内存转载方式。
一种优化数据查找的关键手段是使用多分区将度量值划分成不同的数据片断,从而减少存储引擎需要扫描的数据量。多分区不仅能增强查询速度,而且更便于数据管理和处理。
从查询的角度考虑,存储引擎预计每个MOLAP分区存储的数据,并优化它的MOLAP分区并行查找计划。例如图 4,2005的分区数据显示蓝色,2006的分区显示黄色,数据请求{(Europe, 2005), (Asia, 2005)}只要求2005的数据,因此存储引擎只需要查找2005分区。为了优化查询性能,存储引擎尽可能的使用并行查询。为了在分区中定位数据,存储引擎并行查找段,并使用位图索引有效的扫描页以找到想要的数据。
分区是高性能立方体的主要条件。关于分区的好处,参考下面的章节:
<!--[if !supportLists]-->· <!--[endif]-->要了解如何创建位图索引,在分区中使用位图索引,参考“分区处理作业”章节。
<!--[if !supportLists]-->· <!--[endif]-->要了解分区优点的细节,参考“使用分区增强查询性能”章节。
<!--[if !supportLists]-->· <!--[endif]-->要了解分区对处理所带来的好处,参考“使用分区增强处理性能”章节。
优化维度设计
经过良好调校的维度设计方案是高性能Analysis Services最关键的因素之一。有两种主要的优化维度设计方案:
<!--[if !supportLists]-->· <!--[endif]-->定义属性关系
<!--[if !supportLists]-->· <!--[endif]-->有效地使用层次
定义属性关系
通常情况下,Analysis Services维度数据源是关系型数据仓库中的维度表,维度表包含主键、属性和关联到其他表的外键。
表 1 简单的产品维度表包含的列
维度表列 |
列类型 |
与主键的关系 |
与其他列的关系 |
Product Key |
Primary Key |
Primary Key |
--- |
Product SKU |
Attribute |
1:1 |
--- |
Description |
Attribute |
1:1 |
--- |
Color |
Attribute |
Many:1 |
--- |
Size |
Attribute |
Many:1 |
Many: 1 to |
|
Attribute |
Many:1 |
|
Subcategory |
Attribute |
Many:1 |
Many: 1 to Category |
Category |
Attribute |
Many:1 |
|
表 1显示一种简单的产品维度表。在这个简单的例子中,产品维度表有一个主键列,其他列是属性。从关系的观点看,所有属性与主键都是多对一,或一对一的关系。属性间也存在关系,例如Size和Size Range是多对一的关系;Subcategory和Category也是多对一的关系。
如同在关系型数据库中需要理解和定义字段间功能性依赖关系,在Analysis Services中为了正确聚合数据、有效存储和查找数据,你必须理解属性间关系。为了帮助你在属性间创建联系,Analysis Services提供了属性关系特征。顾名思义,属性关系用于描述两个属性间的关系。
在最初创建维度时,Analysis Services自动将主键和属性间建立多对一关系,如图 5。
<!--[if !vml]-->
<!--[endif]-->
图 5 默认属性关系
图 5中的箭头代表了Product Key与维度其他属性间的关系。图 5是一种有效,但是没有经过优化的维度结构,因为它没有为Analysis Services指明属性间的关系。
使用这种设计,当查询中包含了维度中的属性时,数据永远由维度主键汇总。比如,当你要按Subcategory查询销售量时,需要汇总Product Key级别的销售量;当你要按Category查询销量时,也需要汇总Product Key级别的销售量。这可以说是低效能的,因为Category属性的销售量可由Subcategory属性销售量汇总。而且,使用这种设计,Analysis Services不知道某种属性成员组合是否存在,它必须使用事实数据来识别有意义的成员组合。例如,Category属性包含成员“附件“, Subcategory属性包含成员“山地车”,“山地车”不属于“附件”,所以“山地车”和“附件”的组合是无意义的。但如果没有定义属性关系,而按Category和Subcategory属性组合来查询数据,Analysis Services就不能准确地确定“山地车”和“附件”的组合是否存在。
为了优化维度设计,首先你必须理解属性间的关系,并进行一些设置,让Analysis Services明白这些关系。
为了增强产品维度设计,图 6中使用属性关系来优化维度设计。
<!--[if !vml]-->
<!--[endif]-->
图 6 优化属性关系的产品维度
注意图 6和图 5的区别。图 5中,所有属性关联到主键。而在图 6中加入了两个属性关系,Size关联到Size Range,Category关联到Subcategory,这反映了属性间的多对一关系。
典型的多对一关系如图 6中层次那样。层次表明了多对一关系,但Analysis Services不会自动检验所有层次是否满足多对一关系。所以当属性加入层次前,你必须保证属性间能严格满足多对一关系。如果创建了违反多对一规则的层次,查询时就会得到错误的结果。
举个例子,你有一个时间维度带有月属性和年属性,月属性包含值1月、2月、……、12月,年属性包含2004、2005、……。当你在月属性和年属性间建立了关系,Analysis Services在处理时就不知道如何将月分配到年。Analysis Services不知道将1月关联到2004还是2005。唯一能保证正确建立关系的方法是在月属性的KeyColumns参数包含月列和年列。
KeyColumns是唯一标识属性成员的源列或源列集合。一旦你在属性间建立关系,就要注意KeyColumns。必须确保维中的每个属性的KeyColumns能唯一标识属性成员。如果KeyColumns不是唯一标识属性成员,在处理时遇到重复成员会默认忽略,结果导致不正确的汇总。
注意,如果属性关系有默认的Type,当Analysis Services遇到重复的月时,会错误地将所有月分配给第一或最后的年(依据于数据刷新方法),而且不会提示任何错误。关于Type参数和数据刷新方法对重复键的影响,参考“优化维度成员Insert, Update, Delete”章节。
无论选择何种数据刷新方法,键重复通常都会产生错误的数据汇总,你应该正确设置唯一的KeyColumns来避免这种情况。如果正确配置了KeyColumns,最好修改维度的默认错误配置,以确保处理时不再忽略重复键。将KeyDuplicate值从IgnoreError改成ReportAndContinue或ReportAndStop,这样在遇到重复键时Analysis Services将发出警告信息。
定义了新的属性关系后,为了性能和数据正确性,必须删除多余的关系。图 6中,在加入了新的关系后,Product Key不再需要直接关联Size Range和Category,所以要删除这两个关系。为了帮助你识别多余的属性关系,Business Intelligence Development Studio提供了可视化的警告。
虽然Product Key不再需要直接关联Size Range和Category,但仍然通过属性关系链间接地关联到这些属性。比如,Product Key关联到Size,Size关联到Size Range,这种属性关系链叫做层叠属性关系。
有了层叠属性关系,Analysis Services就能够更好的优化聚合设计、数据存储、数据查找和MDX计算。除了性能方面的优点,属性关系也可以用来增强维度安全,可以用来将度量数据关联到非主键粒度属性。例如,你的销售度量数据粒度是Product Key,但你的预测度量数据粒度是Subcategory,有了Subcategory与Category的属性关系,你的预测度量数据就可以汇总到Category。
设计有效属性关系的核心原则是依据业务逻辑建立维度模型。这里提供优化维度设计的指导和最佳方案,为了设计成功的模型,你必须非常熟悉数据和数据所支持的业务需求。
考虑这样的例子:你的时间维度带有一个称为Day of Week的属性。这个属性包含七个成员,既“星期一”到“星期日”。你可能会想到修改此属性的KeyColumns参数,使星期能与日历日期关联起来,比如将星期与年月组成层次。但你又必须考虑到,某些场合下需要独立分析一周七天的销售变化。所以最好的设计依赖于业务需求。更多关于时间维度的设计方案,参考“UDM中时间计算:并行时间(http://www.sqljunkies.com/WebLog/mosha/archive/2006/10/25/time_calculations_parallelperiod.aspx)”。
有效使用层次
在Analysis Services中,用户可以使用属性建立两种层次:属性层次和用户层次。不同的层次对查询性能有不同的影响。
属性层次是维度中默认建立的。对非父子层次,每个属性层次包括两级:属性本身和All级别。尽管All默认是每一个属性层次的最顶部级别,但你也可以通过设置IsAggregatable参数禁用All级别。在大多数情况下,不建议禁用All级别。禁用All级别后,你的查询总是要用此属性层次中一个特定的成员进行切片。当你显式指定Default Member控制切片时,注意所有查询都将使用此切片,而不管你的查询语句中是否引用了此属性层次。
(※注:原文不是很清楚,我解释一下。例如,一个度量值销售量关联到产品、日期、客户三个属性层次。如果要列出产品的销售量,你会查询{ 销售量, 产品.Members },相当于是查询{ 销售量, 产品.Members, 日期.All, 客户.All }。一旦禁用了日期的All层次后,查询时需要在MDX语句中为日期指定成员用于切片,或者设置日期的Default Member,比如将日期默认成员设置为2007年1月1日,那么当你使用同样的查询时,实际上是在查询{ 销售量, 产品.Members, 日期. 2007年1月1日, 客户.All },会用到日期的默认成员来切片)
就性能来说,属性层次中的属性不会自动聚合。当在查询中用到这些属性时,需要通过主键汇总数据。这样的话,查询性能可能会不理想。
为了增强性能,可以通过设置Aggregation Usage参数将属性标识为用于聚合备选。关于此技术手段的详细信息,参考“定义聚合备选”章节。不过,在修改Aggregation Usage前,先考虑是否可以使用用户层次来增强性能。
在用户层次中,属性被分派到预定义的多级导航树中,使得终端用户能更快捷的分析数据。Analysis Services能让你建立两种用户层次:自然和非自然层次。
<!--[if !supportLists]-->· <!--[endif]-->自然层次中,级别间的成员是直接或非直接的由底到高层的关系。大多数情况下,自然层次遵从多对一的关系。图6中产品维度的例子中,可以创建产品分组层次,此层次由低向上包含产品、产品子类别和产品类别。层次中所有属性都直接关联到上层属性,而不是直接关联到Product Key。另外,如果从层次中移除了产品子类别属性,产品和产品类别仍可构成自然层次。因为产品非直接关联到产品类别,两者是层叠属性关系。
<!--[if !supportLists]-->· <!--[endif]-->在非自然层次中,至少包含两个没有属性关系的连续层次。通常这种层次只是用来创建下钻路径以便用户能在客户端使用。
从性能上看,自然层次和非自然层次差别很大。自然层次中,层次树物化的存储在磁盘中,而且自然层次中的所有属性自动用于聚合备选。自然层次的这一特点非常重要,所以如果有可能,尽量创建自然层次。关于聚合备选的信息,参考“定义聚合备选”章节。
非自然层次不会物化存储在磁盘上,而且属性不会自动用于聚合备选。仅仅提供一种易于使用的下钻路径。你也可以使用各种MDX导航函数将所需的属性集合到层次中,所以另一种代替非自然层次的方法是在查询时,在MDX语句中使用cross-join。cross-join和非自然属性性能上差不多。非自然属性仅仅是提供了重用性和集中管理的好处。
若想要使用自然层次,你必须正确设置了层次中属性层叠关系。因为创建属性关系和创建层次是独立的步骤,所以你可能会忘记为层次中的属性定义属性关系。这样的话,就算你需要的是自然层次,但Analysis Services 会自动将此层次标示为非自然层次。
为了验证你创建的层次的类型,如果层次中缺少属性关系,Business Intelligence Development Studio会显式警告图标 <!--[if !vml]--><!--[endif]-->,目的是帮助你发现非自然层次。如果你的目的就是要创建非自然层次,那么你可以忽略此警告图标。
而且,需要注意属性层次、自然层次和非自然层次在客户端显示的方式。例如,你有一系列地理属性,这些属性已包含在用户层次中。你就应该考虑隐藏的独立的属性层次,以免用户同时看到属性层次和由属性组成的用户层次。要隐藏属性层次,修改AttributeHierarchyVisible参数。
聚合最大化
所谓的聚合,就是预先计算好汇总数据,并将汇总数据物理的存储,这样在查询时就可大大提高性能。更确切地说,一个聚合单元就是与维度属性关联的汇总的度量值。(※注,原文使用的是Aggregation,在汉语中为了跟动词区别开来,翻译时使用了聚合单元,不要跟Cube中的单元Cell搞混)
聚合设计是在所有的聚合单元中挑选出一部分进行物化的过程。虽然物化的聚合单元越多(※注,下文中如无特别说明,聚合单元既是物化后的聚合单元),查询效率越高,但还要考虑创建和更新聚合所花的时间,过多的聚合单元会增加处理时间。所以必须权衡利弊。
因为可以对每个分区进行对立的聚合设计,所以下面讨论的聚合最大化技术适用于一个或多个分区的情况。在这部分文档中,如不是特别说明,聚合针对Cube的单一度量值组和单一分区。关于使用多分区来提供性能的信息,参考“使用多分区增强查询性能”章节。
聚合如何优化查询
你应该能理解通过预聚合可以提升查询性能,不过你可能还不清楚聚合如何实际的让Analysis Services 更高效地满足查询。答案很简单,聚合减少了查询时存储引擎需要扫描磁盘的数据量。要进一步说明,我们可以先讨论在没有聚合的情况下,存储引擎如何满足查询。
之前你可能会认为度量值和事实表数据是影响聚合最重要的因素,但实际上数据聚合中维度扮演着最关键的角色,维度决定着数据如何汇总。举个例子,图7展示了包含三个维度的销售Cube。
图 7 产品, 顾客, 和订单数据维度
图中每个维度有4个属性。Cube最低粒度(叶级别)包含有200个独立的产品,5,000个独立的客户,和1,095个订单。所以Cube中,叶级别的数据数量会是笛卡尔乘积:200 * 5000 * 1095,即三个维度会产生109,500,000个理论上的成员组合。但是只有当所有的这些顾客,每个人在每一天都购买了所有品种的产品时,才可能达到这一理论值,所以实际存在的组合数量会比理论数量少若干个级别。这里我们假设实际的成员数是1,095,000。
通常的情况是,终端用户不会经常查询Cube的叶级数据,很少会用到如此大的数据集(1,095,000个单元)。对非叶级的查询,存储引擎必须通过维度属性即时汇总明细级单元,从性能上看,代价太大了。为了优化汇总,Analysis Services在cube处理时,将预先计算和汇总的数据存储到聚合单元中。如果查询中能用到聚合数据,性能将大大提高。
继续上述例子的讨论,如果Cube包含了由月和产品子类别属性定义的销售聚合数据,那么在按月和产品子类别查询销售数据时,就不需要通过扫描事实数据,而是使用聚合数据满足查询。此聚合单元中理论最大的成员组合数量是720(20个产品子类别成员乘以36个月成员)。因为实际的属性成员组合数会小于720个,那么通过聚合单元远比汇总1,095,000个叶级别的成员组合高效。
而且,除了直接命中聚合单元的查询能提高性能外。存储引擎会试图使用任何可用的聚合来满足查询,存储引擎不用汇总叶级数据,而是简单的汇总聚合数据来满足查询。例如,如果你要按月和产品类别查询销售数据外,存储引擎可以通过简单汇总月和产品子类别的聚合单元来实现查询,而不需要再次汇总叶级别的数据。要利用这一查询优化机制,需要设计合适的维度属性关系和自然层次。关于维度设计的信息,参考“优化维度设计”章节。
存储引擎如何使用聚合
为了进一步理解存储引擎如何使用聚合,你可以使用SQL Server Profiler察看查询事件。命中聚合的数据请求事件是Get Data From Aggregation。图8展示了一个查询的示例和查询返回的结果集。
图8 查询例子和结果集
如图8所示,你可以使用SQL Server Profiler比较以下两种场景下,Analysis Services是如何实现查询的:
· 场景1—聚合满足查询
· 场景2—聚合不能满足查询
图9 场景1:SQL Server Profiler跟踪到命中聚合数据查询事件
图9显示了SQL Server Profiler跟踪使用聚合满足查询的事件。你可以看到存储引擎产生查询结果集的操作:
1. 查询被提交后,在Get Data From Aggregation事件中可看到存储引擎从Aggregation C 0000, 0001, 0000获取数据。
Aggregation C是聚合单元的名称。Analysis Services为聚会分配16进制的唯一名称。注意,从早期版本导入的聚合会有不同的命名方式。
2. 图9中,除了看到Aggregation C外,聚合名称中还包括标量000, 0001, 0000,此标量描述了聚合的内容。关于此标量的实际意义,参考“如何解释聚合单元命名”章节。
3. 聚合数据被装载到存储引擎的度量组缓存中。
4. 一旦数据在度量组缓存中,查询执行引擎从缓存中查找数据,并返回结果到客户端。
图10 场景2:SQL Server Profiler跟踪到非聚合命中查询事件
图10显示的是SQL Server Profiler跟踪没有使用聚合的查询事件,执行的操作如下:
1. 在查询提交后,存储引擎访问分区中的叶级别数据,而不是查找聚合数据。
2. 之后的操作过程就与图9中的一样了。数据被装载到存储引擎度量值组缓存。
3. 一旦数据在度量组缓存中,查询执行引擎从缓存中查找数据,并返回结果到客户端。
总结这两个场景,当SQL Server Profiler显示Get Data From Aggregation时,意味着查询命中聚合单元。这样存储引擎就能使用聚合数据满足全部或部分查询需要,而不用从叶级别数据中汇总数据。聚合命中比响应时间更能说明聚合设计是否成果。
为了帮助你有效设计聚合,Analysis Services提供了帮助创建聚合的工具和手段,详细信息,参考“哪些聚合将被创建”章节。一旦创建并部署了聚合,可以使用SQL Server Profiler在应用程序生命周期内监控聚合使用情况。
为什么不创建所有可能的聚合
因为聚合能显著的提高性能,你可能想知道为什么不创建所有理论上可能的聚合单元。回答这个问题前,先从理论的角度考虑创建所有可能的聚合意味着什么。
注意,理论讨论的目标仅是为了让你理解基于属性构架的聚合的运作方式,而非Analysis Services实际建立聚合的方式。关于更多信息,参考“哪些聚合将被创建”章节。
聚合数据用于汇总属性组合关联的度量值。每个属性有两级数据:属性自身和All属性。图11显示了产品维度每个级别明细数据。
图11 产品维度的属性级别
产品维度有四个属性,每个属性两个级别(All和属性自身),则此维度的属性组合总共会产生2^4 = 16个标量或聚合单元。
图12 三个维度,每个维度带有四个属性
如果像图12那样,所有可能的聚合单元数如下:
聚合单元总数
2 (product key) *2 (color) *2 (product subcategory) * 2 (product category)*
2 (customer key) *2 (gender) *2 (city) *2 (state/province) *
2 (order date key) * 2 (month) * 2 (quarter)* 2 (year)
= 2^12
= 4096
从这个例子中,可知任何Cube的聚合单元数量等于2^ (属性个量)。带有12个属性的Cube理论上会产生4,096个聚合。更大的Cube可能包含有上百个属性,聚合数量会指数级别增加。拥有100个属性的Cube,理论上会有1.26765E+30个聚合。
庆幸的是,这只是理论上的讨论。Analysis Services只评估小部分的聚合单元,并最终挑选出更少的聚合单元实现物化存储。通常情况,有效的聚合设计只包括几十或几百的聚合单元,不会有上千个聚合单元。
理论上的讨论提醒我们在Cube中加入更多的属性,会增加Analysis Services必须要评估的聚合单元数量。而且,因为聚合在Cube处理时创建,太多的聚合单元会影响处理性能,且需要更多磁盘空间。最终可能还会导致处理时间超出数据更新的期限。
如何解释聚合单元命名
当Analysis Services创建一个聚合单元时,维度用标量命名,表明属性是属性自身还是All级别。属性级别用1表示,All级别用0表示。例如,考虑下面产品的聚合例子:
· ProductKey属性的聚合 = [Product Key]:1 [Color]:0 [Subcategory]:0 [Category]:0 或者 1000
· Category属性的聚合 = [Product Key]:0 [Color]:0 [Subcategory]:0 [Category]:1 或者 0001
· ProductKey.All, Color.All, Subcategory.All, Category.All的聚合 = [Product Key]:0 [Color]:0 [Subcategory]:0 [Category]:0 或者 0000
为了识别聚合,Analysis Services用一个长串标量记录一组维度标量,在长串标量中使用逗号分隔每个维度标量。标量中维度的顺序由Cube中维度的顺序决定。如果你要知道Cube中维度的顺序,可以使用下面两种方法之一,在SQL Server Business Intelligence Development Studio中Cube Structure栏显式了Cube的结构,在维度面板的Hierarchies栏和Attributes栏都可以看到维度的顺序;另一种方法是在Cube的XML文件中查看维度顺序。
标量中属性的顺序就是维度中属性的顺序。你可以通过查看维度XML文件中属性顺序来了解属性顺序。
例如,一个subcube定义为(0000, 0001, 0001)代表着这样的聚合:
Product – All, All, All, All
Customer – All, All, All, State/Province
Order Date – All, All, All, Year
知道如何解读这些标量后,你就可以在SQL Server Profiler中监控查询的聚合命中信息。在In SQL Server Profiler中,你可以通过启用Query Subcube Verbose事件了解标量如何映射到特定的维度属性。
哪些聚合将被创建
为了判断创建哪些聚合单元,Analysis Services使用一套聚合成本/收益分析算法评估每个聚合备选单元。
· 聚合单元成本—聚合单元的成本主要受聚合大小的影响。为了计算聚合数据的容量大小,Analysis Services收集源数据、成员的统计数量,以及维度、度量、属性数等元数据。一旦计算好一个聚合单元的成本,Analysis Services会执行一系列的测试,将此聚合单元的成本与其它聚合单元的成本作比较,并判断成本是否超出了成本上限。
· 聚合单元收益—聚合收益依赖于其在查询时减少数据扫描的量。例如,1,000,000个数据汇总到只包含50个值的聚合单元,这个聚合单元将大大提高查询性能。你应该记得Analysis Services可以使用聚合数据满足查询,或者仅汇总低级别聚合数据。当Analysis Services要决定建立哪些聚合时,算法需要知道属性是否相互关联,以便能够检测出哪些聚合单元提供最大的查询覆盖,同时确定哪些聚合单元是不需要的。
为了帮助你建立聚合,Analysis Services提供了两个工具:聚合设计向导和基于使用优化向导。
· 聚合设计向导依据你的Cube设计和数据分布设计聚合。它在幕后使用成本/收益算法选择聚合,算法以Cube设计和数据分布的信息作为算法的参数。你可以在Business Intelligence Development Studio或SQL Server Management Studio中使用聚合设计向导。
· 基于使用优化向导依据用户的查询模式设计聚合。基于使用优化向导使用了同样的成本/收益算法,此外它还评估出现在Analysis Services查询日志中的聚合备选单元。你可以在Business Intelligence Development Studio (BIDS)或SQL Server Management Studio中使用基于使用优化向导。要使用基于使用优化向导,你必须捕获终端用户的查询记录,并将查询记录存储在查询日志中。要配置Analysis Services实例的查询日志,可以在SQL Server Management Studio中进行几步操作以控制查询采样频率和日志保存位置。
某些情况下,你可能需要更精细的控制聚合设计,SQL Server Service Pack 2的样例中包括了一个高级聚合工具,使用这个工具,可以不通过聚合设计算法,自主地创建聚合。关于此聚合工具的信息,参考附录C。
如何影响聚合设计
要帮助Analysis Services成功应用聚合设计算法,你可以使用下述的优化手段来影响和增强聚合设计(后面的章节将详细讨论这些手段)。
提供聚合备选单元–当Analysis Services设计聚合时,聚合设计算法不会评估每个属性是否适合创建聚合。所以,在你设计Cube时,确定哪些属性自动作为聚合备选,接受聚合设计算法评估的,哪些是需要你指定为聚合备选单元的。
提供Cube数据的统计信息–为了智能评估聚合成本,聚合设计算法分析Cube中每个聚合备选单元的统计信息。例如包含成员数和事实表数据量的元数据。确保及时更新元数据,以改进聚合设计。
采用合理的聚合设计策略–为了帮助你设计最有效的聚合,在开发生命周期的不同步骤,利用不同的聚合设计方法。
提供聚合备选单元
当Analysis Services设计聚合时,聚合设计算法不会自动评估所有的属性,判断它们是否合适建立聚合。还记得之前讨论过理论可能的聚合数量吗?如果Analysis Services要评估每个属性,那么它将花费过长的时间设计聚合,而评估的过程是不会干扰向聚合单元中填充数据的。所以为了使此过程能流水线处理,Analysis Services提供了Aggregation Usage参数让你决定哪些属性作为聚合备选而接受评估。对每个度量值组,确定哪些属性自动接受聚合设计算法评估,哪些是需要你指定为聚合备选单元的。
聚合使用规则
聚合备选单元是Analysis Services需要评估是否需要创建聚合的属性。存储引擎通过Aggregation Usage参数决定一个特定的属性是否要作为聚合备选。Cube中每个属性都有Aggregation Usage参数,所以属性的这个参数是全局的,对Cube中所有相关的度量值组和分区都会有影响。Aggregation Usage参数可以是这四个值:Full、None、Unrestricted、和Default。
· Full—聚合包括属性和相关属性链的下级属性。例如,产品维度有属性链:产品、产品子类别、产品类别。如果你设置产品类别的Aggregation Usage参数为Full,Analysis Services可能会创建包括产品子类别的聚合,这将子类别关联到类别,且用来计算类别总计量。
· None—不会创建包括此属性的聚合单元。
· Unrestricted—不限制在聚合设计中使用此属性,但仍要满足一些条件后才能确定属性是有价值的聚合。
· Default—根据属性和维度的类型应用默认的规则来判断是否作为聚合备选。如你所想,这是Aggregation Usage参数的默认值。
默认规则是非常保守的设置,所以你必须要很好的理解它。默认规则可分成四个约束条件:
1. 默认约束1—粒度和All属性应用Unrestricted设置 – 维度中的粒度属性和All属性应用Unrestricted设置。粒度属性是用于与度量值组关联的属性,当维度通过键属性关联到度量值组时,粒度属性就是键属性。
为了使你明白默认约束1,图13显示的产品维度包括6个属性,每个属性显示为属性层次。还有三个用户层次,其中两个显示为蓝色的是自然层次,显示为灰色的是非自然层次。Product Key是用来关联度量值组的粒度属性,因此在默认约束条件1下,它是唯一的聚合备选。
图 13 应用默认约束1后产品维的聚合备选
2. 默认约束2—特殊维度类型应用None设置 – 所有的多对多、非物化引用维度、数据挖掘维度中的所有属性(除了All属性),使用None设置。图13中的产品维度是标准维度,因此不受约束2的影响。关于多对多和引用维度的信息,参考“复杂维度关系”章节。
3. 默认约束3—自然层次应用Unrestricted设置 – 对所有的用户层次,使用特殊的扫描过程识别属性是否包含在自然层次中。如之前所述,自然层次是一种用户层次,在此层次中所有级别的属性都有属性关系。
为了识别自然层次,Analysis Services从用户层次顶级开始向下扫描,检查每一级属性是否通过直接属性关系或非直接属性关系联接到下一级。除了IsAggregatable参数设置为False的属性会应用Full外,通过自然层次检验的属性应用Unrestricted设置。
4. 默认约束4—其他属性应用None。对维度中其他属性应用None设置。在图13的例子中,color属性就使用None设置,因为此属性只在属性层次中使用。
图14显示了产品维度应用了默认规则后,维度中属性的约束条件。黄色的属性是用于聚合备选的。
· 按照默认约束1,Product Key和All属性作为备选。
· 按照默认约束3,Size, Size Range, Subcategory和Category属性作为备选。
· 按照默认约束4,Color不会参与任何聚合。
图 14 应用了默认约束后产品维度的聚合备选
因为图表让你能很方便的了解默认规则的影响,所以你可以在设计聚合时,可以使用聚合设计向导查看特定的聚合备选。
图15显示的是聚合设计向导中对象计数页面。在这个向导页面中,你可以图14中聚合备选属性用粗体字标出。关于对象计数的讨论会在“提供Cube元数据统计信息”章节进行,会详细讨论如何更新统计信息以便让你改进聚合设计。
图 15 聚合设计向导中的聚合备选
影响聚合备选
依据Aggregation Usage参数设置,下面是一些关于影响聚合备选的建议。注意这些修改只是影响聚合备选,不是实际的聚合,在创建聚合前仍要经过成本/收益评估。下面会分三种情况讨论:
· 没有用户层次的维度—如果你的维度只有属性层次,默认地只有粒度属性被用作聚合备选。那么你可能需要增加一些自然层次,不光是因为受Aggregation Usage的影响,而且还可以通过预定义的导航路径让你的用户有更好的分析体验。
· 只用于属性层次的属性—如果一个属性只用于属性层次,如图14中的属性Color,你可能需要更改Aggregation Usage参数:
· 如果一个属性经常被使用,或者有特殊的要求用来旋转和钻取数据,你就可以将Aggregation Usage由Default 改成Unrestricted。例如,如果你有一个高度汇总的记分卡样式报表,需要通过减少初始查询的响应时间来提供用户的体验,那么你就可以更改Aggregation Usage参数的设置。
· 在某些场合,将某个特殊属性的Aggregation Usage设置为Unrestricted是可行的,但不要试图将所有的属性层次都设置为Unrestricted,否则你会发现向导需要花掉大量的时间评估所有可能的聚合备选。在大型的cube中,向导可能会花掉一个多小时去完成聚合设计,之后还需要更多的时间去处理聚合。反之,你应该将经常查询的属性层次设置为Unrestricted。通常可以将维度中一半的属性设置为Unrestricted。
· 只有当你确实需要优化查询时,才将Aggregation Usage参数改成Full。不过应该尽量避免这样做,且只有当属性含有相对较少成员时才改成Full。
· 不常使用的属性—如果自然层次中的属性用户很少使用,你需要将Aggregation Usage设置为None。这样就可以帮你节省聚合数据所占的空间,并确保维度中不超过一半的属性是Unrestricted的。例如,某些属性只被少数的高端用户使用,这些用户能接受轻微的查询延迟。在这种情况下,你可以强制聚合设计算法仅为那些被大部分用户使用的属性建立聚合。还有一种情况,你可能会考虑将Aggregation Usage设置为None:自然层次中某个属性成员的数量与它下一级属性成员的数量几乎一致。例如,产品维有20个子类别,18个类别,将20个成员汇总到18个成员的IO代价几乎可以忽略。普遍来说,如果级别间成员比不超过2:1,你可以考虑将Aggregation Usage设置为None。
提供Cube数据的统计信息
当聚合设计算法确定了聚合备选单元后,它会对聚合备选单元执行成本/收益分析。为了智能评估聚合成本,聚合设计算法分析每个聚合备选单元的统计信息。例如分析聚合备选单元的成员数量和事实表数据行数等元数据。通过更新元数据以增强聚合设计的效果。
你可以通过度量组的EstimatedRows参数定义源事实表的数据行数,也可通过维度属性的EstimatedCount 参数定义属性成员的数量。
你可以在聚合设计向导的Specify Counts页面修改这些数量,如图16所示。
图 16 在聚合设计向导中提供对象计数
如果数量为空(例如你没有在设计Cube的时候定义)点击Count按键计算每个聚合备选和事实表的大小。如果数量已经存在,点击Count按键不会更新数量。你必须手工在对话框中,或通过编程方式修改。如果你在开发环境使用一个小型数据集设计Cube,然后把Cube用于正式生产环境的大型数据库时,手工修改统计信息就非常有必要了。
而且,当你使用多分区来物理分布数据,分区数据计数准确地反应分区中的数据量是非常重要的。如果你每年的数据创建一个分区,年属性在每个分区中的成员计数就是1。如果分区计数没有值,那么会聚合设计会参考估计计数值,此值是整个事实表的计数。
Analysis Services通过对比聚合单元的成本,得出一个成本上限值。如果一个聚合单元的成本超过了此上限值,聚合单元就立刻被抛弃。最重要的一个成本上限值称为一比三原则,既Analysis Services不会建立超过事实表三分之一容量的聚合。实际中,如果一个属性包含大量成员,而聚合单元包含此属性,一比三原则通常就会阻止建立这样的聚合单元。
低级别的聚合有更多的成员,一比三原则倾向于阻止处理低级别聚合。被一比三原则排除的聚合几乎都是那些如同事实级别(叶级别)一样大的聚合,或者如同事实级别一样难以满足高级别查询的聚合。
当你的维度拥有大量成员时,靠近叶级别的聚合大部分都会超出上限。例如,一个度量值组如下设计:
· 客户维有5,000,000个客户成员,这些客户成员被分成50,000个销售小区和5,000个销售大区
· 产品维有10,000个产品成员,分成1,000个产品子类别和30个产品类别
· 时间维有1,095天,36个月和3年
· 销售事实表包含12,000,000条销售记录
如果销售度量值组只有一个分区,Analysis Services不会考虑任何超出4,000,000行记录的聚合(一比三原则)。例如,不会考虑任何包含客户属性的聚合。同理也不会考虑由销售小区、产品类别、月组成的聚合,因为5,000个销售小区、30个产品类别、36个月产生的聚合,理论上可能有5,400,000条记录。
如果度量值组有多个分区,就可以通过将度量值组分成更小得物理数据块,并调整分区的统计信息,来影响聚合设计。
例如,你按月将度量值组分成36个分区,每个分区的统计信息可能如下:
· 客户维有600,000个客户,分成10,000个销售小区,3,000个销售大区
· 产品维有7,500个产品,分成700个产品子类别,25个产品类别
· 时间维有1个月和1年
· 销售事实表有1,000,000行销售记录
通过将数据分成更小的块,Analysis Services就可以找到更多有用的聚合。例如,销售小区、产品类别、月组成的聚合就是一个有效的聚合备选单元,因为它包含75,000行记录(3000 * 25 * 1),小于分区记录数的三分之一。当使用了多分区后,还必须更新图16中的成员计数和分区计数。如果你没有更新统计信息,Analysis Services不会知道分区包含的数据集变小了。注意,这里的例子只是说明多分区如何影响聚合设计。实际的分区容量说明和推荐分区记录数量,参考“设计分区”章节。
注意,你可以使用XMLA从Analysis Services实例中检查元数据,获取支持和监控信息。使用这种方法,你可以得到关于分区的记录计数和聚合容量信息,这些信息可以帮助你更好的了解Cube。
采用合理的聚合设计策略
聚合设计策略的目的是帮助在整个实施生命周期中设计和维护聚合。从聚合的角度来说,Cube生命周期可以分成两个阶段:初始聚合设计阶段和基于查询模式调校阶段。
初始化聚合设计阶段
最有效的聚合设计是基于用户的查询模式。可是,当你最初部署Cube时,你没有任何可用的查询使用记录,所以不可能使用基于使用优化向导。虽然使用聚合通常能得到更快的查询速度,但你应该在最初设计时限制聚合的数量。具体的初始聚合数量取决于Cube复杂度和容量(事实表的容量)。
· 小型Cube—对小型Cube,有效地初始聚合设计是在聚合设计向导中使性能达20%~30%。注意,如果设计中包含太多的属性,在计算聚合性能时,聚合设计向导有可能在达到指定的性能提升百分比前停止,同时用户界面没有任何反应。这种情况下,太多的属性会产生许多理论可能的聚合单元,但实际上只会创建一小部分聚合单元。
· 大型且复杂的Cube—Analysis Services将要花费很长的时间在大型且复杂的Cube挑选出小部分需要建立的聚合单元。理论上聚合的数量是2^(Unrestricted属性的数量)。一个拥有5个维度,每个维度有8个成员的Cube,理论上就有2^40个聚合备选单元。如果聚合向导每秒钟检测1000个聚合单元(这已经是乐观的估计了),将要花费35年才完成聚合设计。而且,大量的聚合单元将需要更多的处理时间,占用大量的磁盘空间。真实的环境中,对大型且复杂的Cube,初始聚合最好是少量的性能提升比(少于10%,甚至1%~2%),且聚合设计向导的运行时间不超过15分钟。
· 中度复杂的Cube—中度复杂的Cube,设计聚合的目的是达到10%到20%的性能提升,且向导执行时间不超过15分钟。如果你不知道如何区别中度复杂和高度复杂的Cube,通常这样考虑:如果一个Cube的某个维度有超过10个Unrestricted属性,那就算是高度复杂的Cube。
在你使用聚合设计向导创建初始聚合前,应该修改Aggregation Usage参数以便尽量使常用到的聚合被物化创建,同时尽量使很少使用的聚合不被物化创建。Aggregation Usage等同于“暗示”聚合算法哪些属性经常使用,哪些不常使用。关于修改Aggregation Usage参数的详细情况,参考“影响聚合备选”章节。
当为分区设计好聚合后,最好检查聚合文件的容量。聚合文件的容量最好是源事实表容量的1~2倍。如果超出1~2倍,就可能需要更长的时间处理Cube和建立更大的聚合文件。在查询时,可能会因为聚合文件太大无法装载到内存中,而使性能低下。如果你碰到这些问题,最好是减少聚合备选单元的数量。
基于查询模式调校阶段
让用户使用Cube一段时间(通常是一、两周),查询日志中就能收集到用户查询模式数据。使用基于使用优化向导分析Cube查询情况,然后根据用户实际的查询模式设计更多的聚合单元。你可以通过处理分区创建新的聚合单元。当用户的查询模式改变后,再用基于使用优化向导更新聚合单元。
如果要让使用优化向导发挥作用,必须捕获终端用户的查询,将查询信息记录在查询日志中。记录用户的查询信息需要一定量的性能开销,所以推荐你在正常情况下关闭它,当需要分析查询模式时再开启。
如果你要更精确地控制聚合设计,还有一种方法可以利用基于使用优化向导。SQL Server Service Pack 2的样例中包含了高级聚合工具,它允许你通过查询日志创建特定的聚合单元,而不用聚合设计算法。关于聚合工具的信息,参考附录C。
使用分区提升查询性能
分区是将度量值组数据分散存储到多个物理单元的机制,有效的使用分区可以提高查询和处理性能。这一章讲述如何通过分区提升查询性能。分区对处理和管理数据的好处将在“使用分区提升处理性能”章节详述。
在查询时如何使用分区
当你查询Cube时,存储引擎尝试从存储引擎缓存中查找数据。如果缓存中没有所需的数据,就试图从聚合中查找数据。如果聚合中也没有数据,就必须在分区中扫描事实数据。如果单分区也能够实现存储引擎并行查询,并使用位图索引确定查询哪部分数据,那就没必要再去优化性能了。
所以你可以通过多分区将度量值组分成多个物理单元。存储引擎就同时独立查询每个分区,并且只需要查询跟需求相关的分区。
图 17 多分区智能查询
图17显示了从称为Adventure Works的Cube中按Business Type查询Reseller Sales Amount的事件。Reseller Sales度量值组按照年分成四个分区。因为查询按2003年切片,所以存储引擎可以直接在2003年的分区中查找数据,而不需要扫描其他的分区。SQL Server Profiler跟踪到这个查询说明查询只需要从2003年分区读取数据。
设计分区
如果你清楚用户的查询习惯,你可以按照用户常用的查询模式设计分区。但如果用户没有统一的查询模式,多少就有些困难了。常用的做法是按照时间元素分区,例如日、月、季度、年等。许多查询都涉及到时间,所以按时间分区通常都能提升性能。
当你设计了分区,你必须将源数据表、视图、或源查询绑定到分区。对MOLAP分区,在Cube处理过程中,Analysis Services使用分区中每个属性的最小和最大DataID标识数据片段,以便计算分区中属性的数据分布范围。分区中每个属性的数据范围信息包含在数据片断定义中,数据片断就如同是子立方体。有了这些信息,存储引擎可以在查询时只挑选相关的分区扫描。对ROLAP和主动缓存分区,你必须自己在分区参数中定义片断。
在设计分区时,使用以下的方法创建和维护分区:
<!--[if !supportLists]-->· <!--[endif]-->当你决定将数据分散到多个分区时,你通常会估量分区的容量和分区的数量。分区容量是分区中数据量和分区聚合文件的大小。尽管分区中的段可以并行查询,但如果聚合文件无法装载到内存,查询效率就非常糟糕。
<!--[if !supportLists]-->· <!--[endif]-->通常,每个分区的记录不应超过两千万条。而且,分区大小不应超过250MB。如果分区超过这个两个限制中的任何一个,就应考虑将分区分成更小的单元以减少分区扫描时间。多分区通常都是不错的,可过多的分区也会影响性能。
<!--[if !supportLists]-->· <!--[endif]-->如果你有多个分区,但每个分区小于50MB或少于两百万数据量,可以考虑将它们整合到一个分区。而且,创建少于4,096行记录的分区不是一个好方法,因为存储引擎不会为如此少的数据创建聚合和索引。注意这个数量上限由msmdsrv.ini文件中的IndexBuildThreshold参数控制。实际的生产环境中应该不会少于4,096行记录。
<!--[if !supportLists]-->· <!--[endif]-->当你定义分区时,不需要保证每个分区都包含一致性的数据集。例如,一个度量值组可以有3个按年的分区,11个按月的分区,三个按周的分区,和1~7天的分区。使用不同级别的数据创建异构分区可以让你更简便的管理新数据装载,因为可以在不影响其他已存分区的情况下处理分区,用户的查询也不会受到干扰。而且,还可以为一组分区设计聚合,共享同级别的数据(详细内容在下一节)。
<!--[if !supportLists]-->· <!--[endif]-->无论何时你需要为度量值组建立分区,你必须取保为每个分区更新统计信息。更确切地说,确保分区记录计数和属性成员计数能准确性是非常重要的。关于如何更新分区计数,参考前面的“提供Cube数据的统计信息”章节。
<!--[if !supportLists]-->· <!--[endif]-->对包含Distinct Count汇总函数的度量值组,考虑专门为此度量值组优化。详细信息,参考“优化Distinct Count计算”章节。
多分区的聚合考虑
你可以独立对每个分区使用不同的聚合设计方案。得益于这一特性,你可以为那些需要高度聚合的数据集建立独立的分区。尽管这种的设计可以增强性能,但太多的分区也会产生负担。
为了帮助你进行聚合设计,在此提供了一些建议。当你的分区少于10个,每个度量值组最好不多于2种聚合设计;当分区少于50个,每个度量值组聚合设计要少于3种;多于50个分区,每个度量值组聚合设计不多于4种。
因为每个分区都可以有独立的聚合设计方案,最好根据分区的统计信息,将类似的分区归为一组。这样就可以为一组分区应用一种聚合设计。
考虑这样的例子:一个Cube按月建立了多个分区,新的数据将会装载到当前这个月的分区中,并且这个分区是目前查询最频繁的分区。在这种情况下,通行的聚合策略是对最近的分区执行基于使用优化,而老的、不常使用的分区可以不用过多关注。
最新的聚合设计可以复制到一个基本分区。这个基本分区不包含数据,它仅仅是保存当前的聚合设计。当要增加新分区时(比如新的一个月开始了,建立了一个新的分区),可以把基本分区克隆到新分区,这样新分区可以继续使用之前基于使用优化的聚合设计。在执行了第一次执行了FULL处理后,就可以周期性的增量更新分区。关于处理技术,参考“有效地更新分区”章节。