大数据下的Distinct Count(二):Bitmap篇
在前一篇中介绍了使用API做Distinct Count,但是精确计算的API都较慢,那有没有能更快的优化解决方案呢?
1. Bitmap介绍
《编程珠玑》上是这样介绍bitmap的:
Bitmap是一个十分有用的数据结构。所谓的Bitmap就是用一个bit位来标记某个元素对应的Value,而Key即是该元素。由于采用了Bit为单位来存储数据,因此在内存占用方面,可以大大节省。
简而言之——用一个bit(0或1)表示某元素是否出现过,其在bitmap的位置对应于其index。《编程珠玑》给出了一个用bitmap做排序的例子:
/* Copyright (C) 1999 Lucent Technologies */
/* From 'Programming Pearls' by Jon Bentley */
/* bitsort.c -- bitmap sort from Column 1
* Sort distinct integers in the range [0..N-1]
*/
#include <stdio.h>
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N / BITSPERWORD];
void set(int i) { a[i >> SHIFT] |= (1 << (i & MASK)); }
void clr(int i) { a[i >> SHIFT] &= ~(1 << (i & MASK)); }
int test(int i) { return a[i >> SHIFT] & (1 << (i & MASK)); }
int main() {
int i;
for (i = 0; i < N; i++)
clr(i);
/* Replace above 2 lines with below 3 for word-parallel init
int top = 1 + N/BITSPERWORD;
for (i = 0; i < top; i++)
a[i] = 0;
*/
while (scanf("%d", &i) != EOF)
set(i);
for (i = 0; i < N; i++)
if (test(i))
printf("%d\n", i);
return 0;
}
上面代码中,用int的数组存储bitmap,对于每一个待排序的int数,其对应的index为其int值。
2. Distinct Count优化
index生成
为了使用bitmap做Distinct Count,首先需得到每个用户(uid)对应(在bitmap中)的index。有两种办法可以得到从1开始编号index表(与uid一一对应):
- hash,但是要找到无碰撞且hash值均匀分布[1, +∞)区间的hash函数是非常困难的;
- 维护一张uid与index之间的映射表,并增量更新
比较两种方法,第二种方法更为简单可行。
UV计算
在index生成完成后,RDD[(uid, V)]与RDD[(uid, index)]join得到index化的RDD。bitmap的开源实现有EWAH,采用RLE(Run Length Encoding)压缩,很好地解决了存储空间的浪费。Distinct Count计算转变成了求bitmap中1的个数:
// distinct count for rdd(not pair) and the rdd must be sorted in each partition
def distinctCount(rdd: RDD[Int]): Int = {
val bitmap = rdd.aggregate[EWAHCompressedBitmap](new EWAHCompressedBitmap())(
(u: EWAHCompressedBitmap, v: Int) => {
u.set(v)
u
},
(u1: EWAHCompressedBitmap, u2: EWAHCompressedBitmap) => u1.or(u2)
)
bitmap.cardinality()
}
// the tuple_2 is the index
def groupCount[K: ClassTag](rdd: RDD[(K, Int)]): RDD[(K, Int)] = {
val grouped: RDD[(K, EWAHCompressedBitmap)] = rdd.combineByKey[EWAHCompressedBitmap](
(v: Int) => EWAHCompressedBitmap.bitmapOf(v),
(c: EWAHCompressedBitmap, v: Int) => {
c.set(v)
c
},
(c1: EWAHCompressedBitmap, c2: EWAHCompressedBitmap) => c1.or(c2))
grouped.map(t => (t._1, t._2.cardinality()))
}
但是,在上述计算中,由于EWAHCompressedBitmap的set方法要求int值是升序的,也就是说RDD的每一个partition的index应是升序排列:
// sort pair RDD by value
def sortPairRDD[K](rdd: RDD[(K, Int)]): RDD[(K, Int)] = {
rdd.mapPartitions(iter => {
iter.toArray.sortWith((x, y) => x._2.compare(y._2) < 0).iterator
})
}
为了避免排序,可以为每一个uid生成一个bitmap,然后在Distinct Count时将bitmap进行or运算亦可:
rdd.reduceByKey(_ or _)
.mapValues(_._2.cardinality())
3. 参考资料
[1] 周海鹏, Bitmap的秘密.
大数据下的Distinct Count(二):Bitmap篇的更多相关文章
- 大数据下的Distinct Count(一):序
在数据库中,常常会有Distinct Count的操作,比如,查看每一选修课程的人数: select course, count(distinct sid) from stu_table group ...
- mysql优化----大数据下的分页,延迟关联,索引与排序的关系,重复索引与冗余索引,索引碎片与维护
理想的索引,高效的索引建立考虑: :查询频繁度(哪几个字段经常查询就加上索引) :区分度要高 :索引长度要小 : 索引尽量能覆盖常用查询字段(如果把所有的列都加上索引,那么索引就会变得很大) : 索引 ...
- 【阿里云产品公测】大数据下精确快速搜索OpenSearch
[阿里云产品公测]大数据下精确快速搜索OpenSearch 作者:阿里云用户小柒2012 相信做过一两个项目的人都会遇到上级要求做一个类似百度或者谷歌的站内搜索功能.传统的sql查询只能使用like ...
- 软工之词频统计器及基于sketch在大数据下的词频统计设计
目录 摘要 算法关键 红黑树 稳定排序 代码框架 .h文件: .cpp文件 频率统计器的实现 接口设计与实现 接口设计 核心功能词频统计器流程 效果 单元测试 性能分析 性能分析图 问题发现 解决方案 ...
- 大数据下的数据分析平台架构zz
转自http://www.cnblogs.com/end/archive/2012/02/05/2339152.html 随着互联网.移动互联网和物联网的发展,谁也无法否认,我们已经切实地迎来了一个海 ...
- 【CSWS2014 Summer School】大数据下的游戏营销模式革新-邓大付
大数据下的游戏营销模式革新 邓大付博士腾讯专家工程师 Bio:毕业于华中科技大学,现任腾讯IEG运营部数据中心技术副总监,负责腾讯游戏的数据挖掘相关工作,包括有用户画像,推荐系统,基础算法研究等.主要 ...
- 大数据下多流形聚类分析之谱聚类SC
大数据,人人都说大数据:类似于人人都知道黄晓明跟AB结婚一样,那么什么是大数据?对不起,作为一个本科还没毕业的小白实在是无法回答这个问题.我只知道目前研究的是高维,分布在n远远大于2的欧式空间的数据如 ...
- 大数据下基于Tensorflow框架的深度学习示例教程
近几年,信息时代的快速发展产生了海量数据,诞生了无数前沿的大数据技术与应用.在当今大数据时代的产业界,商业决策日益基于数据的分析作出.当数据膨胀到一定规模时,基于机器学习对海量复杂数据的分析更能产生较 ...
- 教你做一个牛逼的DBA(在大数据下)
一.基本概念 大数据量下,搞mysql,以下概念需要先达成一致 1)单库,不多说了,就是一个库 2)分片(sharding),水平拆分,用于解决扩展性问题,按天拆分表 3)复制(replication ...
随机推荐
- Linux下如何自己编译源代码(制作成可以安装的.deb文件)
以tree实用程序(以树型结构获取目录树)为例,介绍Ubuntu中如何管理源码包,包括查询,获取,编译源码包,直至安装. 1) 在获取源码包之前,确保在软件源配置文件/etc/apt/source ...
- HTTP method POST is not supported by this URL
修改提交页面的提交方式,修改method为get(在index页面中html标签 method="get") 示例代码修改后的代码: <form action="s ...
- Android Support Library
title: Android Support Library tags: Support Library,支持库 grammar_cjkRuby: true --- DATE: 2016-5-13. ...
- .Net开发笔记(二十一) 反射在.net中的应用
反射概念在网上到处都有,但是讲到的具体的应用很少,一个重要的原因是现实中真的很少用得到它.引用msdn上对“反射”的解释: "通过 System.Reflection 命名空间中的类以及 S ...
- DDD领域驱动设计之领域服务
1.DDD领域驱动设计实践篇之如何提取模型 2.DDD领域驱动设计之聚合.实体.值对象 3.DDD领域驱动设计之领域基础设施层 什么是领域服务,DDD书中是说,有些类或者方法,放实体A也不好,放实体B ...
- 配置NHibernate将枚举保存为Oracle数据库中的字符串
假设有这样一个枚举: /// <summary> /// 字典项类型 /// </summary> public enum DicItemType { [EnumDescrip ...
- TODO:Golang Linux进程退出说明
TODO:Golang Linux进程退出说明 Golang使用os.Exit(code)进程退出导致当前程序退出并返回给定的状态代码.传统上,code代码为零表示成功退出,非零错误退出. sysca ...
- 《Entity Framework 6 Recipes》中文翻译系列 (42) ------ 第八章 POCO之使用POCO
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第八章 POCO 对象不应该知道如何保存它们,加载它们或者过滤它们.这是软件开发中熟 ...
- Sqoop切分数据的思想概况
Sqoop通过--split-by指定切分的字段,--m设置mapper的数量.通过这两个参数分解生成m个where子句,进行分段查询.因此sqoop的split可以理解为where子句的切分. 第一 ...
- Android笔记——关于Cursor类的介绍
使用过 SQLite数据库的童鞋对 Cursor 应该不陌生,加深自己和大家对Android 中使用 Cursor 的理解. 关于 Cursor 在你理解和使用 Android Cursor 的时候你 ...