Lucene On Spark -- 云原生OLAP引擎加速数据分析(一)

一.概述

大数据OLAP引擎根据灵活性、及时性、成本收益比,通常分为如下三种:

A.预聚合查询结果:提前计算好各个维度的汇总数据,常见方案有:kylin,druid等 。

B.即席查询类:实时查询明细数据,常见方案有:clickhouse,doris,TiDB等。

C.离线写入+现场计算:常见方案有:ES,Solr,bitmap on hbase/redis/pgsql等,可附加使用HBase/MongoDB等作为明细级数据存储。

三者转换关系如下:

image

通常在数据运营及数据探索的过程中,需要追求一定的灵活性,较低的硬件成本以及可接受的响应速度,此时通常会选择上述C方案。

考虑到企业数据分析通常不追求高并发和毫秒级响应,而更需要在海量数据的分析灵活性和成本上寻求出路,因此常驻在线服务如ES,Solr等,仍然不是最好的选择。

基于上述场景,本文提出了一种新的解决方案。

image

将Lucene索引文件分成多个partition存储到HDFS,并通过Spark引擎分布式执行方式加速索引的写入和计算,以及处理缓存。

为完成上述方案,要解决几项关键的技术问题:

1)Lucene HDFSIndexWriter和HDFSIndexReader开发。

2)Spark分布式执行各个partition结果,支持筛选,分组,聚合等运算,并对结果集进行Merge。

3)完善的缓存加速机制,解决HDFS随机读性能问题。


二.Lucene On HDFS实现

Solr作为基于Lucene的分布式搜索引擎之一(另一个知名的基于Lucene的搜索引擎产品是ElasticSearch),其曾在SolrCloud源码中实现了基于HDFS的索引,但8.6版本后被标记为Deprecated,9.0版本之后从solr-core中移出,作为单独的模块被放在/solr/modules/hdfs下。

参考如下问题单:

Deprecate HDFS support from 8x

https://issues.apache.org/jira/browse/SOLR-14021 

Migrating HDFS into a module

https://issues.apache.org/jira/browse/SOLR-14660


除上述问题单中讨论的原因外,此处分析一下Solr不适合以HDFS文件系统作为其后端存储系统的其他原因:

1.性能问题:Solr定位为搜索引擎,这种场景通常追求高并发低延时,使用本地文件系统以及固态硬盘通常更合适。

2.配置难度大:无论是Solr,HDFS还是Lucene,都需要一定的技术积累才能完全掌握,Solr在开放其HDFS相关配置时,对配置的说明写得比较模糊,因此导致性能调优难度大。参考:https://solr.apache.org/guide/8_2/running-solr-on-hdfs.html#example-solrconfig-xml-for-hdfs

而OLAP场景下,由于并不追求毫秒级响应,且并发量不高,不会存在上述问题。


对Lucene On HDFS的实现。可参考

solr\modules\hdfs\src\java\org\apache\solr\hdfs\store\HdfsDirectory.java


三.Spark分布式执行


Lucene Index的分布式查询和处理,实际上是一个MapReduce的过程,在map阶段需要查询各个分区的数据得到分区查询结果,在reduce阶段对各个分区的查询结果进行合并,每个分区对应的是ES和Solr中的Shard概念,实际上是Lucene的单个索引目录。

若需要支持100个spark的partition同时计算,则分区数至少应该是100。

Lucene文件大致来讲有三种数据类型:

IndexableField:倒排索引,用于筛选

StoredField:行式存储,用于获得筛选结果集的详情数据

DocValuesField:列式存储,用于对某个字段进行排序,分组或者聚合统计

但实际情况会更为复杂:

是否支持单字段多值(Multivalued),以及不同类型的索引使用不同的类:

如对倒排索引来说:

LongPoint,IntPoint,DoublePoint等是数字类型的倒排索引使用的类。

StringField是字符串类型的倒排索引使用的类。

对行式存储来说:

对于字符串类型,在StringField类中还可以加入Field.Store.YES或Field.Store.NO来指定是否需要行式存储。

但对于数字类型,则需要另外增加一个StoredField来指定是否进行行式存储,另外,字符串类型若不需要倒排索引,仅需要存储,也可以使用StoredField。

对列式存储来说:

需要关注数据类型,是否需要排序,是否需要分组,是否需要聚合,是否支持多值等情况:

如对分组字符串字段,需要使用SortedSetDocValuesFacetField来进行存储,该字段会把值默认存入”$facet”,

值得一提的是facet方法各个引擎有各自不同的实现,lucene自身也有两种实现,一种是创建一个附加索引,另一种则是使用DocValues,一般认为使用DocValues是一种更加通用的方式,而创建附加索引则会有更高的性能,Solr和ES也都是利用DocValues的方案来进行分组的。

posted @ 2022-03-25 18:03  Arli  阅读(356)  评论(0编辑  收藏  举报