海量数据处理的套路——大事化小

为什么会有这么个东西

    所谓海量数据处理,无非就是基于海量数据上的存储、处理、操作。何谓海量,就是数据量太大,所以导致要么是无法在较短时间内迅速解决,并且由于数据量太大,导致无法一次性装入内存。

    那解决办法呢?针对时间,我们可以采用巧妙的算法搭配合适的数据结构,如Bloom filter/Hash/bit-map/堆/数据库或倒排索引/trie树,而针对空间,无非就一个办法:大而化小,分而治之(hash映射),你不是说规模太大嘛,那简单啊,就把规模大化为规模小的,各个击破,最后再把结果汇总,不就完了嘛。
    至于所谓的单机及集群问题,通俗点来讲,单机就是处理装载数据的机器有限(只要考虑cpu,内存,硬盘的数据交互),而集群,机器有多台,只要协调得好,便可以做到人多力量大,便变成了分布式处理,并行计算(更多考虑节点和节点间的数据交互)。

这个东西是怎样工作的

拆分:作为一个海量数据集,它是无法全部直接从硬盘中加载进内存,然后再基于内存进行处理的。所以只能拆分成一部分一部分,达到能加载进内存处理的等级。这里主要要解决的问题就是合理地拆分,以期做到高内存使用率,并且少做磁盘IO。

计算:把拆分好的每一部分数据加载进内存中,并能够用最快的速度处理完这批数据,然后把中间结果以合理的方式存储回硬盘中。这里主要要解决的问题是选用合适的算法再搭配合适的数据结构来完成一个数据的处理,并且输出一个有着易于合并的数据机构的中间结果到磁盘中。

合并:将计算出来的中间结果以最快的方式合并成最终的结果。这里主要的问题就是处理好各个中间结果之间的一个关联关系,然后在保证数据正确的前提下合并出最总的计算结果。

拆分的套路

取模

取模应该是拆分中最常见的方式了。对于数据来说,往往会存在一个key-value的一个范式,例如数据在数据集中的行数为key,改行数据为value;再比如这行数据的前四个字节为key,剩下的字节为value;再比如这条数据自身就是一个key,这是可以说数据自身也是value,也可以说这是只有key的数据。而我们处理数据也往往有一个范式,就是一般情况下都是在对同类型的数据进行处理。所以在拆分数据时,我们便可以通过直接对key进行取模分类,以期让尽量多同类型的数据能够分在同一批,以期能让他们能够在处理的时候一起处理。

hash取模

对于简单的取模,容易使拆分出来的数据集不均匀,例如数据集中的数据取模后都为1,这时候就完全没有起到减小数据集大小的作用了。这时候,就可以考虑先对key进行一次hash,然后再通过对hash取余的方式确认该数据所在的拆分组。

数学分析拆分

对于hash取模也无法很好地完成拆分,或者拆分后仍然无法很好地处理的数据,这时候就要分析key的数学含义了,例如key是一个整形,那我们就可以根据其数据含义以及对应的需求来进行数学上的拆分

问题实例:2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。

思路一:这个例子比上面那个更明显。首先我们将int划分为2^16个区域,然后读取数据统计落到各个区域里的数的个数,之后我们根据统计结果就可以判断中位数落到那个区域,同时知道这个区域中的第几大数刚好是中位数。然后第二次扫描我们只统计落在这个区域中的那些数就可以了。
实际上,如果不是int是int64,我们可以经过3次这样的划分即可降低到可以接受的程度。即可以先将int64分成2^24个区域,然后确定区域的第几大数,在将该区域分成2^20个子区域,然后确定是子区域的第几大数,然后子区域里的数的个数只有2^20,就可以直接利用direct addr table进行统计了。
思路二:同样需要做两遍统计,如果数据存在硬盘上,就需要读取2次。
方法同基数排序有些像,开一个大小为65536的Int数组,第一遍读取,统计Int32的高16位的情况,也就是0-65535,都算作0,65536 - 131071都算作1。就相当于用该数除以65536。Int32 除以 65536的结果不会超过65536种情况,因此开一个长度为65536的数组计数就可以。每读取一个数,数组中对应的计数+1,考虑有负数的情况,需要将结果加32768后,记录在相应的数组内。
第一遍统计之后,遍历数组,逐个累加统计,看中位数处于哪个区间,比如处于区间k,那么0- k-1的区间里数字的数量sum应该<n/2(2.5亿)。而k+1 - 65535的计数和也<n/2,第二遍统计同上面的方法类似,但这次只统计处于区间k的情况,也就是说(x / 65536) + 32768 = k。统计只统计低16位的情况。并且利用刚才统计的sum,比如sum = 2.49亿,那么现在就是要在低16位里面找100万个数(2.5亿-2.49亿)。这次计数之后,再统计一下,看中位数所处的区间,最后将高位和低位组合一下就是结果了。

计算的套路

hashmap统计

将key直接放进hashmap中,将计算结果作为value,起到一个对相同key中间计算结果的快速索引与编辑的作用。例如统计key出现的个数,就可以在hashmap中维护出现的key-value(出现次数)的一个关系。

Bitmap

使用hashmap,理论上来说能够解决大部分问题,但是有些问题是并不需要用到hashmap的,例如只是判断数据是否存在的一个问题,那便只需要维护一个true以及false便可;例如是判断数据出现次数为0,1,多次,则只需要每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行。这时候便可以使用一个大bitmap数组,由于bit是位,比hashmap所需占用的内存少多了,往往只需要很小的内存空间便能表示多个key的计算结果,所以有时候甚至数据都不需要拆分就可以直接用bitmap来处理。

Bloom filter

Bloom filter将集合中的元素映射到位数组中,用k(k为哈希函数个数)(使用k个哈希函数而不是一个是为了让两条不同数据在一个hash函数冲突时另一个hash函数可能不会产生冲突的特性)个映射位是否全1表示元素在不在这个集合中。在判断不在时准确率可以达到100%,但在判断存在时可能会存在误判,而误判率跟哈希函数个数,数据集大小,以及bloom数组的位数有关

Trie树

字典树,又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

各种排序算法(堆/快速/归并排序)

如果需要达到的效果是排序的话,便可以先将每一分块都先排序,实现各分块内有序,最后再对个有序分块进行合并。

数据库索引

适用范围:大数据量的增删改查
基本原理及要点:利用数据的设计实现方法,对海量数据的增删改查进行处理。

倒排索引

适用范围:搜索引擎,关键字查询
  基本原理及要点:为何叫倒排索引?一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。
 以英文为例,下面是要被索引的文本:
    T0 = "it is what it is"
    T1 = "what is it"
    T2 = "it is a banana"
    我们就能得到下面的反向文件索引:
    "a":      {2}
    "banana": {2}
    "is":     {0, 1, 2}
    "it":     {0, 1, 2}
    "what":   {0, 1}
 检索的条件"what","is"和"it"将对应集合的交集。

  正向索引开发出来用来存储每个文档的单词的列表。正向索引的查询往往满足每个文档有序频繁的全文查询和每个单词在校验文档中的验证这样的查询。在正向索引中,文档占据了中心的位置,每个文档指向了一个它所包含的索引项的序列。也就是说文档指向了它包含的那些单词,而反向索引则是单词指向了包含它的文档,很容易看到这个反向的关系。
  扩展:
  问题实例:文档检索系统,查询那些文件包含了某单词,比如常见的学术论文的关键字搜索。

合并的套路

原计算方式的合并

合并的时候只需要使用与计算时一样的套路时,由于这时候进过计算后的数据往往已经比比较小量级了,有时候已经可以直接读进内存中进行合并处理。如果不行,那便再拆分,再合并,以期有一个更小的中间结果。或者通过其他方式,例如修改计算的方式等等来进行合并

中间数据的再处理

有时候,中间计算出来的结果与我们最终要的答案还没有匹配。例如排序中只是分块内有序而已,这时候就需要重新再对所有的分块进行一个归并排序才能得出最终结果。ps(归并排序可用失败树或者置换-选择排序以及最佳归并树来进一步优化)

套路的验证

以后有时间并且有深入理解后再做

 

参考

https://blog.csdn.net/v_july_v/article/details/7382693

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值