海量数据处理-Topk引发的思考

海量数据处理–TopK引发的思考

三问海量数据处理:

  1. 什么是海量数据处理,为什么出现这种需求?
  2. 如何进行海量数据处理,常用的方法和技术有什么?
  3. 如今分布式框架已经很成熟了,为什么还用学习海量数据处理的技术?

什么是海量数据处理,为什么出现这种需求?

如今互联网产生的数据量已经达到PB级别,如何在数据量不断增大的情况下,依然保证快速的检索或者更新数据,是我们面临的问题。所谓海量数据处理,是指基于海量数据的存储、处理和操作等。因为数据量太大无法在短时间迅速解决,或者不能一次性读入内存中。

从何解决我们上面提到的两个问题:时间问题和空间问题

时间角度,我们需要设计巧妙的算法配合合适的数据结构来解决;例如常用到的数据结构:哈希、位图、堆、数据库(B树),红黑树、倒排索引、Trie树等。空间角度:我们只需要分而治之即可,两大步操作:分治、合并。MapReduce就是基于这个原理。

如今分布式框架已经很成熟了,为什么还用学习海量数据处理的技术?

这个问题,就相当于为什么要学习算法,因为大部分人在工作中都很少用到这些算法和高级的数据机构。武侠讲究内外兼修才是集大成着。算法就是内功,可以不用,不可以没有。算法更多的是理解和推到的过程,这就是为什么很多学数学和物理专业的学生,可以在计算机行业做的很好,因为他们的数学好,可以很好的理解各种推到过程和算法原理。最后一句话,知其然而后知其所以然,技术更好的成长。

这篇文章,我采用总分的结构进行写作,我们每次都会抛出一个问题,这个问题对应的海量数据处理的一个方面,我们从下面几个角度分析:

  1. 对应海量数据处理的那个技术,以及是时间角度和空间角度
  2. 分析这个问题,如何解决
  3. 提出解决方案,进行分析
  4. 详细讲解这处理这个问题时,用到的技术,例如什么是堆,hash等

抛出问题:寻找热门查询

任何的搜索引擎(百度、Google等)都会将用户的查询记录到日志文件。对于百度这种公司,我们知道每天有很多Query查询,假设有100G的日志文件,只有一台4G内存的电脑,现在让你统计某一天热门查询的Top 100. (Top 100的统计是很有用意义的,可以提供给用户,知道目前什么是最热门的,关注热点,例如金庸老先生的去世,现在应该就是热门。)

分析问题
根据题目的描述,我们有100G日志文件,只有一台4G的电脑,这是空间不足的问题,我们需要分而治之。

  1. 划分文件,将100G日志文件划分成K的小文件。K >= 100G / 4G = 25。这里我们去K=50(为什么不取25呢?),将所有的Query划分到50个小文件中,然后统计每一个小文件中的Query的频率,之后合并结果,得到最后的Top 100的Query。

  2. 需要我们处理的两个点:划分和合并。
    划分:保证相同的Query划分到同一个小文件中。
    统计:统计每个小文件中Query的频率
    合并:如何快速的合并得到结果。
    划分的时候我们需要用到一个叫做hash的技术,两步走,

  3. hash(string) = key_index,

  4. File_index = Key_index % K(50)
    划分:

设计一个hash函数,需要保证的正string -> index的转换,这里不需要考虑冲突,只有保证相同的string对应一个key_index即可,一个好的hash函数会将均匀分布的数据,均匀分布到不同的文件中去。常用的Hash函数和原理

C++代码:

const unsigned int BIG_MOD = 1000003;
inline unsigned int hash_code(std::string&& key) {
    unsigned int value = 0;
    for(int i = 0; i < key.size(); i++) {
        value = value * 31 + (unsigned int) key[i];
        value %= BIG_MOD;
    }
    return value;
}

统计:

这里给出两种方法, hash_map和Trie数。每一个小文件有一个输出结果,这里我们只需要输出前Top 100频率进行。
Hash_map这个原理跟前面的提到的hash函数基本一样,只是需要解决冲突问题,之后会在别的文章中专门提出。C++的结构map,或者Java中Hashmap或者Python中的dict基本使用方式一样。Map[query] += 1.
HashMap的不足在于我们空间使用多,对于查询这种Query,很多的查询都是一样的,我们可以使用Trie树来解救,这是一个前缀树的结果,例如
Querys = {“我爱你”,“爱你们”,“我”,“我”,“我爱你“,”金庸“},构造的树的格式如需:

数据结构:

const int MAXN = 10000;
struct TreeNode {
    string key;
    int cnt;
    int flag;
    struct TreeNode *next[MAXN];
};

具体的Trie树细节,这里就不在继续展开,可以搜索相关文章,后续可能会继续退出Trie树的相关文章。
基于上述两种方法,我们都可以直接得到Top 100的结果。输出文件格式如下:
Query1 Count1



Queryi Counti

合并:

读取所有的结果在内存中,根据Count排序取前100,时间复杂度是O(nlogn),n是所有个数n=50*100. 但是注意到我们只要前k个最大的,我可以使用堆排序,使用一个叫做最小堆的问题。维护k(100)大小的最小堆,每次插入新的元素,去掉最小的元素,时间复杂度O(k + (n-k)logk),比排序小很多。
这里同样可以使用Trie树,和上述的方式一样,注意这可以转化一个取第k个大小的问题,我们也可以使用快速排序中划分函数,进行找到第k个,前面的就是我们需要的目标。

结束:
到这里我们的问题已经可以结束了,但是却有几个问题需要提出来。

  1. 这真的是热门Query统计吗?百度等公司是这么做的吗?相似的Query怎么处理?如何实时的更新热门榜单呢?等等的问题,需要我们思考。
  2. Hash划分的时候,划分文件不平衡怎么办?如何保证平均划分?

注:这里提到的hash,堆和Trie和快排的划分每一个技术,都可以拿出来单独一篇文章,读者可以先查询相关资料,之后我们也会推出相关的文章。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值