原文:Fast, easy, realtime metrics using Redis bitmaps

http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps/

getspool.com的重要统计数据是实时计算的。Redis的bitmap让我们可以实时的进行类似的统计,并且极其节省空间。在模拟1亿2千8百万用户的模拟环境下,在一台MacBookPro上,典型的统计如“日用户数”(dailyunique users) 的时间消耗小于50ms, 占用16MB内存。Spool现在还没有1亿2千8百万用户,但是我们的方案可以应对这样的规模。我们想分享这是如何做到的,也许能帮到其它创业公司。

Bitmap以及Redis Bitmaps快速入门(Crash Course on Bitmap and Redis Bitmaps)

Bitmap(即Bitset)

Bitmap是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset),在bitmap上可执行AND,OR,XOR以及其它位操作。

位图计数(Population Count)

位图计数统计的是bitmap中值为1的位的个数。位图计数的效率很高,例如,一个bitmap包含10亿个位,90%的位都置为1,在一台MacBook Pro上对其做位图计数需要21.1ms。SSE4甚至有对整形(integer)做位图计数的硬件指令。

Redis Bitmaps

Redis允许使用二进制数据的Key(binary keys) 和二进制数据的Value(binary values)。Bitmap就是二进制数据的value。Redis的 setbit(key, offset, value)操作对指定的key的value的指定偏移(offset)的位置1或0,时间复杂度是O(1)。

一个简单的例子:日活跃用户

为了统计今日登录的用户数,我们建立了一个bitmap,每一位标识一个用户ID。当某个用户访问我们的网页或执行了某个操作,就在bitmap中把标识此用户的位置为1。在Redis中获取此bitmap的key值是通过用户执行操作的类型和时间戳获得的。

这个简单的例子中,每次用户登录时会执行一次redis.setbit(daily_active_users, user_id, 1)。将bitmap中对应位置的位置为1,时间复杂度是O(1)。统计bitmap结果显示有今天有9个用户登录。Bitmap的key是daily_active_users,它的值是1011110100100101。

因为日活跃用户每天都变化,所以需要每天创建一个新的bitmap。我们简单地把日期添加到key后面,实现了这个功能。例如,要统计某一天有多少个用户至少听了一个音乐app中的一首歌曲,可以把这个bitmap的redis key设计为play:yyyy-mm-dd-hh。当用户听了一首歌曲,我们只是简单地在bitmap中把标识这个用户的位置为1,时间复杂度是O(1)。

Redis.setbit(play:yyyy-mm-dd, user_id, 1)

今天听过歌曲的用户就是key是play:yyyy-mm-dd的bitmap的位图计数。如果要按周或月统计,只要对这周或这个月的所有bitmap求并集,得出新的bitmap,在对它做位图计数。

利用这些bitmap做其它复杂的统计也非常容易。例如,统计11月听过歌曲的高级用户(premium user):

(play:2011-11-01∪ play:2011-11-02∪ … ∪ play:2011-11-30)∩premium:2011-11

1亿2千8百万用户的性能比较(Performance comparison using 128 million users)

下面的表格显示了在1亿2千8百万用户上完成的时间粒度为1天,一周,一个月的用户统计的时间消耗比较。

Period Time(ms)
Daily 50.2
Weekly 392.0
Monthly 1624.8

优化(Optimizations)

前面的例子中,我们把日统计,周统计,月统计缓存到Redis,以加快统计速度。

这是一种非常灵活的方法。这样进行缓存的额外红利是可以进行更多的统计,如每周活跃的手机用户—求手机用户的bitmap与周活跃用户的交集。或者,如果要统计过去n天的活跃用户数,缓存的日活跃用户使这样的统计变得简单——从cache中获取过去n-1天的日活跃用户bitmap和今天的bitmap,对它们做并集(Union),时间消耗是50ms。

示例代码(SampleCode)

下面的Java代码用来统计某个用户操作在某天的活跃用户。

import redis.clients.jedis.Jedis;
import java.util.BitSet;
...
Jedis redis = new Jedis("localhost");
...
public int uniqueCount(String action, String date) {
String key = action + ":" + date;
BitSet users = BitSet.valueOf(redis.get(key.getBytes()));
return users.cardinality();
}

下面的Java代码用来统计某个用户操作在一个指定多个日期的活跃用户。

import redis.clients.jedis.Jedis;
import java.util.BitSet;
...
Jedis redis = new Jedis("localhost");
...
public int uniqueCount(String action, String... dates) {
BitSet all = new BitSet();
for (String date : dates) {
String key = action + ":" + date;
BitSet users = BitSet.valueOf(redis.get(key.getBytes()));
all.or(users);
}
return all.cardinality();
}

References:

[1] Redis setbit command http://redis.io/commands/setbit

About Author:

Garyelephant

garygaowork[at]gmail.com

关注互联网创新、分布式、NOSQL,高并发技术。

使用Redis bitmaps进行快速、简单、实时统计的更多相关文章

  1. 实时统计每天pv,uv的sparkStreaming结合redis结果存入mysql供前端展示

    最近有个需求,实时统计pv,uv,结果按照date,hour,pv,uv来展示,按天统计,第二天重新统计,当然了实际还需要按照类型字段分类统计pv,uv,比如按照date,hour,pv,uv,typ ...

  2. Kafka项目实战-用户日志上报实时统计之应用概述

    1.概述 本课程的视频教程地址:<Kafka实战项目之应用概述> 本课程是通过一个用户实时上报日志来展开的,通过介绍 Kafka 的业务和应用场景,并带着大家搭建本 Kafka 项目的实战 ...

  3. Kafka项目实战-用户日志上报实时统计之编码实践

    1.概述 本课程的视频教程地址:<Kafka实战项目之编码实践>  该课程我以用户实时上报日志案例为基础,带着大家去完成各个KPI的编码工作,实现生产模块.消费模块,数据持久化,以及应用调 ...

  4. 基于Redis位图实现系统用户登录统计

    项目需求,试着写了一个简单登录统计,基本功能都实现了,日志数据量小.具体性能没有进行测试~ 记录下开发过程与代码,留着以后改进! 1. 需求 1. 实现记录用户哪天进行了登录,每天只记录是否登录过,重 ...

  5. JavaScript 实现textarea限制输入字数, 输入框字数实时统计更新,输入框实时字数计算移动端bug解决

    textarea称文本域,又称文本区,即有滚动条的多行文本输入控件,在网页的提交表单中经常用到.与单行文本框text控件不同,它不能通过maxlength属性来限制字数,为此必须寻求其他方法来加以限制 ...

  6. Kafka项目实战-用户日志上报实时统计之分析与设计

    1.概述 本课程的视频教程地址:<Kafka实战项目之分析与设计>  本课程我通过一个用户实时上报日志案例作为基础,带着大家去分析Kafka这样一个项目的各个环节,从而对项目的整体设计做比 ...

  7. Redis 十分钟快速入门

    本教程是一个快速入门教程,所以Redis的命令只是简单介绍了几个常用的,如果有其他需求请求官网查看API 使用. 1. Redis简介 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的ke ...

  8. 六、Kafka 用户日志上报实时统计之分析与设计

    一.项目整体概述 简述项目的背景 背景:用户行迹企业运营 分析项目的目的 通过对项目的分析,可以初步得到以下目的: •实时掌握用户动态 •根据实时统计结果,适度推广 •统计分析效果,快速合理的调整 二 ...

  9. 59、Spark Streaming与Spark SQL结合使用之top3热门商品实时统计案例

    一.top3热门商品实时统计案例 1.概述 Spark Streaming最强大的地方在于,可以与Spark Core.Spark SQL整合使用,之前已经通过transform.foreachRDD ...

随机推荐

  1. Java读写Windows共享文件夹 .

    版权声明:本文为博主原创文章,未经博主允许不得转载. 项目常常需要有访问共享文件夹的需求,例如共享文件夹存储照片.文件等.那么如何使用Java读写Windows共享文件夹呢? Java可以使用JCIF ...

  2. 安装Hadoop系列 — 安装Hadoop

    安装步骤如下: 1)下载hadoop:hadoop-1.0.3     http://archive.apache.org/dist/hadoop/core/hadoop-1.0.3/   2)解压文 ...

  3. 关于hadoop的环境变量

    export PATH export JAVA_HOME=/opt/jdk1.7 export PATH=$PATH:$JAVA_HOME/bin 为什么/etc/profile 添加了环境变量had ...

  4. Magic skills of vim from zhihu

    https://www.zhihu.com/question/27478597 插入模式下ctrl-y,重复当前光标上一行的字符 gd 高亮当前词 cc 删除当前行并插入 “.” 这个 mark 代表 ...

  5. JPA详解

    2006 年 5 月 2 日,EJB 3.0 规范最终版由 JCP(Java Community Process) 正式公布,标准号为 JSR(Java Specification Request)2 ...

  6. 深入理解Android内存管理原理(六)

    一般来说,程序使用内存的方式遵循先向操作系统申请一块内存,使用内存,使用完毕之后释放内存归还给操作系统.然而在传统的C/C++等要求显式释放内存的编程语言中,记得在合适的时候释放内存是一个很有难度的工 ...

  7. 第三部分 MediaPlayer的主要实现分析

    第三部分 MediaPlayer的主要实现分析 3.1 JAVA程序部分    在packages/apps/Music/src/com/android/music/目录的MediaPlaybackS ...

  8. BZOJ3280: 小R的烦恼

    题解: 随便建一下图费用流就可以过吧... 代码: #include<cstdio> #include<cstdlib> #include<cmath> #incl ...

  9. 在Google被封的那些日子裏,我們這樣科學上網

    回到正題,如果某天你喜歡的網站被封了,你工作的郵箱無法訪問了,該如何用正確姿勢實現科學上網呢?雷鋒網為大家整理了數個小技巧. 1.FreeGate類傻瓜工具 很多人第一次用翻墻,應該是從FreeGat ...

  10. po 时不生效时, 不要用点方法

    Dot notation for message sending is not supported in lldb. Use bracket notation and cast the result ...