假设有个百万签到系统,记录用户签到记录,签了记录1,没签记录 0,如果我们用redis的string存储,一年就要存1000000*365个key,会占用大量的内存。

为了解决这个问题,Redis 提供了位图数据结构,这样每天的签到记录只占据一个位,365 天就是 365 个位,46 个字节 (一个稍长一点的字符串) 就可以完全容纳下,这就大大节约了存储空间。

bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间。

1.常用命令

byte[] bytes = "hzy".getBytes();
for (byte b : bytes) {System.out.println(Integer.toBinaryString(b));
}

执行结果
1101000
1111010
1111001

我们使用位图设置一个字符串"hzy",基本命令是"setbit key [offset] [value]",我们先得到ascll码对应的二进制数据:01101000 01111010 01111001

## 零存整取
127.0.0.1:6379> setbit test 1 1
(integer) 0
127.0.0.1:6379> setbit test 2 1
(integer) 0
127.0.0.1:6379> setbit test 4 1
(integer) 0
127.0.0.1:6379> get test
"h"127.0.0.1:6379> setbit test 9 1
(integer) 0
127.0.0.1:6379> setbit test 10 1
(integer) 0
127.0.0.1:6379> setbit test 11 1
(integer) 0
127.0.0.1:6379> setbit test 12 1
(integer) 0
127.0.0.1:6379> setbit test 14 1
(integer) 0
127.0.0.1:6379> get test
"hz"127.0.0.1:6379> setbit test 17 1
(integer) 0
127.0.0.1:6379> setbit test 18 1
(integer) 0
127.0.0.1:6379> setbit test 19 1
(integer) 0
127.0.0.1:6379> setbit test 20 1
(integer) 0
127.0.0.1:6379> setbit test 23 1
(integer) 0
127.0.0.1:6379> get test
"hzy"## 零存零取
127.0.0.1:6379> getbit test 1
(integer) 1## 整存零取
127.0.0.1:6379> set test2 h
OK
127.0.0.1:6379> getbit test2 1
(integer) 1

2.bitcount&bitpos

我们可以通过 bitcount 统计用户一共签到了多少天。通过 bitpos 指令查找用户从哪一天开始第一次签到,如果指定了范围参数[start, end],就可以统计在某个时间范围内用户签到了多少天。用户自某天以后的哪天开始签到。

但是需要注意的是 start 和 end 参数是字节索引,也就是说指定的位范围必须是 8 的倍数,而不能任意指定。因为这个设计,我们无法直接计算某个月内用户签到了多少天,而必须要将这个月所覆盖的字节内容全部取出来 (getrange 可以取出字符串的子串) 然后在内存里进行统计,这个非常繁琐。

bitcount命令使用,bitcount key [start] [end]

127.0.0.1:6379> set name hzy
OK
127.0.0.1:6379> bitcount name
(integer) 13
127.0.0.1:6379> bitcount name 0 0  # 第一个字符中1的个数
(integer) 3
127.0.0.1:6379> bitcount name 0 1  # 前两个字符中1的个数
(integer) 8

bitpos命令使用,bitpos key bit [start] [end]

hzy对应的ascll码对应的二进制数据:01101000 01111010 01111001

127.0.0.1:6379> bitpos name 0 #第一个0的下标
(integer) 0
127.0.0.1:6379> bitpos name 1 #第一个1的下标
(integer) 1
127.0.0.1:6379> bitpos name 1 2 10 #从第三个字符起,第一个1的下标(也就是说hz不算,只有y参与)
(integer) 17

3.bitfield

我们设置 (setbit) 和获取 (getbit) 指定位的值都是单个位的,如果要一次操作多个位,就必须使用管道来处理。不过 Redis 的 3.2 版本提供了bitfield指令。

bitfield key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]

## 模拟用户2021年7月15日签到,偏移量从0开始
127.0.0.1:6379> setbit userid:sign:202107 14 1
(integer) 0
## 模拟用户2021年7月16日签到,偏移量从0开始
127.0.0.1:6379> setbit userid:sign:202107 15 1
(integer) 0
## 模拟用户2021年7月31日签到,偏移量从0开始
127.0.0.1:6379> setbit userid:sign:202107 30 1
(integer) 0
## 获取用户2021年7月的签到数据
127.0.0.1:6379> bitfield userid:sign:202107 get u31 0
1) (integer) 98305
// 这是伪代码,key是我写死的,真实场景下,userid是用户的ID
public static void main(String[] args) {LocalDate date = LocalDate.now();Map<String, Boolean> signMap = new TreeMap<>();List<Long> list = jedis.bitfield("userid:sign:202107", "GET", String.format("u%d", date.lengthOfMonth()), "0");if (list != null && list.size() > 0) {// 由低位到高位,为0表示未签,为1表示已签long v = list.get(0) == null ? 0 : list.get(0);for (int i = date.lengthOfMonth(); i > 0; i--) {LocalDate d = date.withDayOfMonth(i);signMap.put(formatDate(d, "yyyy-MM-dd"), v >> 1 << 1 != v);v >>= 1;}}System.out.println(signMap.toString());
}
private static String formatDate(LocalDate date, String pattern) {return date.format(DateTimeFormatter.ofPattern(pattern));
}
// 执行结果
{2021-07-01=false, 2021-07-02=false, 2021-07-03=false,
2021-07-04=false, 2021-07-05=false, 2021-07-06=false,
2021-07-07=false, 2021-07-08=false, 2021-07-09=false,
2021-07-10=false, 2021-07-11=false, 2021-07-12=false,
2021-07-13=false, 2021-07-14=false, 2021-07-15=true,
2021-07-16=true, 2021-07-17=false, 2021-07-18=false,
2021-07-19=false, 2021-07-20=false, 2021-07-21=false,
2021-07-22=false, 2021-07-23=false, 2021-07-24=false,
2021-07-25=false, 2021-07-26=false, 2021-07-27=false,
2021-07-28=false, 2021-07-29=false, 2021-07-30=false,
2021-07-31=true}

注意:i是有符号整数 u是无符号整数,例如u8是一个8位的无符号整数,有符号最多可以获取64位,无符号最多可以获取63位

我们使用bitfield key set子指令,把最后一个字符串y,改为大写的Y,Y的ASCII码是89

127.0.0.1:6379> set name hzy
OK
127.0.0.1:6379> bitfield name set u8 16 89
1) (integer) 121
127.0.0.1:6379> get name
"hzY"

3.千万日活的签到系统如何设计?相关推荐

  1. 4.千万日活的系统如何统计UV?

    HyperLogLog 假设有个千万日活的统计系统,需要统计系统每天的UV.如果是你的话你该怎么设计? 如果统计 PV 那非常好办,使用string的incr就搞定了. 但是 UV 不一样,它要去重, ...

  2. 【北京站】技术沙龙第19期:千万日活量级 iOS 应用背后的技术

    美团点评技术沙龙由美团·点评技术团队主办,每期沙龙邀请美团 · 点评及其他互联网公司的技术专家分享来自一线的实践经验,覆盖各主要技术领域. 美团·大众点评作为国内最大的 O2O 平台,旗下有多个iOS ...

  3. 二维码生成与识别 API 结合之活动签到系统的设计与实现

    引言 活动签到是各类会议.展览和社交活动中必不可少的环节.传统的签到方式常常繁琐且容易出错,因此,采用二维码技术进行签到已成为一种常见的解决方案. 本文将介绍一种利用二维码生成 API 和二维码识别 ...

  4. 深度揭秘:从“0”到“数千万”日活,咪咕视讯高速增长...

    咪咕视讯是中国移动旗下咪咕文化科技有限公司的全资子公司,旗下产品涵盖包括直播.影业.视频等,截至2016年3月,咪咕视讯平台拥有千万量级的正版视频内容库,构建了高出4000万的付用度户群,成为海内领先 ...

  5. 微信技术总监:11亿日活的超大型系统架构之道!13页ppt详解

    点击"技术领导力"关注∆  每天早上8:30推送 作者| 周颢    整理| Mr.K 来源| 技术领导力(ID:jishulingdaoli) 作者,微信技术总监 周颢,2001 ...

  6. vue怎么传数据给后台_千万日活级产品人必备:数据监控后台之数据指标怎么选?...

    [导读]通过核心业务驱动是快速搭建数据监控指标体系的不二法则! 文|三爷 3530字 1 为什么要有数据监控后台? 随着创业大潮的兴起,市场中很多初创公司在初期为了快速上线,往往选择只进行功能开发,而 ...

  7. 微信技术总监:11亿日活的超大型系统架构之道!13页ppt详解!

    公众号回复'架构'获取架构师电子书及视频课程 点击下方图标关注公众号IT架构师联盟获取更多专业内容 微信的成功归结于腾讯式的"三位一体"策略:即产品精准.项目敏捷.技术支撑.微信的 ...

  8. 签到系统的设计与实现

    介绍 运营配置签到奖励规则,如"连续签到N天送积分"."累计签到N天送优惠券","指定日期签到送抽奖次数"等,对达成签到条件的用户发放奖励 ...

  9. 连续签到数据表设计php,数据库,连续的签到系统怎么设计?

    背景:对连续签到的系统进行递级给积分,并且连续签到有一定的积分奖励,并且用户连续签到30天给比较多的积分. 要求:并不是按照自然月来签到. 怎么判断是不是连续签到呢? 然后就是数据库怎么设计好呢,因为 ...

最新文章

  1. 三无网站怎样通过服务器审核的,面对“三无”网站该怎么解决?
  2. 利用excel办公软件快速拼凑sql语句
  3. 数学之路-分布式计算-disco(4)
  4. Java -- 异常的捕获及处理 -- 范例 -- throw与throws的应用
  5. dos窗口mysql创建数据库指定字符集_MySQL数据库 dos 命令窗口命令集
  6. jquery获取radio值
  7. 关联Left Outer Join的第一条记录
  8. java同步队列_Java 中队列同步器 AQS(AbstractQueuedSynchronizer)实现原理
  9. HCSE交换知识重点
  10. HashMap 容量的初始化计算公式。是这样算的。请收藏
  11. 【c语言】产生一个1到1000的随机整数,用户进行猜测
  12. 模型实践 | Alphafold 蛋白质结构预测
  13. Android术语查询软件,术语在线app_术语在线查询_学术术语在线-多特软件站安卓网...
  14. 小米pro加装固态硬盘以及拆机方法
  15. 用 Python 实现手机自动答题,这下百万答题游戏谁也玩不过我!
  16. 看地理标志产品苏尼特羊肉如何投放广告宣传打造品牌
  17. 几种重要的排序算法——交换排序
  18. 怎样记住英语单词的意思
  19. mysql伪列的使用,mysql伪列的妙用,mysql获取近几天的日期用来连表查询
  20. [词性] 二十三、情态动词 2 [ have to ] [ ought to ] [ dare ] [ be able to ] [ needn‘t ] [ had better ]

热门文章

  1. a标签 靠右对齐,a标签中的图标和文字如何上下对齐
  2. SQL注入中的报错注入,updatexml(1,concat(0x7e,database(),0x7e),1)
  3. Maven聚合项目搭建集成knife4j
  4. 地铁时光机第一阶段冲刺四
  5. mysql好友关系数据表设计_社交系统中用户好友关系数据库设计
  6. 解决VMmare虚拟机安装过程没有权限问题
  7. 麒麟操作系统+龙心 编译qt-arm
  8. linux最新内核5.80版本移植详细过程
  9. SharePoint PerformancePoint(BI)开发实例
  10. Word 给论文加不带编号的脚注并保证分栏的正文内容不受影响,以及在word中如何分栏和分节?