alibaba / mdrill

for千亿数据即席分析
https://github.com/alibaba/mdrill
Apache License 2.0
1.54k stars 764 forks source link

一个改进mdrill的group by sum的一个思路 #69

Closed muyannian closed 10 years ago

muyannian commented 10 years ago

1.我的问题描述如下

我这边在开发mdrill的时候遇到一个问题 想咨询下大家,看大家有没有什么思路 .

我们很大一部分需求是对数据进行类似这样的查询
select province,city,sum(amt) from table where thedate>20141002 and thedate <20141230

目前是采用开源的lucene实现的
查询的时候先根据过滤条件fq得到对应的doclist,每个doclist存储了一个文档的ID

然后根据doclist去得到对应的列的值,然后在进行后续的分组和求和计算

通常来说 这个doclist一般非常大(几百万,总表是几亿),因为我们的磁盘在随即读取上性能很差,一百万次docid的查询很恐怖,所以不能从索引中直接得到某个docid对应的值,需要转换为顺序读取。

我处理的时候一般都是 遍历一次倒排表,得到docid对应的列的值,然后缓存在内存里,很明显这样做很不核算,我只需要查几千万条记录,但确遍历了这个几亿级别的倒排表。

我的问题是:
1.索引 因为存在这个倒排表的遍历,在首次查询上,即使一个过滤条件,命中的doclist只有几百个,也会整体遍历一次倒排表,太亏了。
2.如何提升遍历这个倒排表的性能,倒排表的压缩目前采用了PForDelta压缩,在亿级别的遍历上非常慢。

2.讨论后得到的思路如下

倒排表的存储是 term=>doclist,在lucene内部实际上这个doclist也是一个跳跃表,没间隔一定的docid,会记录一个位置(关键帧,而且这个关键帧是有层次的),所以实际上这里的doclist为多个,像这样的term=>doclist1,doclist2,......,doclistN.
lucene在进行查询的时候,多个fq进行过滤的时候,实际上就借助了这个这种doclist跳跃的性质,而不必将全部的doclist都读进来,不然如果有些词对应的文档ID特别多的话,那么fq的过滤也是很影响性能的。

针对这种特性,我们就可以没必要对倒排表进行整体的扫描了,本身我们经过where过滤后会得到一个doclist,我们称为基准doclist.然后对倒排表进行比对的时候实际上是比对 term=>doclist1,doclist2,......,doclistN 。我们仅仅需要读取1,N个跳跃点,从而就可以判断出对应的
term=>doclist1,doclist2,......,doclistN 应该读哪些doclist,比如说我可以仅仅读取doclist2,其他的都不读取。

这种情形对 重复值特别高的列非常有帮助,但是对于那种ID类型的重复值特别低的列没有什么好办法,性能没有改进。

2.lucene跳跃表基础请参考这里 http://blog.csdn.net/forfuture1978/article/details/4976794

muyannian commented 10 years ago

如果有些列的值是null列,也要一下处理,标记为null列

muyannian commented 10 years ago

在一次遍历中,如果找到了全部匹配的doclist,那么可以立即退出。而不是要继续循环下去。

muyannian commented 10 years ago

2014-04-30 12:05:33 RealTimeDirectory [ERROR] deleteDirector java.io.IOException: Cannot delete /disk7/taobao/mdrill/higo/tanx_click/20140429/0/realtime/20140429220727_160_3772555332/_5_new at org.apache.lucene.store.FSDirectory.deleteFile(FSDirectory.java:376) at org.apache.lucene.store.LinkFSDirectory.deleteFile(LinkFSDirectory.java:319) at com.alimama.mdrill.solr.realtime.realtime.RealTimeDirectorUtils.deleteDirector(RealTimeDirectorUtils.java:68) at com.alimama.mdrill.solr.realtime.RealTimeDirectory.expire(RealTimeDirectory.java:193) at com.alimama.mdrill.solr.realtime.RealTimeDirectory.expire(RealTimeDirectory.java:45) at com.alimama.mdrill.adhoc.TimeCacheMap.clean(TimeCacheMap.java:139) at com.alimama.mdrill.adhoc.TimeCacheMap.maybeClean(TimeCacheMap.java:123) at com.alimama.mdrill.adhoc.TimeCacheMap$2.run(TimeCacheMap.java:83) at java.util.TimerThread.mainLoop(Timer.java:512) at java.util.TimerThread.run(Timer.java:462) 2014-04-30 12:05:33 SolrCore [INFO] getSearcher:fact_wirel

muyannian commented 10 years ago

实时部分不稳定,特点 1.首次查询,看不到数据 2.有的时候查询数据有波动

muyannian commented 10 years ago

该改进上线了有一段时间了,但发现对性能的改进并不明显,相反性能还变差了。 总结了下原因,因为要实现跳跃功能,有太多的额外信息要保留(比如说偏移量),而且因为涉及随机读,以及跳跃表放在了doclist的后面,会导致并不是单向的随即读,故性能不核算

新的解决办法,单独一个文件,里面定长的存储每个docid对应的termNum就可以了。不保留原始值,而且termNum根据存储重复程度,可以是byte,short,int采用变长类型存储

muyannian commented 10 years ago

报错

2014-06-15 17:10:17 ShardGroupByTermNumCompare [INFO] ####SortType [sortFieldNum=-1, typeNum=0, typeEnum=index] 2014-06-15 17:10:17 MdrillParseGroupby [INFO] ##baseDocs.size## 543844@543844 2014-06-15 17:10:17 FacetComponent [ERROR] getFacetCounts java.lang.ArrayIndexOutOfBoundsException: 65535 at org.apache.solr.request.BigReUsedBuffer$BlockArray.get(BigReUsedBuffer.java:412) at org.apache.solr.request.uninverted.RamTermNumValue$termDoubleValue_indexl.doc(RamTermNumValue.java:165) at org.apache.solr.request.uninverted.RamDocValue$TermNumReadSingleNotNull.quickToDouble(RamDocValue.java:227) at org.apache.solr.request.uninverted.RamDocValue$TermNumReadSingle.quickToDouble(RamDocValue.java:299) at org.apache.solr.request.uninverted.UnInvertedFieldBase.quickToDouble(UnInvertedFieldBase.java:165) at org.apache.solr.request.mdrill.MdrillParseGroupby$fetchContaioner.updateStat(MdrillParseGroupby.java:243) at org.apache.solr.request.mdrill.MdrillGroupBy.makeTopGroups(MdrillGroupBy.java:105) at org.apache.solr.request.mdrill.MdrillGroupBy.get(MdrillGroupBy.java:71) at org.apache.lucene.index.SegmentReader.invertScan(SegmentReader.java:520) at org.apache.lucene.index.DirectoryReader.invertScan(DirectoryReader.java:591) at org.apache.lucene.index.FilterIndexReader.invertScan(FilterIndexReader.java:320) at org.apache.solr.request.mdrill.FacetComponent.getResult(FacetComponent.java:165) at org.apache.solr.request.mdrill.FacetComponent.process(FacetComponent.java:86) at org.apache.solr.handler.component.SearchHandler.handleRequestBody(SearchHandler.java:101) at org.apache.solr.handler.RequestHandlerBase.handleRequest(RequestHandlerBase.java:129) at org.apache.solr.core.SolrCore.execute(SolrCore.java:1506)

muyannian commented 10 years ago

异常特征 按照某些类别筛选 比如说筛选 辽宁省的,但是筛选的结果 存在各种省的都有。

muyannian commented 10 years ago

已经有新的实现发布了,