海量数据处理二:方法总结

去年5月份总结的一个海量数据处理方法,不成熟,主要是总结给自己看的........
参考了很多的资料,加上自己的简单理解分析,还有些问题自己本身都不是特别明白......
 
1、大数据量的主要面临什么问题?
(1)无法在短时间内迅速解决 —— 时间
(2)无法一次性装入内存 —— 空间
 
2、如何解决上述问题?
(1)时间:算法+数据结构
(2)空间:分而治之
 
3、常用的方法:
(1)分而治之/hash映射+hash统计+堆/快速/归并排序
(2)双层桶划分
(3)Bloom filter/Bitmap
(4)Trie树/数据库/倒排索引
(5)外排序
(6)分布式处理之Hadoop/Mapreduce
 
-------------------------分而治之/hash映射+hash统计+堆/快速/归并排序-------------------
 
问题:
(1)求前10——有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词?
     a、内存是否能放下?不能放下分而治之、能放下统一处理(注意数据具有重复性,如果去除这些重复看是否能放下)
     b、若划分,hash取模分为N份——对每一份hash_map(构造hashtable)进行频率统计——对每一份堆排序求前10——文件多路归并取前10
 
(2)按频度排序——有10个文件,每个文件1G,每个文件的每一行都存放的是用户的query,每个文件的query都可能重复。要你按照query的频度排序?
 
(3)a、b两个文件,找出共同的数据
     a、内存是否放得下?不能分而治之,能放下统一处理
     b、假设分为1000份,hash映射模1000——分别比较对应的小文件(hash_set:把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了)
 
(4)去除文件中重复的数据
 
处理步骤(以统计query频率为例):
(1)hash映射——O(N)
     a、对每个Query模1000(分到1000个小文件当中)
(2)hash统计(HashTable,时间复杂度O(N)):
     a、维护一个key为query字串,value为query出现次数的HashTable
     b、如果该字串不在Table中,那么加入该字串,并将value值设为1
     c、如果该字串在Table中,将该字串对应的value加1
 
Hash:将一种任意长度的消息压缩到某一固定长度的消息摘要的函数
 
Hash表特点:
     数组的特点:寻址容易,插入、删除困难
     链表的特点:寻址困到,插入、删除容易
     HashTable:一种寻址容易,插入、删除也容易的数据接口
(1) hashtable(key,value)构造方式:
     a、把key通过一个固定的函数(hash函数)转换成一个整型数字
     b、将该数字对数组长度进行取余,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里
(2)查询:
     a、使用hash函数将key转换为对应的数组下标
     b、通过数组下标定位到数组空间获取value
 
hash函数:元素特征转变为数组下标的方法:
(1)除法散列法——取模——index=value%16
(2)平方散列法——  index = (value * value) >> 28(右移,除以2^28)
(3)斐波那契散列法——针对平方散列法,找到一个理想的乘数,而非拿value当乘数:
          16位整数:40503 
          32位整数:2654435769            index = (value * 2654435769) >> 28
          64位整数:11400714819323198485
 
3、堆排序(时间复杂度NlogK)——求前K个数据
     (1)维护一个K大小的堆(求最大用最小堆,求最小用最大堆)
     (2)复杂度计算:建堆——O(K)、调整堆——O(logk)、遍历新的元素X,若入堆,调整——O(logk)
             总复杂度为:O(k*logk+(n-k)*logk)=O(n*logk)
4、快速排序
5、归并排序
 
----------------------------------多层划分----------------------------------------------
 
适用范围:
第K大、中位数、不重复或重复的数字
思想:分而治之——因为元素范围很大,不能利用直接寻址表,所以通过多次划分,逐步确定范围,然后最后在一个可以接受的范围内进行
 
问题:
(1)求中位数
     将数据划分为n个区域,统计落到各个区域里的个数,之后通过统计结果就可以判断中位数落到哪个区域,同时知道该区域的第几个数是中位数
 
个人理解与hash的区别:
     hash取模划分区域是按余数的值进行区域划分,使得各个分区不是有序的,而此处划分的n个区域可以保证,n1的区域>n2的区域....(或者小于),这样就可以很容易找到中位数所在的区域
 
------------------------------------Bloom filter/Bitmap------------------------------------------
 
Bloom fliter(布隆过滤器):Bloom filter是一个二进制向量数据结构,用来检测一个元素是不是集合中的一员。
Bloom filter原理:——错误率换空间——通过允许少量的错误来节省大量的存储空间
     加入集合:当一个元素被加入集合时,通过K个Hash函数将这个元素映射成一个位数组中的K个点,把这些点置为1;
     检索元素:当检测一个元素是否存在这个集合当中时,通过上述K个Hash函数将其映射成K个点,如果这些点有任何一个0,则一定不存在,否则,很可能存在。
优点:空间效率很高
缺点:有可能把不属于这个集合的元素误认为属于这个集合(false positive),不适合那些“零错误”的应用场合
个数问题:元素个数n、位数组大小m、hash个数k
错误率最小的情况hash函数个数:k=(ln2)*(m/n)
错误率不大于E的情况下,m至少要等于n*lg(1/E)才能表示任意n个元素的集合
为了保证bit数组里至少一半为0,则要求m>nlg(1/E)——m=1.44*nlg(1/E)(lg是以2为底的对数)
 
Bloom filter适用范围:数据的判重、集合求交集
 
问题:
(1)求两个文件共同的URL(同hash映射的问题3)——
        给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL。如果是三个乃至n个文件呢?
        内存4G=2*2*1024*1024*1024*8bit 大概是340bit
        将一个文件中的url映射为这340亿bit,然后挨个读取另一个文件,检查是否存在(会有一定的错误率)          
        注:如果允许0.001的错误率情况:n=50亿——>m=1.44*nlg(1/E) 大概是13*50=650亿个bit,需要的是650亿个bit内存(例子都是给的0.01的错误率,我觉得是0.001)
 
Bit-map:用一个bit位来标记某个元素对应的值,而这个bit位的key即该元素本身,使用bit数组来表示某些元素是否存在(略拗口,举例理解)
     要对0-7内的5个元素进行排序(4,7,2,5,3),则需要8个位,开辟1B(1个字节)的空间
     a、构造一个长度为8的bit数组:
          
0 0 0 0 0 0 0 0
     b、首先是4,则将4对应的位置置为1,即数组的第5位(从0开始):
0 0 0 0 1 0 0 0
     c、以此类推,最后的结果为:
0 0 1 1 1 1 0 1
     d、将值为1的各位对应的编号(key)输出:2,3,4,5,7
 
问题:
(1)去重——已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数?
     8位数字最大是99 999 999,也就是说数字的范围是0-99999999,号码最多的情况是每个数字都存在,则一共99 999 999个,需要这么多个bit位(这样去理解,约为99M个bit==1.2MB的空间,仅需要1.2MB的空间是不是很厉害!!!)
    a、构造一个长度为99 999 999的数组
    b、对每个号码进行处理,如果其对应的bit位的值为0,则将其对应的bit位置为1;如果为1,则不进行处理
    c、处理完之后,各个值为1的位对应的编号输出
 
(2)判重——在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数?
     2.5亿个整数,需要2.5亿个bit位,250 000 000个bit,除8大概为30M的内存,肯定放的下了(例题算出1G了,肯定有问题)
     采用2-bitmap方法,即为每个数分配2个bit,00表示不存在,01表示存在1次,10表示存在多次,11无意义——需要大概60M的内存
     a、构造一个长度为500 000 000的数组
     b、对每个数字进行扫描,查看bitmap的对应为,00置为01,01置为10,10则不变
     c、处理完之后,将对应为是01的整数输出即可
 
ps. 感觉各个博客的内存都是计算有误的,不管,知道咋回事就行了
 
(3)判断元素是否存在——给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?
     a、构造数组
     b、扫描这个40亿个数,将对应为置为1
     c、查看所给的数对应的位是否为1,是的话就存在,不是就不存在
 
(4)求两个文件中相同的数据(自己理解)
      a、构造数组
      b、先扫描一个文件中的数据,将对应位的值置为1
      c、再扫描另一个文件中的数据,如果对应位的值为1,则输出
 
(5)排序(个人理解):对于排序有一个特别重要的要求,就是数据不存在重复,如果有重复的话,没有办法标记是否重复以及重复的个数,即使用2-bitmap,最多也只能记录三次重复
 
注:bloom fliter是对Bit-map的扩展,个人理解,如果对于纯数字Bit-map足以应付,假如上述url问题如果url为ip地址,完全可以用Bit-map,可是如果不是纯数字,就得hash处理了
      如果不是纯数字,带有字母和特殊符号的url,用bit-map的方法,每个位代表一个元素,很可能也放不下啊,而且很复杂
话说:bloom filter到底是怎么hash处理的啊??
 
--------------------------------------------Trie树/数据库/倒排索引-------------------------------
 
Trie树:字典树(又称查找树或键树,是一种哈希树的变种)
 
Trie树原理:利用字符串的公共前缀来降低查询时间的开销——空间换时间
(1)根节点不包含字符,除根节点外每一个节点都只包含一个字符
(2)从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串
(3)每个节点的所有子节点包含的字符都不相同
 
实例如下:对于每一个节点,从根遍历到他的过程就是一个单词,如果这个节点被标记为红色,就表示这个单词存在,否则不存在

适用范围:
数据量大,重复多,但是数据种类小可以放入内存
 
问题:
(1)前缀查询——已知n个由小写字母构成的平均长度为10的单词,判断其中是否存在某个串为另一个串的前缀子串?
     法1:最容易想到的:即从字符串集中从头往后搜,看每个字符串是否为字符串集中某个字符串的前缀,复杂度为O(n^2)
     法2:使用hash:我们用hash存下所有字符串的所有的前缀子串,建立存有子串hash的复杂度为O(n*len),而查询的复杂度为O(n)* O(1)= O(n)
     法3:使用Trie:建立trie的复杂度为O(n*len),而建立+查询在trie中是可以同时执行的,建立的过程也就可以成为查询的过程,hash就不能实现这个功能,所以总的复杂度为O(n*len)。实际查询的复杂度也只是O(len)。(说白了,就是Trie树的平均高度h为len,所以Trie树的查询复杂度为O(h)=O(len))
 
(2)统计词频——有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词?
     hash取模分为N份——trie树统计每个词的频率——构造最小堆(流程同第一部分的hash方法,差别在于第二个步骤中的统计词频)
     词频统计(个人理解):对每个词进行处理,首先从根节点开始查询这个词是否存在,不存在就建立这个词对应的节点和边,建立完之后,词频记为1;如果这个词存在,词频+1
     Trie树复杂度(个人理解):假设N个词——建立的过程也是统计的过程(O(N*len))
     hash_map复杂度(个人理解):N个词——建立的过程也可以统计——hashtable(O(N))——对每个词,用has_key判断,无这个词就建立,value设为1,有的话value+1——hash_map是这样实现的吗??如果是的话这种方式更好啊
 
(3)去重——1000万字符串,其中有些是相同的(重复),需要把重复的全部去掉,保留没有重复的字符串。请问怎么设计和实现?
     法1:上述Bloom filter(个人理解):对每一条数据进行hash映射——如果位数组中对应的位存在0,将相应的位的值置为1并且保存这条数据到文件中;都对应的位的值均为1,证明已记录过,继续遍历下一条。时间复杂度(对每一条数据进行K词hash映射K*N,hash映射完成之后查看每个位的值K*N,所以为O(2K*N))
     法2:Trie树(个人理解):对每个字符串进行处理,首先从根节点开始查询这个词是否存在,不存在就建立这个词对应的节点和边,若存在则继续遍历下一条,最后所有的不重复字符串都存在这个Trie树上。时间复杂度(O(N*Len))
     法3:Hash_map——对每个字符串进行处理,若不存在,则加入hashtable,并保存到文件中,若存在,则继续遍历下一条(O(N))
     对比(个人理解):Bloomfilter——占用内存小;Trie树——准确率高;时间复杂度差不多;如果重复率低的话Trie占用内存太多;还是Hash_map好??
 
 
数据库索引:用来定位
 
例如:select * from table1 where id=32; 
        如果没有索引,则要遍历整个表,直到找到id=32这一行;
        如果在id这一列上建立索引,则直接在索引里面查找32(即在id这一列中找)
 
索引分类:聚簇索引——按照数据存放的物理位置为顺序——可以提高多行检索的效率
               非聚簇索引——可以提高单行检索的效率
 
在哪建索引:
(1)经常需要搜索的列上
(2)主键
(3)外键——加快表之间的连接
(4)需要排序的列
................这些自行体会..................
 
适用范围:大数据量的增删改查
 
倒排索引(Inverted index):一种索引方式,索引表中的每一项包括一个属性值和该属性的各记录的地址,不是由记录来确定属性值,而是由属性值来确定记录的位置
 
例如:
     以英文文本为例,以下是要被检索的文本:
     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}
 
适用范围:查询哪些文件包含了某单词
 
对比:
正向索引:文档指向了其所包含的单词
反向索引:单词指向了包含它的文档
 
-------------------------------------------外排序-------------------------------------------
 
外排序:处理的数据不能一次装入内存,只能放在磁盘上
 
归并排序:
     两路归并:归并的有序表有两个——既使用于内排序也适用于外排序
     多路归并:若归并的有序表有K个,则成为K路归并——外排序
 
多路归并:
     时间复杂度:长度为N的文件,需进行lgn次二路归并,每趟归并的时间为O(n),故其复杂度为O(nlgn)     
 
思路:
     前提:数据量太大,不能同时放入内存
     1、内存排序:分为N个文件依次放入内存进行排序,对每个文件的排序不做约束,可以选择快排(时间复杂度低,但是不稳定),可以采用二路归并(稳定)....
     2、多路归并排序:
          a、将每个文件中最开始的数读入(即最小的数),存放在一个大小为N的数组中
          b、选择数组中最小的数写入result文件,同时要获取该数所对应文件的索引index,然后更新数组(用index所标记的文件中的下一个数代替之前输出的数)
          c、判断是否所有数据读取完毕,如果未完成,返回步骤b,否则结束排序
          时间复杂度:数据量个数M、文件N:读入读出O(M)+O(M)——找最小的数O(M)*O(N)
 
------------------------------------------------------------MapReduce-------------------------------------
posted @ 2016-03-07 21:54  李闹闹童鞋  阅读(264)  评论(0编辑  收藏  举报