Bitmap的秘密

为什么80%的码农都做不了架构师?>>>   hot3.png

之前已经参加过几次QCon峰会,不过今年QCon 2014 上海峰会对我来说比较特别,不再只是一名听众,而是第一次登台演讲。感觉的确不太一样,一来是身份从听众变成了讲师,二来是因为成了讲师,让我接触到更多的业内朋友,也遇到了更多的提问、咨询。会后已经有一段时间了,还有朋友提出想了解更多的技术知识。看来会上行云流水的半个小时,未能把一个技术点讲述明白,我想还是总结一下,让技术圈朋友们对Bitmap这个技术点加深点理解。

一、背景

a) 历史的困惑

每个技术点背后都有一系列业务的故事,透过我这次讲的Bitmap,我们可以看到历史上始终困扰营销领域的一个核心问题。著名广告大师约翰•沃纳梅克提出:我知道我的广告费有一半浪费了,但遗憾的是,我不知道是哪一半被浪费了 (翰•沃纳梅克,始创第一家百货商店“沃纳梅克氏”,被认为是百货商店之父;同时也是第一个投放现代广告的商人)。为什么会出现这样的浪费呢?我个人觉得还是对营销所面向的人群理解不透彻导致的。因此,营销领域一直期待技术系统能够提供一种能力,可以高速、灵活的、从海量用户中,找出最合适的那一部分。

b) 时代的曙光

需求的背后,是技术的不断进步。最近几年,信息技术的突飞猛进,给解决问题带来了一线曙光。大数据处理技术和系统,已经渗透到当今每一个行业和业务职能领域,成为重要的生产因素。人们对于海量数据的挖掘和运用,预示着新一波生产率增长和消费者盈余浪潮的到来。使用各种各样新型的技术武器(Hadoop、Spark、Storm等等开源工具),我们近乎完美的捕捉到这个时代的浪潮:将人群的属性分析、行为分析、甚至是心理分析深入到相当精深的地步。

相关厂商内容

京东618大促下的数据驱动个性化推荐

如何构建软硬件结合的人工智能产品研发体系

中国创新型互联网企业走向海外的技术机遇与挑战

数据分析与企业架构

安全狗,业内首创云+端模式保护服务器安全

相关赞助商

25165829_MOsj.jpg

全球架构师峰会,7月17日-18日,深圳大梅沙京基海湾大酒店。马上报名。

c) 业务驱动力

业务需求对技术的要求是永无止境的。我们需要更好的工具:

  1. 更快速的分析,一次任务就需要个把小时,甚至需要一天,数据的产出,无法支持我们业务的快速调整。
  2. 更灵活的分析工具,应该比Oracle或者IBM的BI系统更好,我们需要有多维交叉的计算能力,而不只是简单的几种统计数字。
  3. 更轻量级的系统,动辄几十台服务器组成的Hadoop集群,我们真是无法负担啊,成本太高昂了。

二、现有技术分析

a) 硬币的两面

我们最初非常仰仗Hadoop,不过后来发现Hadoop不是银弹,核心的问题不是Hadoop出了错,而是我们对Hadoop的本质理解稍显片面。Hadoop是一种高吞吐量系统,其设计和实现中采用了小操作合并,基于操作日志的更新等提高吞吐量的技术。硬币的两面在使用过程中逐步显现:高吞吐量和低延迟是两个矛盾的目标。既然要低延迟,上Storm,上Redis。在2011年,我们在Hadoop的批处理的基础上,为我们的统计平台增加了实时计算能力。下面这张图,大约反映了一种典型的架构系统:如何使用两种流派的计算系统解决计算问题。

25165830_YWMM.png

看似完美的解决了高吞吐量和低延迟的矛盾,但是,核心业务问题没有得到解决:业务人员需要在海量数据中,使用多维交叉的方式,不断计算手里的数据。批量处理还是需要几个小时,甚至一天才能出一批数据。流式处理只能算当前的数据,历史数据无法回朔。

b) 白天鹅:传统技术的缺陷

看待硬币的两面是一种视角,这也许是一种平庸的视角。如果不看硬币,把硬币当做金属,寻找、发现、创造一种新的铸造硬币的方法,又是另外一种视角。2012年,我们跳出了传统的视角(例如Hadoop、或者Storm的改造),重新观察我们的问题。经过分析,我们得到一个结论:我们需要做的,是创造第三种计算方式,而不是继续在两种计算方式中挖潜。

如果我们把传统系统看作白天鹅,并把高速的、灵活的多维交叉分析当作飞行的目标,我们应该看看白天鹅为什么飞得累,飞得慢?

第一个技术障碍是高I/O开销,包括磁盘I/O和网络I/O两种主要开销。传统的数据存储都是以行的方式存储在磁盘文件中,而传统的文件系统又都是为大文件连续读写做优化的。假如我们要做多维交叉分析,我们分析一下系统的运作过程:

  1. 根据查询需求,定位到数据在某些文件。
  2. 从文件系统中提取文件。这里产生了磁盘开销。
  3. 如果是分布式系统(例如普遍使用Hadoop),这里产生了高昂的网络传输开销。
  4. 然后需要按行的方式,从文件中提取数据。这里会碰到一个非常隐蔽的问题,每行数据中会有大量的信息对本次查询无任何意义(可能造成非常巨大且不幸的浪费)。

第二个技术障碍是计算相对低效。传统计算中,把大量的CPU和内存放到了数据装载,数据过滤等等(上面的浪费就是例子)。

新一代的计算体系的设计重点是降低整体IO,并尽量让计算资源用在真正有价值的点上。

c) 从白天鹅到黑天鹅

我们用了三种法宝克服上面的缺陷,以提高整体计算效率:

  1. 预处理:尽量降低低效的文件读取在整个计算过程中的比重。
  2. 压缩:使用高压缩比的压缩算法将需要计算的数据压缩到最小(同时不影响计算精度)。
  3. 内存计算:将计算需要的全量数据全部一次性装载入内存,这样可以最大程度的将CPU的计算能力用于业务计算。

那么到底是哪只黑天鹅将以上的优点集于一身内?它就是Bitmap。

三、Bitmap的秘密

a) Bitmap如何做到多维交叉计算的?

Bit即比特,是目前计算机系统里边数据的最小单位,8个bit即为一个Byte。一个bit的值,或者是0,或者是1;也就是说一个bit能存储的最多信息是2。

Bitmap可以理解为通过一个bit数组来存储特定数据的一种数据结构;由于bit是数据的最小单位,所以这种数据结构往往是非常节省存储空间。比如一个公司有8个员工,现在需要记录公司的考勤记录,传统的方案是记录下每天正常考勤的员工的ID列表,比如2012-01-01:[1,2,3,4,5,6,7,8]。假如员工ID采用byte数据类型,则保存每天的考勤记录需要N个byte,其中N是当天考勤的总人数。另一种方案则是构造一个8bit(01110011)的数组,将这8个员工跟员工号分别映射到这8个位置,如果当天正常考勤了,则将对应的这个位置置为1,否则置为0;这样可以每天采用恒定的1个byte即可保存当天的考勤记录。

综上所述,Bitmap节省大量的存储空间,因此可以被一次性加载到内存中。再看其结构的另一个更重要的特点,它也显现出巨大威力:就是很方便通过位的运算(AND/OR/XOR/NOT),高效的对多个Bitmap数据进行处理,这点很重要,它直接的支持了多维交叉计算能力。比如上边的考勤的例子里,如果想知道哪个员工最近两天都没来,只要将昨天的Bitmap和今天的Bitmap做一个按位的“OR”计算,然后检查那些位置是0,就可以得到最近两天都没来的员工的数据了,比如:

25165838_rk9S.png

再比如,我们想知道哪些男员工没来?我们可以在此结果上再“And”上一个Bitmap就能得到结果。

b) Bitmap如何做到高速运算的?

回忆一下前面,浪费的有两个部分:其一是存储空间的浪费,Bitmap比文件强多了,但是仍然有浪费的嫌疑。它需要保存到外部存储(数据库或者文件),计算时需要从外部存储加载到内存,因此存储的Bitmap越大,需要的外部存储空间就越大;并且计算时I/O的消耗会更大,加载Bitmap的时间也越长。其二是计算资源的浪费,计算时要加载到内存,越大的Bitmap消耗的内存越多;位数越多,计算时消耗的cpu时间也越多。

对于第一种浪费,最直觉的方案就是可以引入一些文件压缩技术,比如gzip/lzo之类的,对存储的Bitmap文件进行压缩,在加载Bitmap的时候再进行解压,这样可以很好的解决存储空间的浪费,以及加载时I/O的消耗;代价则是压缩/解压缩都需要消耗更多的CPU/内存资源;并且文件压缩技术对第二种浪费也无能为力。因此只有系统有足够多空闲的CPU资源而I/O成为瓶颈的情况下,可以考虑引入文件压缩技术。

那么有没有一些技术可以同时解决这两种浪费呢?好消息是有,那就是Bitmap压缩技术;而常见的压缩技术都是基于RLE(Run Length Encoding,详见http://en.wikipedia.org/wiki/Run-length_encoding)。

RLE编码很简单,比较适合有很多连续字符的数据,比如以下边的Bitmap为例:

25165838_hzNn.png

可以编码为0,8,2,11,1,2,3,11

其意思是:第一位为0,连续有8个,接下来是2个1,11个0,1个1,2个0,3个1,最后是11个0(当然此处只是对RLE的基本原理解释,实际应用中的编码并不完全是这样的)。

可以预见,对于一个很大的Bitmap,如果里边的数据分布很稀疏(说明有很多大片连续的0),采用RLE编码后,占用的空间会比原始的Bitmap小很多。

同时引入一些对齐的技术,可以让采用RLE编码的Bitmap不需要进行解压缩,就可以直接进行AND/OR/XOR等各类计算;因此采用这类压缩技术的Bitmap,加载到内存后还是以压缩的方式存在,从而可以保证计算时候的低内存消耗;而采用word(计算机的字长,64位系统就是64bit)对齐等技术又保证了对CPU资源的高效利用。因此采用这类压缩技术的Bitmap,保持了Bitmap数据结构最重要的一个特性,就是高效的针对每个bit的逻辑运算。

常见的压缩技术包括BBC(有专利保护),

WAH(http://code.google.com/p/compressedbitset/)

和EWAH(http://code.google.com/p/javaewah/)。在Apache Hive里边使用了EWAH。

c) Bitmap在大数据计算上的能力?

我们用一个TalkingData Analytics中用户留存的例子来看Bitmap如何做到用户回访的统计。比如想知道某个应用,昨天新增的用户中,有多少人今天又开启了应用(次日留存)。使用过Hive的工程师,不难理解下面语句的含义:

25165839_48Jw.png

同时,我们使用Bitmap技术后,同样实现上述的计算,对比测试显示出效率的差异是巨大的:

25170108_ewLB.png

d) 引入Bitmap技术后,分析系统可能的处理流程大体是什么样的?

  1. 数据收集系统收集设备上传数据,然后分发给实时处理系统和批量处理系统;
  2. 实时系统采用自有计数器程序,或者基于Storm之类中间件的计数器程序,计算各类简单计数器,然后批量(比如30s或者1min)更新到Redis或者HBase之类的存储;前端供应计数器类数据的服务通过访问后台计算器程序或者是计数器存储来给报表系统提供服务;
  3. 批量系统对该批的数据按用户进行去重生成/修改某天/某个应用的活跃用户Bitmap,同时可以根据需要,将机型、地域、操作系统等等各种数据提炼成属性Bitmap,备用。
  4. 报表中针对分析需要,提取各种Bitmap(用户、属性……Bitmap),高效的利用CPU/内存,通过组合And/Or/Not等基础计算,最终完成多维交叉计算功能,反馈客户结果。

四、黑天鹅的未来

TalkingData提供给客户大数据下高速的多维交叉计算能力,还只是一个开始。在大数据时代,技术的发展必将推动业务的进化。TalkingData的未来在于提供客户更快速、更便捷、更灵活的数据服务。黑天鹅也遇到了更多的问题,需要逐一解决。

a) 内存映射文件

比如即便用了优化的压缩技术,Bitmap从文件中迁移到内存中的速度相对来说还是短板。例如系统构建初期,我们曾经把Bitmap存储在Mysql中,感觉还不错,不过随着数据量的增加,随机读的问题越来越严重:大约一次查询中,90%的时间全部被Mysql占去(某些开销是挺浪费的,例如Mysql一次SQL的执行计划,怎么都需要1ms左右)。下一步,我们计划采用“内存映射文件”技术来解决这个问题。

内存映射文件与虚拟内存有些类似,通过内存映射文件可以保留一个地址空间的区域,同时将物理存储器提交给此区域,只是内存文件映射的物理存储器来自一个已经存在于磁盘上的文件,而非系统的页文件,而且在对该文件进行操作之前必须首先对文件进行映射,就如同将整个文件从磁盘加载到内存。

由此可以看出,使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用。

b) 分布式Bitmap计算

可以预见到的第二个Bitmap计算的难题是:我们有可能遇到“需要非常巨大的计算能力才能解决的问题”。举个简单的例子,假如一个客户想看几年的数据指标,那么有可能需要提取出成千上万个,甚至几十万个Bitmap,放到内存中进行计算,这是相当恐怖的要求。

TalkingData第一代Bitmap计算引擎,虽然利用了诸如“Fork-join”技术最大程度的利用CPU/内存,但是遇到上面的计算要求,肯定还是力不从心。第二代Bitmap计算引擎,采用分布式计算:把一个需要非常巨大的计算能力才能解决的问题分成许多小的部分,然后把这些部分分配给许多计算机进行处理,最后把这些计算结果综合起来得到最终的结果。

请大家注意一下Bitmap天生的特性,其实一个Bitmap代表了一个集合,同时它支持的计算中,就是集合计算中的“And/OR/Not”。大家可以在这个wiki上复习一下“集合代数”的知识。

http://zh.wikipedia.org/wiki/%E9%9B%86%E5%90%88%E4%BB%A3%E6%95%B0

这里面有一些给分布式Bitmap计算提供了理论基础,下面这张图表达的是分配律:

25170111_L1Kn.png

假如我们有三个集合,分别是A(去过美国的游客),B(去过中国的游客),C(使用IOS设备的游客)。现在业务人员要求找出,既去过美国,又去过中国,同时又使用IOS设备的游客。最直接的计算是:(A∩B)∪C,但是假如我们在分布式系统的视角下,我们可以分拆后的计算是:(A∪C)∩(B∪C),可以在两台计算节点上分别计算,再汇总到中心节点获得最后结果。基于这些集合代数计算的原理,我们可以把复杂的多维交叉分析,分解成很多小单元计算,分配到不同的服务器上计算,再做汇总计算得到结果。

原理简单,但是执行起来还是有很多困难的,比如数据倾斜、分拆计算优化、在高并发下解决负载均衡……

任重道远!

总结

当我最近阅读30年前的Paper的时候,我发现科学领域的宽广和深度。大约在30年前,天体物理学家利用基数计算(Bitmap就是一种基数计算)来解释恒星数据,除此以外,还有时间序列分析和频谱分析。我们今天研究某个产品在时间上的规律,还有一个人群使用产品的频率特征,不就是这些计算科学在商业世界的再实践吗?

从历史观的角度来看,这些计算科学很早就已经存在,而现代多数程序员由于需要学习各种各样的IDE、语言,忽视了这些基础算法的学习和实践,要持续、有效地发展个人和团队,后者恰恰更重要。Bitmap技术不但让我们支持了业务的发展,也证明了我们走在一条正确的路上:透过现象看本质,从基础的算法出发,吸收各种技术流派的思想,创造属于自己的技术,反馈和服务于技术社区。这就是TalkingData的技术底蕴。

转载于:https://my.oschina.net/muou/blog/470815

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/263182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

POJ 2018 Best Cow Fences (二分答案构造新权值 or 斜率优化)

$ POJ~2018~Best~Cow~ Fences $(二分答案构造新权值) $ solution: $ 题目大意: 给定正整数数列 $ A $ ,求一个平均数最大的长度不小于 $ L $ 的子段 这道题首先我们如果没有长度限制,直接扫一遍数组即可而有了长度限制…

hdu 2531 Catch him

Catch him Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 124 Accepted Submission(s): 49 Problem Description在美式足球中,四分卫负责指挥整只球队的进攻战术和跑位,以及给接球员传球…

POJ 3889 Fractal Streets(逼近模拟)

$ POJ~3889~Fractal~Streets $(模拟) $ solution: $ 这是一道淳朴的模拟题,最近发现这种题目总是可以用逼近法,就再来练练手吧。 首先对于每个编号我们可以用逼近法求出它在各个图上是处于左上,右上,左下&a…

我,只关心接口

我们去饭店吃饭,坐下。然后叫:服务员!好,服务员来了。你会说:倒茶。或说:点菜。是吧。你不会说:来,我们讨论一下什么是面向对象吧。这是为什么呢?很简单,对你…

POJ 2054 Color a Tree (贪心)

$ POJ~2054~Color~a~Tree $ $ solution: $ 我们先从题中抽取信息,因为每个点的费用和染色的次数有关,所以我们可以很自然的想到先给权值大的节点染色。但是题目还说每个节点染色前它的父亲节点也必须被染色,这就有了很多的后效性。 暂时没有办…

使用Null Object设计模式[转]

在ESFramework的设计实现中,很多地方都用到了Null Object设计模式。Null Object模式的含义在于,提供一个对象给指定的类型,用以代替这个对象为空的情况。 Null Object提供了“什么也不做”的行为,隐藏来自它的合作者的细节。对于如何理解和应…

angular input使用输入框filter格式化日期

最近使用angular日期选取器。只需要把所选的输出迄今input输入框,根据默认的假设,显示是在时间的形式的时间戳。不符合规定。需要格成一个特定的公式格公式。但input上ng-model不能直接对用于filter。因此内容需要一种方法来在这里显示格式化。 网上寻找…

CH0805 防线 (二分值域,前缀和,特殊性质)

$ CH~0805~ $ 防线 (二分值域,前缀和,特殊性质) $ solution: $ 注意博主所给题面的输出和原题有些不同 这道题当时想了很久很久,就是想不到怎么写。果然还是太 $ vegetable $ 了。首先我们可以肯定的是,我们不能暴力枚举&#xff…

基于Layui实现的树形菜单页面

基于Layui实现的树形菜单页面具体方法实现方法一:针对Layui模板的前后端统一更新1. 删除2. 添加3. 后端方法二:基于Dtree实现的纯前端树形增删改文中的组件地址具体方法实现 实现树形菜单,本文将给出两种实现方式。 针对Layui前端模板EasyW…

POJ 1723 Soldiers (中位数)

$ POJ~1723~Soldiers $ (中位数) $ solution: $ 这道题说难也不算太难,但是当时自己想的很矛盾。所以还是列一篇题解。 这道题首先比较容易看出来的就是:行和列是两个分开的问题,而且行的移动就是一个仓库选址的板子,直接求中位数…

(一)Windows环境下汇编编程读书笔记

看了一节关于80x86系列处理器简史,不知道云里和雾里,什么晶体管啊,什么什么之类的不知道云里和雾里,看了讲什么都不知道啊! 转载于:https://www.cnblogs.com/Nuxgod/articles/692990.html

Docker知识点总结及其命令的使用

DockerDocker简介Docker与Tomcat有什么区别?Docker与虚拟机有什么区别?Docker的基本组成Docker的联合文件系统Docker基本命令Docker中的几个重要组件一、容器数据卷二、Dockerfile三、Docker网络虚拟机共享网络的三种方式Docker共享网络的四种方式Docker…

主题:Spring注解入门(转载)

原文链接:http://www.iteye.com/topic/295348 1. 使用Spring注解来注入属性 1.1. 使用注解以前我们是怎样注入属性的 类的实现: Java代码 public class UserManagerImpl implements UserManager { private UserDao userDao; public void …

XDJM的情意比山高,比海深!!

又是兄弟姐妹们帮我提前过的生日,我们这帮人从SC出来后还没好好聚过,乘这个机会把大家约了出来。星期五整整一天都很快乐,特别是我,NANA,小乔期待着晚上的聚餐,期待着金贸的蛋糕,嘿嘿。。他们好…

最大子矩阵和

最大子矩阵和 $ n^3 $ 算法 $ solution: $ 首先我们不难想到枚举上下左右边界,然后两层循环统计权值和,复杂度 $ O(n^6) $ 。这个我们用前缀和可以省去后面的循环,将复杂度降成 $ O(n^4) $ 。然后我们考虑不枚举上下左右四个边界,…

Springfox-swagger使用详解

Springfox-swagger使用详解什么是Swagger?Swagger的具体使用一、导入依赖二、建立Swagger配置类三、通过Swagger测试接口引用什么是Swagger? 是一个开源的API Doc的框架可以将我们的Controller中的API方法以文档的形式展现,并支持为其添加注…

Android中调用系统已安装的播放器来播放网络流媒体视频

2019独角兽企业重金招聘Python工程师标准>>> 实现思路比较简单几行代码就可以搞定,在界面放一个Button或者带有播放图标的imageview,点击事件中调用本地播放器来播放。 Uri uri Uri.parse("http://218.200.69.66:8302/upload/Media/20…

WireShark详解

WireShark详解Wireshark介绍Wireshark使用一、基础数据说明二、指定数据包过滤Wireshark安装Wireshark介绍 Wireshark是一款可以运行在多平台的网络抓包工具,可以嗅探通过本机网卡的各类网络包,并对它们的协议,源、目标地址等多种数据进行解…

多图上传 - Web Uploader

http://fex.baidu.com/webuploader/ 官方DEMO,我都不想说了,各种问题。参考ShuaiBi文章 http://www.cnblogs.com/ismars/p/4176912.html 用了bootstrap 代码百度网盘地址:http://pan.baidu.com/s/1pJkj9wf 自己参照改改就好了。 //所有文件上…

[开发手记] 使用.NET实现你的IP切换器

发布日期:2007.4.17 作者:Anytao ©2007 Anytao.com 转贴请注明出处,留此信息。 下载:[Anytao.IPHelper][代码下载,近期上传] 本文将介绍以下内容: • 批处理文件应用 • 调用外部应用 • 文件处理…