分析型数据仓库中读写分离的实现

数据仓库和传统的事务型数据库相比,一个很大的特点就是主要面向批量写和查询进行优化,可以不支持更新、事务这些高级特性。一些商用的数据仓库分析系统,例如Vertica,已经可以做到千亿级数据的秒级导入和秒级查询。本文主要探讨如何利用现有的开源组件实现分析型数据仓库当中的读写分离。

为什么要进行读写分离?

分析性数据仓库一般有下面几个特点:

  1. 面临着复杂的多维分析需求,需要能够进行任意维度的上卷下钻。
  2. 存储的数据维度一般比较多,所以是宽表,而且一般比较稀疏。
  3. 数据量比较大,一次写入,多次查询。

针对这样一些特点,分析性数据库一般选择列存储数据格式,例如Parquet等。优点是对于统计分析效率很高,而且对于稀疏的宽表具有很高的存储压缩比。所以我们可以认为列存储格式是一种面向读进行优化的存储格式,我们称为Read Optimized Store(ROS)。但是列存储格式还有一些缺点,这种格式的数据一旦生成,就很难进行修改,也很难往已有的数据文件当中插入新数据,只能增加新的数据文件。所以我们自然而然的想到,像Mysql这种传统的数据库当中使用的行存储文件格式是一种适合修改和插入的存储格式,我们可以认为这种行存储格式是面向写进行优化的存储格式,我们称为Write Optimized Store(WOS)。
综上所诉,要实现一个可以秒级导入、秒级查询的分析型数据库,如果我们只选用ROS,则很难支持大数据量的秒级导入。如果我们只选用WOS,则很难实现任意维度的秒级查询。所以我们需要进行读写分离。

读写分离的实现原理

数据仓库当中需要同时存在WOS和ROS,这样对于所有的写操作我们都是生成WOS型文件;同时对于所有的读操作,我们主要依赖于ROS文件,但也要查询少量的WOS文件。整体的示意图如下:


分析型数据仓库中读写分离的实现_第1张图片
读写分离的原理图

如图所示,WOS文件需要定期的转换为ROS文件,同时因为ROS在数据仓库当中一般是分为多个partition存在,所以一个WOS可能需要转化为多个ROS。同时转化的过程需要是一个原子操作,因为对上层查询引擎来说,在同一时刻,同样的数据必须只能有一份。

开源方案的选择

上面简单介绍了读写分离方案的原理,在具体的工程实践过程中我们还面临着很多方案的选择和实践难点。下面简单说一下我们的实践选择和碰到的技术难点,以后有时间在单独写一篇介绍实现方案。ROS的选择比较简单,我们已经选择了Parquet + Impala的查询方案,同时结合我们的业务特点做了很多代码级别的优化。WOS的选择可能会比较多,例如我们可以选择常用的HDFS行存储文件格式,例如TextFile、SequenceFile、Avro等。以SequenceFile为例,我们在定义自己的Impala表的时候,可以指定一个特殊的partition指定文件存储格式为SequenceFile,同时其它的partition作为正常的按照日期partition的数据指定格式为Parquet。这种方式会简单一些,因为我们始终只有一个表。但是基于一些查询效率和以后的架构升级方面的考虑,我们最终选择了Kudu作为我们的WOS。架构实现示意图如下:

分析型数据仓库中读写分离的实现_第2张图片
读写分离的实现图

如图所示,我们会建立三张物理表,其中两张Kudu表作为WOS。一张Paquet表作为ROS。所有的写操作都会写入到Ingesting状态的Kudu表中,当Ingesting表写到一定的大小之后,会自动转换为Staging状态。这时我们一方面生成一张新的Kudu表作为Ingesting表,另一方面会开始WOS到ROS的转换。我们有一个叫做Mover的task来执行这个操作,将Staging状态的Kudu表中的数据全部转换到对应partition的Parquet表当中。Staging状态的表转换完成且Ingesting状态的表写满时,会触发一个切表操作,需要更新元数据,告诉Impala使用新的数据进行查询,整个切表的操作是原子的。而且已经转化的Staging表还需要保留一段时间,避免切表之前发起的查询操作没有及时执行完成。对于查询请求来说我们会建立一个包含Staging表、Ingesting表和ROS表的虚拟表,即一个View。用户的查询始终查询始终指向的是一个View,但是下面的物理表会经常发生变化。这样就做到了既能查询到最新的数据,又能够对查询性能进行很好的优化。

在实现的过程中还有很多具体的工作,如果:如何对表进行加列曹组,保证各个表的结构一致;Parquet表中碎文件较多影响查询效率如何定期的合并等。限于篇幅,这里就不再具体介绍了。

写后感

这是在写的第二篇文章,没想到写完第一篇文章之后也成了我们的客户(当然不是因为我_)。现在对也更加亲切了,也我们一起实现数据驱动,哈哈。

你可能感兴趣的:(分析型数据仓库中读写分离的实现)