用位图索引技术实现超大规模数据量的分组算法

来源:互联网 发布:mac显示硬盘隐藏文件 编辑:程序博客网 时间:2024/05/22 06:30

最近在做一个商业智能软件,也就是我们大家通常说的BI。找了许多资料,关于数据挖掘里面数据立方体的算法到时介绍的很多,但是个人却始终觉得没有什么实际的意义。而我所说的位图索引,也是书中概念的一部分。我把它拿来用来了,但是不是书中所说的一般的方式。个人也觉得挺幸运的,侥幸的知道了位图索引的概念,然后又侥幸的通过位图索引解决了大数据量处理的问题。可能前人已经想到了类似的方法,但是以下说诉,都是个人的思想。

先介绍下位图索引的概念。如果有个二维的销售表,包含以下几个字段:省份、销售员、产品类型、产品。简单的假设表的值如下:

销售省份销售员产品类型产品江苏jack手机iphone江苏jane家电电视机浙江apple手机n97浙江tom家电电冰箱江苏Jerry家电电磁炉位图索引,简单的理解就是用位置去表示值,而且能知道制定位置是否存在值。实际的位图索引是这样子的数据结构:

针对省份里面的“江苏”,因为表里面第0、1、4行是江苏,那么可以就在该行用1表示。具体结果如下:

行号江苏浙江010110201301410上面的表格其实是图的数据结构,可以用图的算法进行压缩存储。但是,前提是:保证索引快速准确。因为图经过压缩后,如果我想知道第n行第m列的位置是0还是1,还要经过算法的转化。如果直接用二维数组保存上诉索引,那么直接能取到制定位置。

现在来介绍下分组的算法:

假设我们要对省份、产品类型两个字段进行分组:

省份里面有两个值:江苏和浙江。先把江苏的索引给取出来,是11001。然后取产品类型的位图索引:手机-10100、家电01011。

我们把江苏-11001和手机-10100比较,同时有1的位置就是既有江苏又有手机的位置,也就是对11001、10100求与运算,得到结果10000,10000不全为0,说明存在这个分组,而且位置是第0行。再将江苏-11001和家电-01011求与得到01001,存在这个分组,而且位置是第1、4行。如果全部为0,说明不存在这个组合的分组。

这样一层次一层次的遍历下去,就会得到一个树形的数据结构。也就是分组的结果。如下:

root---江苏(11001)---手机(10000

      ---家电(01001

       ---浙江(00110)---手机(00100)

    ---家电(00010)

这样分组的好出有一下几点:

1.速度快。主要运算基本都是“与”运算

上诉的分组过程中,字段不同值之间的分组结果其实是不相干的。(我想先算江苏或者先算浙江都一样,而且两者互相直接没有任何关系,因为它们的位图索引其实是互斥的)

不相干带来几个好处:

2.传统的分组算法,必须将关系表遍历几遍,得到最终准确结果,才能准确展示。而位图索引的不相干性,表明了分组完全可以部分计算。比如说:我只计算江苏的分组,一层一层的与下去,当结果数根节点的个数打到了当前页里面的条数,那么就可以break了(后者你后台继续算)。

所以,部分计算、部分展示的特性是其强大的优点。

3.多线程计算。不相干代表了计算结果互不影响,那么多线程计算就是必须添加进去了。可以多线程计算分组、或者汇总

4.过滤(条件分组)很方便。拿过滤条件(过滤条件也可以转换成位图),与树节点中的每个节点求与操作,如果全0,从树中舍弃该节点及其子节点。比如说求条件为“销售员=jack”下的省份、产品类型分组,该条件的位图为:10000,然后去上面的树进行与操作。实际的过滤条件可能较为复杂,需要进过各种“与”、“或”等操作得到。

5.汇总值、平均值等计算方法。只要取根节点里面所存位图的,然后取位图中值为1的位置相应的汇总字段,求汇总即可。也可以加入多线程。

6.排序非常简单。只要对一个节点的子节点进行排序就行。简单的迭代遍历

7.省去许多重复计算。比如存在了“省份、销售员、产品类型”的结果,求“省份、销售员”就直接从前者的树里面截取就得到了。


实际操作中存在几个难点:

位图索引的保存、压缩问题。

我是用了几种方法:

1.用long数组保存。一个long值就是64个位置,那么每个位置都与实际的位置想对应。为何不用int或者short之类的呢。因为long是64位,假如640000行数据,我只要10000个long,意味着位图索引与的时候只要遍历10000次。而用int,就是20000次了。

2.long数组能快速的取,快速的遍历,但是在大数据量的时候,所占的内存空间也很大。比如说1000W行的数据,那么就是1000W/64个long值,大约是1M的内存空间。假如该字段里面的值不是很多,我们还可以接受这种数据结构。假如省份有10000个,那么这样子就不行了,会内存溢出。当然可以将索引保存到本地文件,用的时候再读取。但是这毕竟不是最终解决之道。

3.我用了其他两中方式来保存上述状况,但是这两种还有差别。

第一种:一个有顺序的“1”的位置列表。假如1000W行数据中,省份=“江苏”的位置只有1,10000行这两个位置,那么就是保存的1和10000。这种情况在数字字段里面较多,因为一些随机数字重复出现的概率很低。但是这样数据结构必须用的时候才能生成。原因很简单:1000W个不同的数字,就说明有1000W个不同的位图索引,每个里面的都有一个位置,而这个位置又是一个整数,也就是说明有1000W个整数在内存中。显然不可取。所以呢,快速的生成这种数据结构是个值得研究的,而且这种数据结构与上面的long数组做“与”操作的算法,也必须完美。

第二种:因为long[]数组中肯定存在许多的0,这些0毫无意义,其实只要我们知道这些数据那些位置是0就好。这种就是对0的压缩。

本人上述的两种方式,都能快速知道随机某个位置是否是0(都是是通过二分法),只是比数组的直接索引慢了一点。运算时间还可以接受。

个人认为一个“与”、“或”运算的事件最好不能超过100us。一般客户等个1s就算长的,1s除以100us等于1W,说明最多可以有1W次与操作,也就是1W个树节点,而1W个节点是大数据量展示一页显示差不多的量。


位图运算的难点根本在于位图的数据结构和计算方法。

本来想用B+树实现压缩的,后来发现效率有点低(虽然压缩度最大),就放弃了。可能是我掌握的不太好的原因吧,有机会肯定要重新试一下。

说了这么多废话的,本来想放源码的,但是因为商业机密(即使这些代码都是本人写的),不得不、、、

感兴趣的、有不明白的可以私信M我


原创粉丝点击