mondrian源码分析和改造设计_第1页
mondrian源码分析和改造设计_第2页
mondrian源码分析和改造设计_第3页
mondrian源码分析和改造设计_第4页
mondrian源码分析和改造设计_第5页
已阅读5页,还剩18页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

mondrian源码分析TOC\o"1-5"\h\z\o"CurrentDocument"概述1\o"CurrentDocument"若干概念1\o"CurrentDocument"架构2\o"CurrentDocument"静态类包分析3\o"CurrentDocument"包解释3\o"CurrentDocument"Schemamanger部分4\o"CurrentDocument"包mondrian.calc4\o"CurrentDocument"包mondrian.olap—接口5\o"CurrentDocument"mdx函数包5\o"CurrentDocument"funCall5\o"CurrentDocument"Query类6\o"CurrentDocument"包mondrian.rolap—计算层6\o"CurrentDocument"成员读取包MemberReader6\o"CurrentDocument"单元格读取CellReader8\o"CurrentDocument"RolapResult类11\o"CurrentDocument"RolapEvaluator类12\o"CurrentDocument"关于排序12\o"CurrentDocument"聚集层Starlayer13\o"CurrentDocument"概述13\o"CurrentDocument"聚合装载过程13\o"CurrentDocument"segment详解14\o"CurrentDocument"缓存失效控制17\o"CurrentDocument"交互管理层18\o"CurrentDocument"初始化MondrianModel18\o"CurrentDocument"获取结果集19\o"CurrentDocument"修改点22\o"CurrentDocument"mondrian角色参数化22\o"CurrentDocument"缓存失效控制22\o"CurrentDocument"connection创建参数优化23\o"CurrentDocument"维度成员排序23\o"CurrentDocument"对mondrianbug的修正24\o"CurrentDocument"to-doList24概述若干概念成员(member):成员是代表维度中一次或多次数据出现的项。度量值也可以算作一个维度,因此一个具体度量值项也可以作为一个成员。元组(tuple):是向量,用于定义来自多维数据集的数据切片;它由来自一个或多个维度的单个成员的有序集合组成。元组用于标识来自多维数据集的特定多维数据块;由来自多维数据集中各个维度的一个成员组成的元组完全描述单元值。换言之,元组是一种成员向量。例如:(时间.[下半年],路线.非陆地.航空),由单个成员组成的元组也可括在圆括号内,但这不是必需的。单元(cell):多维成员的交集创建单元,可以是单个单元或单元块。元组唯一标识多维数据集中的一部分;它不必指某个特定单元,也不必包括多维数据集中的所有维度。集合(set):集合是零个、一个或多个元组的有序集合。集合最常用于在MDX查询中定义轴维度和切片器维度,并且同样可能只具有单个元组或可能在某些情况下为空。下面的示例显示具有两个元组的集合:{(时间.[上半年],路线.非陆地.航空),(时间.[下半年],路线.非陆地.海路)}架构mondrian总共包含四个层次:表示层,计算层,聚集层,存储层.。表示层(presentationlayer):指最终呈现在用户显示器上的,以及与用户之间的交互,有许多方法来展现多维数据,包括数据透视表,饼,柱,线状图.计算层(dimensionallayer):分析,验证,执行MDX查询.一个mdx查询语句会有多个处理阶段。先是计算轴,然后是轴上的单元值。为效率起见,计算层批量将单元请求发送到聚集层。请求转换器允许程序操作存在的请求,而不是为每个请求从头构造mdx请求。元数据描述了计算模型和它怎么匹配到关系模型。聚集层(starlayer):一个聚集指内存中一组计算值(cell),这些值通过维列来限制.计算层发送单元请求,如果请求不在缓存中,或者不能通过旋转聚集导出的话,聚集层向存储层发送请求.聚集层是一个数据缓冲层(cache),从数据库来的单元数据,聚合后提供给计算层。聚集层的主要作用是提高系统的性能。存储层:提供聚集单元数据(cell)和维表的成员(member),这些层可以不在同一机子上,但是计算和聚集层必须在同一台机子上。有三种需要存储的数据:事实数掘事实表)、维度表和聚集数据(即聚合表)架构图如下:

静态类包分析2・1.包解释mondrian.calcmondrian.guimondrian.i18nmondrian.mdxmondrian.olapmondrian.olap4j提供编译好的表达式。mondrian.calcmondrian.guimondrian.i18nmondrian.mdxmondrian.olapmondrian.olap4j设计Mondrianschema的图形接口国际化和本地化工具为mdx表达式定义解析树核心包,定义了连接和schema的元模型,用来执行查询中间层,olap服务器的驱动,用来代替jolap的mondrian.recorder任务处理记录接口mondrian.rolapolap包的数据访问层的实现mondrian.spi用户自定义扩展的服务端支持接口

mondrian.tuimondrian.udfmondrian.utilmondrian.tuimondrian.udfmondrian.utilmondrian.webmondrian.xmla用户定义方法Mondrian工具包Mondrian的servlet和tag库xmlforanalysisAPI的实现Schemamanger部分Mondrian.rolap.RolapSchema类是mondrianschema的核心类,该类在在建立RolapConnection时被建立,但是有个schemaPool维护着schema的缓存,参见RolapSchema.Pool内部类。在RolapSchema对象生成之前,首先有个原始的MondrianDef$Schema对象,该对象相当于mondrianschemaxml文件的简单对应,由xml解析器直接生成。具体来说首先由xml文件解析成dom对象,然后再生成更加结构化的MondrianDef$Schema,最后再load成更加高级的RolapSchema对象,具体参见RolapSchema.load(StringcatalogUrl,StringcatalogStr);以MondrianDef$Schema或RolapSchema对象为根,都还会有一系列的内部类去对应于mondrianschema下的子元素。如果一个Hierarchy的hasAll为true,则会有一个allmember,对应着也需要一个虚拟的alllevel,位于该Hierarchy的levels[0]位置。包mondrian.calcCalc是所有可计算表达式的基接口。在mondrian中关于表达式有如下两个概念:ThelogicallanguageofparsedMDXfragments(Exp).Thephyiscallanguageofcompiledexpressions(Calc).两种语言可以允许我们将逻辑语言(即mdx语言)和物理语言(howitistobeevaluated)分开.物理语言对类型的要求更加严格,andcertainconstructswhichareimplicitinthelogicallanguage(suchastheadditionofcallstothe<Member>.CurrentMemberfunction)aremadeexplicitinthephysicallanguage。Calc接口针对各种类型有许多子接口:其中IntegerCalc,BooleanCalc,DoubleCalcStringCalc是数值型的,MemberCalc,LevelCalc,HierarchyCalc,DimensionCalc贝^用于olap模型中的元素。每一个子接口有都有一个实现的虚基类:AbstractIntegerCalc,AbstractBooleanCalcAbstractDoubleCalc,AbstractStringCalc,AbstractMemberCalc,AbstractLevelCalcAbstractHierarchyCalc,AbstractDimensionCalc表达式(Expression)通常由表达式编译者(ExpCompiler)创式。对于一个给定的表达式通常有若干个evaluation策略,表达式编译者在编译过程中可以给我们一个选择的机会。包mondrian.olap—接口Mondrian的核心包,定义了连接对象和完整的olap模型结构元对象,并且允许执行mdx查询语句。mdx函数包mondrian.olap.fun定义了mdx内置的函数集。函数(function)的定义:参数描述例子name函数名"Members"signature函数标志"<Dimension>.Members"description函数描述"Returnsthesetofallmembersinadimension."flagsEncodingofthesyntactictype,returntype,andparametertypesofthisoperator.Theencodingisdescribedbelow."pxd"Theflagsfieldisanstringwhichencodesthesyntactictype,returntype,andparametertypesofthisoperator.Thefirstcharacterdeterminesthesyntactictype,asdescribedbyFunUtil.decodeSyntacticType(String.Thesecondcharacterdeterminesthereturntype,asdescribedbyFunUtil.decodeReturnCategory(String).Thethirdandsubsequencecharactersdeterminethetypesoftheargumentsarguments,asdescribedbyFunUtil.decodeParameterCategories(String).Forexample,"pxd"means"anoperatorwithpropertysyntax(p)whichreturnsaset(x)andtakesadimension(d)asitsargument".funCallAFunCallisafunctionappliedtoalistofoperands.Theparsercreatesfunctioncallsasanunresolvedfunctioncall.Thevalidatorconvertsittoaresolvedfunctioncall,whichhasafunctiondefinitionandextratypeinformation。参见:Mondrian.olap.ResolveFunCallUnresolveFunCallQuery类用于mdx查询。创建query仓U建:Connection.parseQuery(java.lang.String)。仓U建mondrian.olap.Parser类,基于java_cup实现,调用其parse或debug_parse()方法,里面最主要是CUP$Parser$do_action()方法,内部针对所有语句词句做了解析。其中case75:生成最后的mondrian.olap.Query对象,内部调用parser.makeQuery()方法,内部初始化时会再调用query的resolve,mondrian.olap.QueryAxis的resolve(),进而会执行数据库。执行query执行:Connection.execute(mondrian.olap.Query)返回结果:Result.有些查询从缓存中读取,非常迅速。当也有些需要花费些时间,这时可以通过MondrianProperties.QueryTimeout参数设置timeout。如果想控制返回结果不至于太大,可以通过MondrianProperties.QueryLimit参数控制返回的cell数目。在查询执行的任何时候,另一个进程都可以通过cancel()方法取消该查询,此时Connection.execute(Query)会抛出异常。包mondrian.rolap—计算层实现最终的实际的olap数据访问功能,包括读取维度成员值和cell值。成员读取包MemberReader概述包路径:Mondrian.rolapo该部分的起点是RolapEvaluator类。当一个类似于“member.children”的成员表达式被请求时,RolapEvaluator将调用RolapSchemaReader对象.RolapSchemaReader将再负责调用各个MemberReader对象(每个维度一个memberReader)。在大部分场合下,将使用SmartMemberReader来迅速返回所需要的维度成员值。SmartMemberReaderSmartMemberReader实现了MemberReader接口,它实现了维度成员及其子成员的缓存,如果有一个成员位于缓存中,则还会有一个其子成员的列表。它同时缓存了level下的成员们。该类主要的成员有:source:MemberReader,用于实际从数据库中读取维度成员值。mapMemberToChildren:map,实现成员及其子成员的映射,key为RolapMember,value为List<RolapMember>。mapKeyToMember:map,实现所有成员的缓存,其中的key为MmberKeymapLevelToMembers:map,实现级别及其所有成员的映射,key为RolapLevel,value为List<RolapMember>。上述的source其实为mondrian.rolap.SqlMemberSource类,该类中反过来又存储了SmartMemberReader对象,作为其cache成员属性。成员读取过程:smartMemberReader.getMemberChildren(parentMembers,children,constrain);■最终通过source.getMemberChildren()…,其中反过来会把找到的children赋予mapKeyToMember。■最终除了将结果返回在children输出参数中,同时也对mapMemberToChildren赋值了。另外,smartMemberReader.getMembersInlevel()实现了对mapLevelToMembers的缓存。在newRolapEvaluator()时被调用。在读取时会对成员进行orderby(如果设置了ascending的话。)单元格读取CellReader概述包路径:Mondrian.rolap。Cells会被求值多次。第一次时,Evaluator使用FastBatchingCellReader来求值。当一个单元被求值时,evaluateCurrent()被调用。此时FastBatchingCellReader并没有被调用,而是为那个cell记录了一个CellRequest并且return(notthrow)anexception。在所有的cells都有了对应的CellRequests之后,Aggregation会生成SQL,以一个单独的sql请求来载入所有的cells。然后由AggregatingCellReader重新计算cells,从缓存中返回cells值。

主要方法,Objectget(Evaluatorevaluator)首先根据当前的上下文环境(即一组members)创建cellRequest,cellRequest中包含了所有必要的从star中取值的信息。该组members的交集便是要求值的单元格,其中切片轴上的成员和其他轴上的成员完全同等对待;其中度量轴上的成员要求上StoredMeasure(非计算成员CaculatedMember);度量值上的成员位于第一个。通过调用request的addConstrainedColumn()方法把各member对应的column和value(属StarColumnPredicate)值加至至Urequest中.调用AggregationManager.getCellFromCache(request,pinnedSegments)方法从缓存中获取cell值。首先根据request中的列组索引标识从缓存中获取aggreation缓存对象,如果为空说明缓存还未建立则直接返回null,如果有值则调用aggregation.getCellValue(measure,colValueKeys)方法获取缓存的cell值;getCellValue内部首先会根据measure查找匹配的segment,然后调用segment.getCellValue(keys)从segment的dataset缓存集中查找相应的cell值。如果getCellFromCache返回为null则调用recordCellRequest()记录需求。这些cellrequest会被组织成多个cellrequestbatch,以便将来聚合层进行批读取以提高效率。关于batch的详细讨论参见下面Batch类章节。上层会在适当的时候调用batchCellReading.loadAggregations()以实际读取这些cell值,前提是batches对象中已有cellRequest了。每个batch的读取参见batch.loadAggregation()方法,最终调用聚合层的方法,参见aggreation.load(・・・.)。FastBatchingCellReader.Batch类每个batch对应与一组特定的columns环境下的cell求取(具有相同的列和列值(列值是具体的值,不会是“all”值));从batch的属性可以看出batch包含了哪些上下文:RolapStar.Column[],这个指明了基于哪些列(也即基于哪些维度,包括切片维度)进行读取;Set<StarColumnPredicate>[],保存了每列的限定值,对于一列而言,限定值可能会有多个(毕竟是批处理,一次请求多个);MeasureList,指明求取哪些度量值上的cell(度量值本质是度量维上的限定值)。BitKey,该batch的唯一索引。如图所示的一个mdx查询结果界面:MDXW询编辑者囱Iselect{[Measures].[YJD],[Measures].[GCLC]}ONCOLUMNS^ZINONEMPTYOrder(Crossjoin({[dimLX].[AlldimLXs],[dimLX].[AlldimLXs].[龙苦[dimLX],[AlldimLXs].[宁波一梁辉L[dimLX].[AldimLX司.[峡义爪]「[dimLX],[AIIdimLXs],[泽围一坎门L[dimlLX].[AIIdimLXs]j同江一三亚],[dimLX].[Alldiml_Xs].[云寿线)jdimLX],演1dimLXsjj金丽温],[dimLX].[Alldiml_Xs].[iM龙线]},{[dimTime].[AIIdimTimes]}),[Measures].[YJD],DESC)ONROWSfrom[干线交通重调查情况]'''''''where[dimStation].[AldimStatSons]^1?^市]应用转换指标路线调查时间J拥挤度%>观测里程(公里)-所有路犊4全部123.25%2955.032+宁波一梁辉4全部326.67%120.604+同江一三亚+全部32.45%317.769Slicer:[弛市二宁波市]此时会产生两个batch,每个batch最终可能会产生若干segment,segment是cells的集合,segment数和度量值个数相同。一个batch是(其中“当量数/适应交通量=拥挤度”,拥挤度是计算成员),最终产生3个segment,每个segment只有一个cell:(地市=’宁波市’,measure=’观察里程’)(地市=’宁波市’,measure=’当量数’)(地市=’宁波市’,measure=’适应交通量’)另一个batch是(其中的G310等是路线代码,最终过滤掉空值后就剩下两个了),最终产生3个segment,每个segment有多个cell:»(地市=’宁波市’,roadIdin(G310,G322,G210,S321....),measure=’观察里程’)»(地市=’宁波市’,roadIdin(G310,G322,G210,S321....),measure=’当量数’)»(地市=’宁波市’,roadIdin(G310,G322,G210,S321....),measure=’适应交通量’)2、...RolapResult类RolapResult是一个运行中的请求的结果集。Mondiran的执行结果由RolapResult类表单,由于mdx查询语句本身就包含onrows(行轴上)、oncolumns(列轴上)和where部分(切片轴上),结果集中相对应的为ROlapAxis对象,这其中有个sliceAxis对象。因此结果集是由若干ROlapAxis对象和一个RolapCell组构成的。每个axis对象又由若干Position对象组成,每个Position对象又可能由若干member组成(注意一个postion会横跨多个维度的成员)。注意ROlapAxis是抽象类,实际的对象类可能随着不同的轴是不同的。如图:图中,column轴上两个position(每个position含有一个成员),分别是:[[Measures].[YJD]][[Measures].[GCLC]]Row轴上有三个position(每个position含有二个成员),分别是:[[dimLX].[AlldimLXs],[dimTime].[AlldimTimes]][[dimLX].[AlldimLXs].[宁波—梁辉],[dimTime].[AlldimTimes]][[dimLX].[AlldimLXs].[同江一三亚],[dimTime].[AlldimTimes]]切片轴上则有^■个position:[[dimStation].[AlldimStations].[宁波市]]单元值们则放置在RolapResult中的cellinfos对象里,属CellInfoContainer接口,其中存放着Cellinfo,并通过Cellkey进行索引。CellKey:用于在maps里访问cellinfo时使用的键值,根据cell的位置来决定键值。CellKey共有四个默认实现,及zero、one、two、three和many版的实现,分别对应着轴的个数。这些类中关键的属性便是存储各轴的位置值。CellInfo、CellInfoContainer:内部类。CellInfo包含了一个cell所需要的所有信息(最关键的包含value值和一些formatter设置);最终将作为构造ROlapCell对象的参数。CellInfoContainer显然是cellInfo的容器,并使用CellKey来索引。ROlapCell:最终返回给jpivot的cell单元值。RolapEvaluator类最终负责在多维环境中执行mdx表达式。该类中维护一个很重要的对象,即currentMembers,该上下文对象针对每个维度都包含了一个成员;通过setContext方法用来设置当前维度,以开始计算当前维度组合下的表达式值。关于排序排序有单元值排序和维度成员排序两种场景,但单元值排序优先级更高,只有当单元值排序相当时,才再对成员值进行排序。单元值排序有六种,升序、降序、打破Hierarchy升序、打破Hierarchy降序、topcount、bottomcount。其中前四种参见:FunUtil.SortMembers()中的方法,这是总入口,其中有BreakMemberComparator、HierarchicalMemberComparator、BreakArrayComparator、HierarchicalArrayComparator等排序器(Array包含多个member,此时相当于cell上下文环境由多个维度参与)。对于array类排序,参见其中的publicintcompare(Member[]a1,Member[]a2)方法;对于单member类排序,参见其中的publicintcompareInternal(Membera1,Membera2)方法。维度成员排序似乎有三种:按照orderKey、按照ordinary>按照值(参见RolapMpareTo()方法,最终相当于按字符串或数值比较,其中第三种内部又会用到ascending属性),这个是我们自己新增的属性。具体参见FunUpareSiblingMembers()方法。聚集层Starlayer概述包mondrian.rolap.agg,管理聚合缓存,这些缓存中包含着各单元格值。RolapStar中含有aggregation(聚合),一个aggregation是针对一组columns的,该聚合可以包含多个segment,同一个aggregation中的每个segment都将覆盖到相同的列集合;每个segment表达了一组cell值,这些cell自然是由具体的列值和一个必备度量值(如下面的unitsales)来限定,如:(Unitsales,Gender=,F,,Statein{'CA','OR'},MaritalStatus=anything),由于其中的列值可能会取多个,因此最终表达的cell值也可能是多个。RolapStar中有一个aggregations,是一个map对象,通过request的constrainedColumnsBitKey来索弓|一个aggregation。聚合装载过程实际中无论是底层的单元值还是聚合后的单元值都是放在聚合对象aggregation中的。以aggregation.load(colums,measures,predicates,pinnedSegments为入口:■参数中的除了measure不一样外,其限定的列(colums)及列值(prediactes)都是一致的。因此转换成对对若干segment[]的求值:Segment.load(segment[],•••.),在该方法内部:>首先根据segments中的信息生成sql查询语句,有两个不同的生成类:AggQuerySpec和SegmentArrayQuerySpec,前者用于找到聚合表情况下的sql语句生成,后者用于基于原始表的sql语句生成。具体可以参见它们的generateSqlQuery()方法,这里注意对以distinctcount有不同的生成方法。Sql生成的核心类是sqlQuery,类似于交换系统中的QuerySqlFactory类。注意:聚合操作如avg、sum等都最终还是利用sql语句实现的,并非mondiran自己实现这些聚合功能。利用jdbc,执行sql语句,获取到jdbc结果集。参见mondrian.rolap.RolapUtil.executeQuery()方法。解析结果集,将结果集中的数据填充到rows[][]二维数值中,并且把各列的值也填充好。如图:结果集每条记录的值如宁波市、G010・・・.,前面两个是维度列值,后面几个是度量值。各列的值(其中第0项值为:[宁波市]):决定采用稀疏性(sparse)还是稠密性(dense)SegmentDataSet存储;并创建该空的DataSet对象。每个segment关联一个DataSet对象;但其稀疏性还是稠密性都是一致的。注意dataset中单元值的个数可能是1个或多个,是由各限定列的指定值个数乘积,若所有限定列都取单值,则显然最终决定一个唯一的单元。将上述的rows中间集转换到SegmentDataSets集中。最后再分拣给每个segment,确保每个segment的setData(SegmentDataSet)被调用。■若干排序:ValueColumnConstraintComparatorsegment详解■下图是有两个限定列的两个segment的描述(注:其中roadid列虽然指定了8个候选值,但由于使用了空行/列过滤,最后只剩下两个路线有值,故最后segment结果

集的单元数也只有两个,对应于G010和G318的):其中第二个对应的dataset为:[317.769,120.604]对应的透视界面为(参见其中的“观测里程”度量值,与上面的dataset一致):再譬如有三个限定列的segment描述,它们位于另一个aggreation对象中:(其中timeld列的any代表所以可能的时间值,共有2003〜2005三个年,所以最终该segment共有3个cell值)Segment#S{nieasure=sum(FACT_T^^调.观测里程)D干线交调—观测立4地市={宁波市}DIM_干线交调_观测]站.ROADID={S318)

干线交调—时间.TIHEID=any}对应的dataset为:[129.910,129.909,57.950]对应的透视界面为(显然该aggreation还有另外一个segment,其中的roadId对应于G010-宁波梁辉):

指标路线调查时间*拥挤度驰>观测里程(公里)-所有路绣味全部123.25%2955.032+同江一三亚-全部32.45%317.7692003年30.61%129.9102004^35.85%129.9092005年31.68%57.950*宁波一梁辉-全部326.67%120.6042003年280.12%40.2002004^390.74%40.2022005年309.16%40.202[弛市二丁波市]再譬如维度中有多个层次的情况时,一个维度会对应多个列:Queryquery=connection.parseQuery("SELECT"+"{[Time].[1997],〃+"[Time].[1997].Children}ONCOLUMNS,"+"{[Customer].[USA],"+"[Customer].[USA].[OR],"+"[Customer].[USA].[WA]}ONROWS"+"FROM[Sales]");Resultresult=connection.execute(query);该语句执行后产生的segment分别为(除了第一个外,其他segment都会包含多个cell,因为它们的限定列中含有多值的情况):SegmentYN#1YearNationUnitSales1997USAxxxPredicates:Year=1997,Nation=USASegmentYNS#1YearNationStateUnitSales1997USAORxxx1997USAWAxxxPredicates:Year=1997,Nation=USA,State={OR,WA}SegmentYQN#1YearQuarterNationUnitSales1997Q1USAxxx1997Q2USAxxxPredicates:Year=1997,Quarter=any,Nation=USAYearQuarterNationStateUnitSalesSegmentYQNS#11997Q1USAORxxx1997Q1USAWAxxx1997Q2USAORxxx1997Q2USAWAxxxPredicates:Year=1997,Quarter=any,Nation=USA,State={OR,WA}2.6.4.缓存失效控制为了提高访问效率,必须要使用缓存;但使用缓存必须在数据库底层数据发生变化时做好数据的失效和刷新工资。老版的做法非常粗略:mondrian.rolap.RolapSchema.clearCache();这意味着连mondrianschema都被失效,与之关联的全部缓存也都被失效。新版的做法是引入了mondrian.olap.CacheControl接口,可以很精细地由应用控制缓存。为此mondrian引入了cellregion的概念,region是由一个或多个成员定义出的多维空间(定义时可以指定是否包含这些成员的子成员)。为了使缓存失效,你需要首先定义一个region,然后告诉mondrian失效该region内的所有cells;为了确保一致性,mondrian还会自动地把这些cell的聚合单元也一起失效。具体参见CacheControl.CellRegion类。最终调用cacheControl.flush(cellRegion)。具体失效时,还是针对segment进行的,因为mondiran中的cell缓存是通过segment组织的。失效发生时,可能segment中的部分cell无用了,但mondrian并不会真正删除这些cell,而是strengthened其predicates,例如失效前的某个segment为:1997USAORxxx1997USAWAxxxPredicates:Year=1997,Nation=USA,State={OR,WA}然后Crossjoin([time].[1997].[Q2],[Customer].[USA].[OR])的cellRegion失效,此时该segment变为:1997USAORxxx1997USAWAxxxPredicates:Year=1997,Nation=USA,State={WA}目前这样处置未必最合理,因为会有内存泄露。将来可能根据该segment中的cell达到定的失效阀值,就彻底把该segment抛弃掉。Segment的合并也是将来应考虑的事情,以减少segment碎片。维度成员值的缓存控制目前mondrian还未提供控制接口。但是metaCacheControl,例如控制schemaxml等还是提供不少接口了,例如:mondrian.rolap.RolapSchema.clearCache()mondrian.olap.MondrianServer.flushSchemaCache()mondrian.rolap.cache.CachePool.flush()mondrian.rolap.RolapSchema.flushRolapStarCaches(boolean)该方法不失效schema,但失效缓存,在应用层还不想使用cellRegion失效缓存前,可以适度使用。mondrian.rolap.RolapSchema.flushAllRolapStarCachedAggregations()mondrian.rolap.RolapSchema.flushSchema(String,String,String,String)mondrian.rolap.RolapSchema.flushSchema(DataSource,String)交互管理层MondrianModel是JPivot中用来展现一个MDX查询的所有原数据的模型。这里主要讲MondrianModel的三个方法,initialize。,getResult(),destroy。。初始化MondrianModel1、在jpivot.mondrian.MondrianModel执彳亍initialize()方法之前,先用MondrianModelFactory.Config对象为MondrianModel对象设置jdbc,schemaurl等参数。2、调用mondrian.olap.DriveManager.getConnection()方法,创建并返回mondrian.rolap.RolapConnection对象,置于变量monConnection中;创建连接的参数指定详见mondrian.olap.RolapConnectionProperties。2.1在创建RolapConnection对象的构造函数中,会用对象池技术建立mondrian.rolap.RolapSchema对象。该对象会被缓存在对象池中,只会创建一次(RolapSchema对象中还有一个大对象xmlSchema,是纯定义性质的,Mondrian.olap.MondrianDef.Schema);这里需要注意的是:RolapSchema对象的建立有几种方法:首先schema字符串内容本身可以通过参数直接传递进来;也可以通过url地址指定;还可以通过自定义的DynamicSchemaProcessor类来给定(这样就有机会动态对schena内容进行修改)。RolapSchema对象本身的建立一般是从缓存中读取,没有时则创建。但若使用了上述的DynmicSchemaProcessor则每次都会重新创建;并且若指定了UseContentChecksum参数(将使用md5对字符串内容编码),若UseContentChecksum为true,贝md5编码值与以前的不同,则即使存在了RolapSchema对象也会重新创建。可与DynamicSchemaProcessor属性一起用。在Mondrian新版本3.0中,RolapConnectionProperties中除了定义了DynamicSchemaProcessor和UseContentChecksum参数,还新增了UseSchemaPool属性,如果该属性为false,将禁用schema缓存。2.2最后调用RolapSchema.createRole()方法创建该连接关联的role对象(如果指定了的话);并创建ROlapSchemaReader对象(和role绑定的,读取时会进行必要的控制)。2.3根据传入的参数设置locale,没有的话按照en处理。3、调用connection的parseMDX(),最终调用ConnectionBase.parseQuery()方法准备好一个mondrian.olap.Query对象,置入成员变量monQuery;query对象由mondrian.olap.parse对象生成,该对象是一个自动生成的类,使用了javacup技术。获取结果集1、之前确保MondiranModel中的monQuery对象已经创建好。2、Jpivot中,界面会调用jpivot.table.TableCompont对象的render方法进行绘制,最终会调用MondrianModel.getResult()的方法,但返回的是jpivot.olap.model.Result对象,并非mondrian.olap.Result对象。首先执行MondrianQueryAdapter对象queryAdapter的onExecute()。得到mdx语句。调用monConnection.execute(monQuery)获取mondrianresult对象;内部会创建newROlapResult(monQuery,true),RolapResult得到的是运行中请求的结果,其中ture代表要实际装载数据。2.3内部会进行Evaluator算法:显式成员:在轴上的成员。隐式成员:不在轴上的成员,可能是在函数中。如果一个维度的成员都是隐式成员,相当于在切片上使用默认成员。有两种特定维度:时间维度和度量维度。时间维度度量维度的默认成员是没有所有的XX的,约定成俗的是第一个度量。首先,RolapEvaluator会被创建。创建过程中,它会从每个hierarchy中获取一

个member,每个member是hierarchy的默认成员。对于大多数hierarchy,这个默认成员就是所有成员。可能有两种例外:1)hierarchy没有所有成员;2)hierarchy有所有成员但是它不是默认成员。这样的话,默认成员仍然会被使用,但是可能会有问题。然后载入所有无所有成员的hierarchy的根成员,载入非默认成员的所有的成员。2.4内部会进行Evaluator算法,三大步骤:Determineaxes步骤:决定每个轴上的所有成员,但此时还不保存信息(不创建RolapAxis)。确定切片轴上的成员。轴上发现的成员都会被加入到AxisMember中。如果该成员是度量,则切片指明请求的度量,并将该度量放入evaluator的context中(取代cube中的第一个度量,即默认度量)。其它的切片成员也会被放入evaluator的context的。executeaxes步骤:为每个轴保存所有成员,创建RolapAxis对象(重复了第一步骤,只是此时创建了RolapAxis对象,该对象是结果集中的维度成员值部分)。执行第一步和第二步时确保数据已经载入。getcell步骤:根据轴上的成员值计算并存储每个单元值(结果集的cell值部分),参见executeBody()方法,内部主要调用executeStripe()方法,»ExecuteStripe()方法内部采用递归方法依次获取每个单元值,从行轴开始(轴位置索引最大的那个),针对每个行轴的mondrian.olap.position进行循环,在一个循环内再针对歹0轴的每个position进行循环,求得交叉点的cell值。每个position由一个或多个维度成员值组成,具体的个数同该轴上交叉的维度数在计算某cell值之前,会依次用该cell的member值(包括切片上的维度值)设置好evaluator的context。最后调用evaluator.evaluateCurrent()方法获得当前context下的cell值。》Evaluator.evaluateCurrent()内部可能会执行到间接递归调用,这个发生在执行计算成员值的情况下;此时为了计算成员,首先需要获取evaluateCurrent()方法。EvaluateCurrent内真正获取单兀值的地方是调用:cellReader.get(this);此处使用的cellReader为FastBatchingCellReader类,调用其get()方法,内部实际是从aggregation的缓存中获取单元值。其他度量值,而为了获取这些前置的度量值又要调用»最后所有的单元对象CellInfo存放在CellInfoContainer中,通过cellkey进行索弓I。其他度量值,而为了获取这些前置的度量值又要调用■问题是,上述最终是从aggregation缓存中获取单元值的,但一开始缓存中的值又从哪里来呢,这是个关键问题。原来executeBody第一次调用ExecuteStripe()时,从缓存中获得的值都只是空值,但同时会记录下所有的cellRequest信息;然后执行完executeBody后紧接着就会调用FastBatchingCellReader的另一个方法loadAggregation,该方法内部会根据刚才设定的cellRequest信息实际从数据库中读取信息并赋予这些cells。再之后会第二次执彳亍ExecuteStripe从而获取到真正的cell值,循环往复,直至没有新的cellRequest产生为止。参见executeBody主体代码:While(true)(evaluator.setCellReader(fastBatchingCellReader);ExecuteStripe(evaluator);If(!fastBatchingCellReader.loadAggregation(query))return;//此处可以退出循环了。}其实上面的第一步骤或第二步骤中,在决定各轴成员值时,可能就需要获取cell值(如排序函数),此时就已经会记录request和进行loadAggregation了;如果这样的话,到第三步时可能缓存中就已经有值可以直接获取了。研发注意:在RolapEvaluator中,许多方法对性能的影响很大,这里都用了final,以便JVM可以优化它们。如果未来需要的话,final可以移除,方法可以重写。2.5执行完execute后,将mondrian的result转换成JPivot的result。4.修改点mondrian角色参数化mondrian本身是支持角色的(但mondrian本身不负责角色与用户的挂钩);在实际应用中,角色往往不能事先定义死,而是可能定义出一个角色模版,但里面会有些参数;实际运行时再把这些参数实例化。例如:<MemberGrantmember="[dimStation].[AlldimStations].[{area}]"access="all”/>在mondrian的ROlapSchema模型对象中原有一个mapNameToRoles哈希表,存储着schema中所有的role对象(不是role定义,role定义本身是另外一个对象);在mondrian的ROlapConnection对象中则有一个role对象,代表当前connection使用的角色;该角色实际上是引用schema角色组中的某个角色,schema角色组中的对象为所有连接共享。当引入参数化需求后,由于每个连接建立时传递的参数都不一样,因此原schema中的角色对象组列表已经没有意义,原schema中的role定义信息则还需要保留下来;而connection中的role对象则每次都根据实例

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论