转载自 https://www.cnblogs.com/fvsfvs123/p/4293203.html

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)。

[java]

  • Redis.setbit(play:yyyy-mm-dd, user_id, 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代码用来统计某个用户操作在某天的活跃用户。
[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();
  • }

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代码用来统计某个用户操作在一个指定多个日期的活跃用户。
[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();
  • }

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

About Author:

Garyelephant
      garygaowork[at]gmail.com
       关注互联网创新、分布式、NOSQL,高并发技术。

使用Redis bitmaps进行快速、简单、实时统计相关推荐

  1. Redis实战 - 09 Redis BitMaps 实现用户签到,统计签到次数,统计签到情况等功能

    文章目录 1. 需求分析 2. 设计思路 3. 用户签到和统计连续签到的次数 1. 签到控制层 SignController 2. 签到业务逻辑层 SignService 3. 测试 4. 按月统计用 ...

  2. Flume+Kafka+Storm+Redis构建大数据实时处理系统:实时统计网站PV、UV+展示

    http://blog.51cto.com/xpleaf/2104160?cid=704690 1 大数据处理的常用方法 前面在我的另一篇文章中<大数据采集.清洗.处理:使用MapReduce进 ...

  3. Spark Streaming 项目实战 (4) | 得到最近1小时广告点击量实时统计并写入到redis

      大家好,我是不温卜火,是一名计算机学院大数据专业大二的学生,昵称来源于成语-不温不火,本意是希望自己性情温和.作为一名互联网行业的小白,博主写博客一方面是为了记录自己的学习过程,另一方面是总结自己 ...

  4. 基于netty+websocket实现门户游客实时统计功能

    基于netty+websocket实现门户游客实时统计功能 基本需求 商城门户页面需要实时展示游客访问的数量,商城后台页面需要实时游客访问量.登录用户数量,以及下订单用户数量. 技术选型 1.首先实时 ...

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

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

  6. Redis数据结构:快速的Redis有哪些慢操作?

    Redis数据结构:快速的Redis有哪些慢操作? 引言 键和值用什么结构组织 为什么哈希表操作变慢了 集合数据操作效率 有哪些底层数据结构 不同操作的复杂度 引言 它接收到一个键值对操作后,能以微秒 ...

  7. 【项目二】爱奇艺分类点击实时统计

    项目源码: SparkStreaming部分:https://gitee.com/jenrey/project_two SpringBoot部分:https://gitee.com/jenrey/pr ...

  8. 爱奇艺分类点击实时统计

    项目源码: SparkStreaming部分:https://gitee.com/jenrey/project_two SpringBoot部分:https://gitee.com/jenrey/pr ...

  9. 用redis实现消息队列(实时消费+ack机制)【转】

    用redis实现消息队列(实时消费+ack机制) java queue 消息队列 redis 消息队列 首先做简单的引入. MQ主要是用来: 解耦应用. 异步化消息 流量削峰填谷 目前使用的较多的有A ...

最新文章

  1. 使用NetBeans IDE开发C程序
  2. 移动端开发框架Zepto.js
  3. android ndk常见的问题及解决的方法
  4. 面试必问:读写一致性,你需要思考的问题
  5. [BZOJ 1588] [HNOI 2002] 营业额统计
  6. *++p和*p++的区别
  7. Extjs GridPanel 监听事件 行选中背景
  8. 分享:mysql分表,分区的区别和联系
  9. jquery 表单 清空
  10. swagger上传文件并支持jwt认证
  11. 站在巨人肩膀上——vb.net学习
  12. 长得像就是双胞胎?图样图森破
  13. raid服务器怎么装win7系统安装,安装win7系统时怎么加载SATARAID驱动
  14. 怎么用python画一个皮卡丘,用python画皮卡丘的代码
  15. 帕斯卡三角形 python
  16. 7小时Unity3D游戏开发培训教程
  17. 阿里达摩院的AI Earth(AIE)初体验
  18. 甲骨文欲购市值775亿美元的埃森哲 已聘专家评估
  19. 新构造运动名词解释_新构造运动的含义
  20. android 8.1 9.0 10.0 默认允许安装第三方app去掉未知来源弹窗直接安装apk

热门文章

  1. [USACO18JAN][luoguP4183 ]Cow at Large P
  2. PostgreSQL学习笔记2之模式
  3. C++设计模式之装饰模式
  4. 千万不要在深夜提交代码
  5. 腾讯TencentOS 十年云原生的迭代演进之路
  6. 深度解密Go语言之Slice
  7. 音视频技术开发周刊 | 133
  8. LiveVideoStackCon深圳-精选最新多媒体技术方案
  9. Spark之Spark概述
  10. 解决github图片不显示问题【完美解决】