现在要弄1些数据分析(OLAP)相干的数据,恰好mondrian提供了1个用于测试的footmart数据集,这个数据库主要记录了1些关于销售数据的事实表和维度表,内容很丰富,并且提供了foorMart.xml文件,这个文件定义了所有需要使用的cube的定义集合,但是我们现在用自己开发的系统定义cube,所以这个配置文件应当就用不上了,主要需要生成这个数据库的数据,首先将它放到mysql上吧,然后再将它通过sqoop导入到hive中,用我们的OLAP报表系统定义cube和报表履行查询,这1套流程走下来基本上能把OLAP查询的基本流程走通了,接下来的主要工作还是在如何优化多维查询的性能。
首先这个
数据库数据的生成是通进程序履行得到的,首先我们需要下载mondrian的某个版本(根据官方文档),下载地址在http://sourceforge.net/projects/mondrian/,但是有个问题就是在3.8.0版本以后只能找到jar包了,不能够找到完全的那个zip包,所以我就舍弃了最新的版本,下载1个比较新的3.7.0版本的zip包(http://sourceforge.net/projects/mondrian/files/mondrian/mondrian⑶.7.0/mondrian⑶.7.0.0⑺52.zip,大小为119M),解压以后在testsrc/main/mondrian/test/loader目录下有1个MondrianFoodMartLoader.java文件,这个java程序就是为了生成测试数据的,固然测试数据都是寄存在1个.sql文件里面,这个文件在demo目录下,文件名是FoodMartCreateData.zip,将它解压过以后,编译MondrianFoodMartLoader程序,然后运行以下命令:
-verbose -tables -data -indexes -jdbcDrivers=com.mysql.jdbc.Driver -outputJdbcURL=jdbc:mysql://172.17.3.102:16666/foodmart -outputJdbcUser=root -outputJdbcPassword=root -outputJdbcSchema=foodmart -outputJdbcBatchSize=50 -inputFile=C:UsersAdministratorDesktopFoodMartCreateData.sql
根据参数名可以看出参数的含义分别是:
-verbose 详细输出日志信息
-tables 如果表不存在则创建表
-data 如果数据存在则删除全部数据在load,如果不选则不导入数据
-indexes 决定是不是创建索引
-outputJdbcUser、 -outputJdbcPassword 导出
数据库的用户名和密码
-outputJdbcSchema 目标
数据库名,如果不指定则使用默许的
-outputJdbcBatchSize 是不是批量插入,默许是50条记录1批进行插入
-inputFile 导入的数据文件,就是之前在mondrian中提供的解压以后的.sql文件路径
创建完成以后有以下1些表:
mysql> show tables;
+-------------------------------+
| Tables_in_foodmart |
+-------------------------------+
| account |
| agg_c_10_sales_fact_1997 |
| agg_c_14_sales_fact_1997 |
| agg_c_special_sales_fact_1997 |
| agg_g_ms_pcat_sales_fact_1997 |
| agg_l_03_sales_fact_1997 |
| agg_l_04_sales_fact_1997 |
| agg_l_05_sales_fact_1997 |
| agg_lc_06_sales_fact_1997 |
| agg_lc_100_sales_fact_1997 |
| agg_ll_01_sales_fact_1997 |
| agg_pl_01_sales_fact_1997 |
| category |
| currency |
| customer |
| days |
| department |
| employee |
| employee_closure |
| expense_fact |
| inventory_fact_1997 |
| inventory_fact_1998 |
| position |
| product |
| product_class |
| promotion |
| region |
| reserve_employee |
| salary |
| sales_fact_1997 |
| sales_fact_1998 |
| sales_fact_dec_1998 |
| store |
| store_ragged |
| time_by_day |
| warehouse |
| warehouse_class |
+-------------------------------+
可以看出这里面的store_sales、store_cost、unit_sales可以作为度量的列,然后将他们使用某些聚合方式计算就能够作为度量了。其余的例如product_id、time_id、customer_id、promotion_id、store_id是其它维度表的主键,通过这个id来关联维度表,例如与time_id关联的表是time_by_day,表结构以下:
| time_by_day | CREATE TABLE `time_by_day` (
`time_id` int(11) NOT NULL,
`the_date` datetime DEFAULT NULL,
`the_day` varchar(30) DEFAULT NULL,
`the_month` varchar(30) DEFAULT NULL,
`the_year` smallint(6) DEFAULT NULL,
`day_of_month` smallint(6) DEFAULT NULL,
`week_of_year` int(11) DEFAULT NULL,
`month_of_year` smallint(6) DEFAULT NULL,
`quarter` varchar(30) DEFAULT NULL,
`fiscal_period` varchar(30) DEFAULT NULL
) ENGINE=NTSE DEFAULT CHARSET=utf8 |
这样我们就能够设定time维度,这个维度关联的维度表就是time_by_day ,可以选取该表中可以枚举的列作为维度的级别(例如the_month、the_day、the_year、day_of_month、week_of_year等),这些级别之间可以有层级的关系,某1个级别是另外1个级别的父级,在当前的项目中1个维度下只支持1个级别有1个父级和1个子级的关系。例如year这个级别对应的是‘the_year’这1列,它的子级可以是‘quarter’列作为级别,例如在当前的表中‘the_year’这1列只有两个取值:
mysql> select distinct the_year from time_by_day;
+----------+
| the_year |
+----------+
| 1998 |
| 1997 |
+----------+
而‘quarter’列有4个取值:
mysql> select distinct quarter from time_by_day;
+---------+
| quarter |
+---------+
| Q1 |
| Q2 |
| Q3 |
| Q4 |
+---------+
当设定这个级别之间的关系以后,可以在指定year这个级别以后对其履行下钻操作,这样就会计算year=1997下quarter为每个取值时的度量值,因此,级别的设定主要是为了设定对同1个维度不同粒度的聚合方式(依照年、依照月or依照季度)。固然有了这个层级的关系,为上卷和下钻也提供了操作的基础。
在cube的定义中主要的就是维度和度量的定义了,固然在表里面我们可以看到1些以agg开头的表,这些表的创建是为了mondrian履行的时候优化的,在mondrian提供的FoodMart.xml文件中可以看到文件的开头制定了1些聚合表,这些聚合表相当于对1些度量依照不同维度履行的预计算,当履行查询的时候如果匹配可以从聚合表中查询而不用再进行动态的计算,固然这样的与计算对数据更新还是有1定的代价的。
目前基于mondrian的OLAP查询引擎面临的最大问题就是性能的问题,由于大部份的mdx查询操作都是动态得翻译成sql,然后从元
数据库中履行查询操作,这难免会造成性能低下,特别是对不面向响应时间的hive
数据库,查询速度那是相当的慢,目前的优化方式大体上是履行预计算的方式,在创建cube的时候预先将全部cube或可以支持全部cube的所有度量值都计算出来,查询的时候从预计算的结果中查询或再进行聚合,由于预计算的结果可以寄存在内存或性能更高的key-value
数据库中,这类方式比直接从元
数据库查找的性能会有1定的提升,但是当数据量比较大的时候预计算的结果可能会非常大,如果节省缓存的空间也是面临的1个大问题。