本文主要介绍Mondrian缓存模块源码分析以及缓存扩展实现。
概述
联机分析处理(On Line Analytical Proccessing,简称OLAP) 概念最早由关系数据库之父E.F.Codd于1993年提出。OLAP应用是目前数据仓库上的重要应用之一,是决策分析的关键。作为数据仓库最重要的多维分析工具,OLAP利用存储在数据仓库中的数据完成各种分析操作,并以直观易懂的形式将分析结果返回给决策人员。它的目标是满足决策支持或多维环境特定的查询和报表需求,技术核心是多维分析。OLAP具有灵活的分析功能、直观的数据操作和分析结果可视化表示等突出优点,从而使用户对大量复杂数据的分析变得轻松而高效,以利于迅速做出正确的判断,辅助决策。
Pentaho Analysis Services,即 Mondrian(项目代号),是 Pentaho 的多维分析、OLAP 解决方案。 Mondrian 就是一个 OLAP 引擎,而且是一个 ROLAP 引擎,ROLAP 指的是使用关系数据库的OLAP它本身不管理数据的储存,这是由关系数据库来做的,它通过 JDBC 来访问数据。这样可以大大减少 OLAP 引擎的复杂性,而且可以使用多种数据库。
Mondrian 采用开源协议 Eclipse Public License。
Mondrian从架构上可以分为四个层次:展现层、维度层、星层、存储层。
展现层确定了终端用户最终能够通过系统在显示器上看见什么内容,并提供了用户与系统进行交互的手段和方式。这里有多种方法展示多维数据,包括枢纽表(pivot tables)、饼图,线性图和柱状图等,和一些高级的图形展示工具,如可交互的地图、动态图形等。展示部分的程序可以通过java的Swing或者JSP进行实现,而图表可以被返回为JEPG或者GIF格式的文件,或者通过XML进行封装传输。用户通过展示层询问系统要展示什么东西,OLAP服务回应该询问。
第二层是维度层。维度层按阶段确认和执行了MDX查询请求。一个查询请求可以看成由多个阶段执行完成。维度轴线首先被计算,然后计算该维度轴线上的单个元素值。为了提高计算效率,维度层将元素(cell)请求以批量的方式发送给了聚合层(aggregation layer)。同时元数据描述了该维度模型和如何将维度模型映射为关系模型的方法。
第三层是星层,它负责维护一个聚合体(一个或多个cubes)的缓存,即一个聚合代表了由维度信息表征的内存中的元素(cell)的一个集合。第二层的维度层传送了对指定元素的请求给星层,如果所需要查询的元素不在该缓存中,或者正在执行对聚合的滚卷操作,聚合管理器将发送一个数据请求给存储层,从而装载数据到内存中。
存储层为一个RDBMS。它负责提供被聚合的元素数据和维度表的成员。Mondrian目前主要持RDBMS作为数据源,不支持直接的多维数据库。
以上四层可以同时部署到一台机器上,也可以进行分布式部署到多台机器上。但是第二层和第三层必须部署到一台计算机上,其组成了Mondrian服务。存储层可以部署到其它的机器上并通过远程JDBC连接进行访问。在一个多用户系统中,展现层可以存在在每个用户终端机上(除非通过Jsp页面以服务器的方式产生)。
为了提高海量数据下的查询响应速度,Mondrian自动将首次查询的结果缓存到内存中,之后的查询如果命中缓存内容,则不再访问数据库。这种实现方式实现实时OLAP时会存在问题,实时OLAP中数据变化频繁导致缓存中的数据不是最新的。
缓存控制接口:为了做到不重启OLAP Server也能更新缓存,Mondrian提供了一系列的刷新缓存的接口,支持指定清除指定schema的元数据缓存、查询结果缓存;清除动作可以是全部清除 也可以是部分清除。
缓存功能分析
Mondrian中缓存功能,查询过的mdx语句会存入到缓存中。下次再次查询时数据将直接在缓存中读取。
在Mondrian中使用的是软引用,如果内存不足时对象将会被回收。但是有些SegmentBody非常的大。这样导致对象经常被回收,经常需要重复的查询数据库。所以mondrian优化可以从修改缓存的部分开始,可以将mondrian的缓存放到另一个进程里,这样就可以大大的减轻mondriand造成的压力。可以将缓存部分修改成现在比较流行的ehcache或memcache,而且ehcache与memcache还支持集群或者文件形式的缓存。
如果你的数据量非常的大,数据库的查询性能,已经无法在优化,例如非传统数据库 hive spark sql ,这种数据库在查询性能方面天生具有缺陷。这时你可以修改缓存部分来提高mondrian的易用性。在缓存中存放已经聚合完成的数据,这样下次请求时可以直接可以在缓存中提取聚合后的数据。
缓存类型
Mondrian有三种不同的缓存:Schema、Member、Segment
Schema Cache将Schema 保存在缓存中,因此每次cube多维数据集加载时不需要重读。
Member Cache从内存中的维度中存储Member值,从而减少了从数据库读取的次数。
Segement Cache将预先计算的值存储在缓存中,不需要回收和重新计算,可以减少重复读取计算的次数,显著加快分析速度。
缓存清除
当原始数据改变时需要更新缓存数据,通常当ETL执行完需要清除缓存数据。
Schema
每个Schema缓存将唯一保存在内存中。当模式从缓存中刷新时,其关联的Member和Segment缓存也被刷新,这是一种性能很低的方法。但是,如果模式已经更改,或者确定要刷新的内容的细节太复杂,那么这可能是最好的方法,当然也是最简单的方法。缓存可以是使用前一节中描述的技术重新填充。
大多数Mondrian工具,如PunaHo,提供了一种手动刷新的方法。在PunaHo中,您可以使用企业控制台或用户控制台。如果你是作为管理员登录到用户控制台,只需选择工具>刷新>Mondrian缓存。
手动方法很好,但管理员通常希望自动化。缓存刷新作为整个ETL工作流的一部分。下图显示Mondrian如何高速缓存适合整个ETL工作流。在填充OLAP数据库之后,Mondrian被调用来刷新并保存缓存。这个过程确保了缓存与基础数据库同步,以便在执行分析时,数据是最新的。
为了便于将缓存保存到ETL过程中,可以创建一种包含刷新缓存部分的方法的类。您也可以创建一个JSP使用新类。然后通过配置参数调用URL来保存缓存变得容易。
下图显示了ETL工具将支持的三种情况。场景从刷新所有内容到刷新缓存的特定区域。Mondrian的缓存控制SPI非常详细,可以允许您控制缓存的任何部分。
清单显示了控制缓存的JSP代码接收参数,然后调用CacheFlusher类以刷新指定的部分缓存。这很容易将缓存从用户界面中清除的工作分开。或者在适当的情况下嵌入应用程序中。
-
JSP刷新缓存
为了刷新整个缓存,可以简单地调用JSP,而不需要参数。例如,如果JSP部署到本地机器的patahoweb应用程序的公用文件夹中。会调用http://localhost/punaho/pult/ flushcache.jsp。这将调用CacheFlusher类的FlushAll()方法,这只适用于新的连接。
-
整个Shema缓存刷新
Cube
在有许多不同的模式和立方体的环境中,可能存在多个只适用于指定立方体的ETL处理过程。数据更新后只有被影响的Cube立方体的缓存数据需要更新。这意味着其他Cube立方体将继续使用缓存。这只会影响新的连接。
以下代码显示了清除指定Cube立方体缓存的代码。若要清除缓存,只需调用JSP并指定Catelog和Cube清除缓存数据。
Ø 指定Cube立方体缓存清除
这样很好清除了整个Cube立方体缓存。但通常只是想更新Cube立方体的部分数据,特别是当时间为维度时,因为过去事实不应该改变。下一节描述如何刷新特定区域的立方体缓存。
Region
更新指定区域Cube多维数据集缓存提供了对缓存的最佳控制。假设已经跟踪销售数年了。有个每晚需要从操作数据库更新数据到数据仓库的处理过程,只需要更新改变的数据缓存,如本月的销售。下面代码显示了刷新区域所需的代码。看起来有点复杂,但是其实只有几个关键调用参数。代码Schema和Cube现在了解了,所以我们只关注代码清理区域。
单元格区域是Cube立方体中需要清除的一组单元格区域。memberNameToSegmentList 方法将成员名称转换为成员的特殊列表。
一旦定义了所有区域,就创建交叉连接和缓存控制,对象刷新区域。下一个调用mondrian指定区域中的单元格将从数据库读取并填充缓存。
源码分析
Session Manager:最为重要的一个部分。接受MDX查询、解析MDX,返回结果。
Schema Manager:与初始化紧密相关。主要是一些重要的数据结构如缓存池的构建以及多维模型的生成。
Aggregate Manager:实现了对聚集表的管理。主要是对OLAP缓存的管理,属于性能优化的部分。
Dimension Manager:维度的管理。实现多维模型中维度和关系数据库表中列的映射,在Schema Manager也有部分功能处理这些映射。
Schema缓存
Schema缓存将Schema对象保存在缓存池中,因此每次cube多维数据集加载时不需要重新加载,显著加快分析速度。
缓存相关类
| 类名 | 说明 |
|---|---|
| mondrian.rolap.RolapConnection | Mondrian OLAP服务连接 |
| mondrian.rolap.RolapSchema | 多维数据模式类 |
| mondrian.rolap.RolapSchemaPool | Schema缓存处理类 |
| mondrian.rolap.SchemaKey | Schema缓存key |
初始化缓存
-
初始化Schema缓存池
-
初始化RolapConnection对象
每个RolapConnection有一个RolapSchema对象,RolapConnection对象初始化过程中会初始化对应的Schema对象。
Schema对象初始化过程中首先去Schema缓存池中查找数据,如果存在缓存数据对象则直接返回,否则初始化加载Schema对象,将该对象保存到缓存池中。
-
初始化Schema对象
(一)解析XML生成Schema
通过RolapSchema的load方法,传入Catalog的XML形式的Schema,使用org.eigenbase.xom.XOMUtil创建一个XML的解析器,将XML传入解析器,返回一个DOMWrapper对象_def。根据此对象创建DOMElement解析器,用它来解析Schema下包含的元素,获取Schema的所有属性,并将annotation、parameter、dimension、cube、virtualCube、namedSet等片段存入NodeDef集中,形成一个树形结构,对此树进行深度优先遍历。
(二)RolapSchema加载Schema
首先,检查在Schema中是否有用户自定义函数,然后初始化FunctionTable。然后根据Schema解析结果树,对层次创建SmartMemberReader用于维度成员以其子成员的缓存。最后初始化AggregationManager,然后返回。
加载缓存
根据RolapConnection初始化对象传入的连接参数查找Schema缓存数据,如果存在直接返回Schema缓存对象,否则创建加载Schema对象,将对象保存至缓存池中。
清除缓存
Schema缓存将Schema对象保存在缓存池中,当Schema XML文件修改更新后,需要清除Schema缓存重新加载缓存数据。
Segment缓存
Segement 缓存将预先计算的值存储在缓存中,不需要回收和重新计算,可以减少重复读取计算的次数,显著加快分析速度。
缓存相关类
| 类名 | 说明 |
|---|---|
| mondrian.spi.SegmentHeader | 用于从Segment缓存中检索Segment的关键对象 |
| mondrian.spi.SegmentBody | 包含Segment缓存数据的对象 |
| mondrian.rolap.agg.SegmentCacheManager | 活动对象,用于维护“全局缓存”(在JVM中,但是在使用特定模式的连接之间共享)和“外部缓存” |
开启关闭缓存
通过配置文件属性配置项控制Mondrian数据缓存的使用,disableCaching控制每次执行查询是否缓存数据,disableLocalSegmentCache控制是否使用本地缓存。
- mondrian.properties配置
- MondrianProperties.java源码
- 说明
| 名称 | 说明 |
|---|---|
| mondrian.rolap.star.disableCaching | True:不使用缓存False:使用缓存 |
| mondrian.rolap.star.disableLocalSegmentCache | True:不使用本地缓存False:使用本地缓存 |
缓存服务扩展
Mondrian缓存服务扩展可以通过属性文件配置、服务查找实现。实现流程为关闭本地缓存、实现外部缓存扩展。
关闭本地缓存
mondrian.properties配置
mondrian.rolap.star.disableLocalSegmentCache=true关闭MemorySegmentCache本地缓存处理
服务扩展实现
-
实现SegmentCache接口
-
配置缓存服务
通过配置文件配置服务类
将自定义扩展实现SegmentCache接口类在mondrian.properties属性文件中配置
mondrian.rolap.SegmentCache
通过服务查找实现
创建服务配置文件mondrian.spi.SegmentCache
处理代码