万亿级日志与行为数据存储查询技术剖析(续)——Tindex是改造的lucene和druid

摘要:
5、 Tindex Digital Intelligence基于开源解决方案开发了一套数据存储解决方案,通过扩展Druid实现了数据查询和索引编写框架。它不仅保证了数据的实时性和指标的自由定义,而且与原始数据相比在一定程度上扩展了索引后的数据容量。它可以确保以较少的容量支持高效的查询。Tindex压缩数据的存储容量仅为原始数据的1/5左右。2.列反转和正向索引的存储在实际使用中。

五、Tindex

数果智能根据开源的方案自研了一套数据存储的解决方案,该方案的索引层通过改造Lucene实现,数据查询和索引写入框架通过扩展Druid实现。既保证了数据的实时性和指标自由定义的问题,又能满足大数据量秒级查询的需求,系统架构如下图,基本实现了文章开头提出的几个目标。

(点击放大图像)

万亿级日志与行为数据存储查询技术剖析(续)——Tindex是改造的lucene和druid第1张

Tindex主要涉及的几个组件

Tindex-Segment,负责文件存储格式,包括数据的索引和存储,查询优化,以及段内数据搜索与实时聚合等。Tindex是基于Lucene的思想重构实现的,由于Lucene索引内容过于复杂,但是其索引的性能在开源方案中比较完善,在数据的压缩和性能之间做了很好的平衡。我们通过改造,主要保留了其必要的索引信息,比原有的Lucene节省了更多的存储空间,同时也加快了查询速度。主要改进有以下几点:

1、高效压缩存储格式

对于海量行为数据的存储来说,存储容量无疑是一个不容忽视的问题。对于使用索引的方案来说,索引后的数据容量通常相对原有数据会有一定程度的膨胀。针对这类情况,Tindex针对索引的不同部分,分别使用了不同形式的压缩技术,保障了能够支持高效查询的同时仅仅需要较少的容量。对于数据内容部分,使用字典的方式编码存储,每条记录仅仅存储文档编号。对于字典本身的存储,使用了前缀压缩的方式,从而降低高基数维度的空间消耗。实际情况下,使用 Tindex 压缩后的数据占用的存储容量仅仅为原始数据的1/5左右。

2、列式倒排和正向索引的存储

由于实际使用中,往往需要同时支持搜索和聚合两种场景,而这两种方式对于索引结构的需求是完全相反的。针对这两种情况,Tindex结合了倒排索引和列正向索引这两种不同类型的索引。对于倒排索引部分,使用字典和跳表等技术,实现了数据的快速检索,而对于正向部分,则通过高效的压缩技术,实现了对于海量行下指定列的快速读取。同时,根据不同的情况,可以选择性的只建立其中一种索引(默认情况对于每一列均会同时建两种索引),从而节省大约一般的存储空间和索引时间。

Tindex-Druid,负责分布式查询引擎、指标定义引擎、数据的实时导入、实时数据和元数据管理以及数据缓存。之所以选择Druid是因为我们发现其框架扩展性、查询引擎设计的非常好,很多性能细节都考虑在内。例如:

  • 堆外内存的复用,避免GC问题;
  • 根据查询数据的粒度,以Sequence的方式构建小批量的数据,内存利用率更高;
  • 查询有bySegment级别的缓存,可以做到大范围固定模式的查询;
  • 多种query,最大化提升查询性能,例如topN、timeSeries等查询等等。

框架可灵活的扩展,也是我们考虑的一个很重要的元素,在我们重写了索引后,Druid社区针对高基数维度的查询上线了groupByV2,我们很快就完成了groupByV2也可见其框架非常灵活。

在我们看来,Druid的查询引擎很强大,但是索引层还是针对OLAP查询的场景,这就是我们选择Druid框架进行索引扩展的根本原因。 另外其充分考虑分布式的稳定性,HA策略,针对不同的机器设备情况和应用场景,灵活的配置最大化利用硬件性能来满足场景需要也是我们所看重的。

在开源的Druid版本上自研,继承了Druid所有优点的同时,对查询部分代码全部重新实现,从而在以下几个方面做了较大改进:

1、去掉指标预聚合,指标可以在查询时自由定义:

对于数据接入来说,不必区分维度和指标,只需要定义数据类型即可,数据使用原始数据的方式进行存储。当需要聚合时,在查询时定义指标即可。假设我们要接入一条包含数字的数据,我们现在只需要定义一个float类型的普通维度。

2、支持多种类型:

不同于原生的Druid只支持string类型维度的情况,我们改进后的版本可以支持string, int, long, float、时间等多种维度类型。在原生的Druid中,如果我们需要一个数值型的维度,那么我们只能通过string来实现,这样会带来一个很大的问题,即基于范围的过滤不能利用有序的倒排表,只能通过逐个比较来实现(因为我们不能把字符串大小当成数值大小,这样会导致这样的结果‘12’ < ’2’),从而性能会非常差,因为数值类型维度很容易出现高基维。对于改进后的版本,这样的问题就简单多了,将维度定义为对应的类型即可。

3、实现数据动态加载:

原有的Druid线上的数据,需要在启动时,全部加载才可以提供查询服务。我们通过改造,实现了LRU策略,启动的时候只需要加载段的元数据信息和少量的段信息即可。一方面提升了服务的启动时间,另外一方面,由于索引文件的读取基本都是MMap,当有大量数据段需要加载,在内存不足的情况,会直接使用磁盘swap Cache换页,严重影响查询性能。数据动态加载的很好的避免了使用磁盘swap Cache换页,查询都尽量使用内存,可以通过配置,最大限度的通过硬件环境提供最好的查询环境。

HDFS,大数据发展这么多年,HDFS已经成为PB级、ZB级甚至更多数据的分布式存储标准,很成熟了,所以数果也选用HDFS,不必重新造轮子。Tindex与HDFS可以完美结合,可以作为一个高压缩、自带索引的文件格式,兼容Hive,Spark的所有操作。

Kafka/MetaQ,消息队列,目前Tindex支持kafka、MetaQ等消息队列,由于Tindex对外扩展接口都是基于SPI机制实现,所以如有需要也可以扩展支持更多的消息队列。

Ecosystem Tools,负责Tindex的生态工具支持,目前主要支持Spark、Hive,计划扩展支持Impala、Drill等大数据查询引擎。

支持冷数据下线,通过离线方式(spark/Hive)查询,对于时序数据库普遍存在的一个问题是,对于失去时效性的数据,我们往往不希望它们继续占据宝贵的查询资源。然后我们往往需要在某些时候对他们查询。对于Tindex而言,可以通过将超过一定时间的数据定义为冷数据,这样对应的索引数据会从查询节点下线。当我们需要再次查询时,只需要调用对应的离线接口进行查询即可。

SQL Engine,负责SQL语义转换及表达式定义。

Zookeeper,负责集群状态管理。

未来还会持续优化改造后的Lucene索引,来得到更高的查询性能。优化指标聚合方式,包括:小批量的处理数据,充分利用CPU向量化并行计算的能力;利用code compile避免聚合虚函数频繁调用;与大数据生态对接的持续完善等等。

后续笔者还会深入讲解每一部分的详细实现原理及实践经验,敬请关注!如有凝问,可以加笔者微信happyjim2010,一起交流!

作者简介

王劲,数果智能,创始人&CEO。
曾任酷狗音乐大数据技术负责人、大数据架构师,负责酷狗大数据技术规划、建设、应用。

免责声明:文章转载自《万亿级日志与行为数据存储查询技术剖析(续)——Tindex是改造的lucene和druid》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇oracle11g数据库导入、导出操作双行表头DatagridView的简单实现下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Oracle 分析函数(转载)

  实际应用我们在做项目中常遇到类似这样的统计需求 , 列出一些数据列表,最后来一个合计的功能,类似如下:              姓名 工资 SMITH 800 ALLEN 1600 WARD 1250 JONES 2975 MARTIN 1250 BLAKE 2850 CLARK 2450 SCOTT 3000...

matlab中的曲线拟合与插值

曲线拟合与插值在大量的应用领域中,人们经常面临用一个解析函数描述数据(通常是测量值)的任务。对这个问题有两种方法。在插值法里,数据假定是正确的,要求以某种方法描述数据点之间所发生的情况。这种方法在下一节讨论。这里讨论的方法是曲线拟合或回归。人们设法找出某条光滑曲线,它最佳地拟合数据,但不必要经过任何数据点。图11.1说明了这两种方法。标有'o'的是数据点;...

HTTP协议---HTTP请求中的常用请求字段和HTTP的响应状态码及响应头

基本 HTTP 协议 打开浏览器,输入服务器 IP,例如 http://192.168.0.3,如果端口号不80,例如是 8000,则输入 http://192.168.0.3:8000 。这时浏览器向服务器发送的 HTTP 协议头如下: GET / HTTP/1.1 Host: 192.168.0.3:8000 User-Agent: Mozilla/5...

抓取js动态生成的数据案列

本文博客采摘https://www.cnblogs.com/bobo-zhang/p/10561617.html?tdsourcetag=s_pcqq_aiomsg谨防转载 需求:爬取https://www.xuexi.cn/f997e76a890b0e5a053c57b19f468436/018d244441062d8916dd472a4c6a0a0b....

pl/sql下DBMS_OUTPUT.PUT_LINE的输出位置

项目里存储过程中用到DBMS_OUTPUT.PUT_LINE进行输出日志,一开始不知道在哪里看,网上很多都是直接运行后的位置。但是储过程中的日志找了好一会,记录一下。 1、运行时输出位置。 declare in_interval_start_id varchar2(40); in_interval_end_id varchar2...

as3.0 如何把变量转成XML子节点方法(elements)

游戏新加了一批特殊建筑物,为了给建筑添加浮动信息,由于建筑物太多,不想一个个添加浮动信息,也为了方便以后修改,我把数据配置到外部XML表里, 节点如下 <build_1083 funName="晶石提炼场(1083)" needRank="29"  gotoUrl=""/><!--晶石提炼场(1083) --><build_1...