首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >第六章 bitma介绍

第六章 bitma介绍

原创
作者头像
RookieCyliner
发布2025-06-08 21:44:25
发布2025-06-08 21:44:25
1580
举报
文章被收录于专栏:redisredis

常见的四种统计

①. 聚合统计

统计多个集合元素的聚合结果,就是前面讲解过的交差并等集合统计交并差集和聚合函数的应用

②. 排序统计:如抖音视频最新评论留言的场景,请你设计一个展现列表。考察你的数据结构和设计思路 - list、zset

③. 二值统计:集合元素的取值就只有0和1两种。在钉钉上班签到打卡的场景中,我们只用记录有签到(1)或没签到(0) - bitmap

④. 基数统计:指统计⼀个集合中不重复的元素个数 - hyperloglog

bitmap位图 - 概述

  • Bit array我们可以称之为位图,由许许多多的小格子组成,每一个小格子里面只能放1或者0,用它判断Y/N状态说的专业,每一个个小格子就是一个个的bit
bitmap结构
bitmap结构
  • 用String类型作为底层数据结构实现的一种统计二值状态的数据类型位图本质是数组,它是基于String数据类型的按位的操作。该数组由多个二进制位组成,每个二进制位都对应一个偏移量(我们可以称之为一个索引或者位格)。Bitmap支持的最大位数是2^32 位,它可以极大的节约存储空间,使用512M内存就可以存储多大42.9亿的字节信息(2^32 = 4294967296)
代码语言:txt
复制
512M = 4294967296 / 8 /1024 /1024
  • 由0和1状态表现的二进制位的bit数组

bitmap位图 - 作用

  1. 用户是否登陆过Y、N,比如京东每日签到送京豆
  2. 电影、广告是否被点击播放过
  3. 钉钉打卡上下班,签到统计
  4. 统计指定用户一年之中的登陆天数
  5. 某用户按照一年365天,哪几天登陆过?哪几天没有登陆?全年中登录的天数共计多少?

基本命令

基本命令汇总

bitmap基本命令
bitmap基本命令
  • setbit key offset value(setbit 键 偏移位 只能0或者1),Bitmap的偏移量是从零开始算的.
代码语言:txt
复制
127.0.0.1:6379> setbit bitmap1 0 1 # 111001111
(integer) 0
127.0.0.1:6379> setbit bitmap1 1 1
(integer) 0
127.0.0.1:6379> setbit bitmap1 2 1
(integer) 0
127.0.0.1:6379> setbit bitmap1 5 1
(integer) 0
127.0.0.1:6379> setbit bitmap1 6 1
(integer) 0
127.0.0.1:6379> setbit bitmap1 7 1
(integer) 0
127.0.0.1:6379> setbit bitmap1 8 1
(integer) 0
127.0.0.1:6379> getbit bitmap1 0
(integer) 1
127.0.0.1:6379> strlen bitmap1 # 2个字节
(integer) 2
127.0.0.1:6379> bitcount bitmap1 # 7个长度
(integer) 7
127.0.0.1:6379> bitfield bitmap1 get u2 0 # u:表示无符号,2查询两个 0:下标开始位置
1) (integer) 3  # 11 = 1+2=3
127.0.0.1:6379> bitfield bitmap1 get u3 0
1) (integer) 7  # 111=1+2+4=7
127.0.0.1:6379> bitpos bitmap1 1 # 1开始的位置
(integer) 0
127.0.0.1:6379> bitpos bitmap1 0 # 0开始的位置
(integer) 3
127.0.0.1:6379> 
  • getbit key offset
  • setbit和getbit案例说明:按照天、按照年
案例说明
案例说明
  • bitmap的底层编码说明,get命令操作如何
  1. 实质是二进制的ascii编码对应
  2. redis里用type命令看看bitmap实质是什么类型?String类型
ASCII
ASCII
案例说明
案例说明

strlen:统计字节数占用多少,不是字符串长度而是占据几个字节,超过8位后自己按照8位一组一byte再扩容

strlen说明
strlen说明

bitcount作用

  • 全部键里面含有1的有多少个
  • 一年365天,全年天天登陆占用多少字节
bitcount示例
bitcount示例

bitop作用

  • 连续2天都签到的用户
  • 加入某个网站或者系统,它的用户有1000W,做个用户id和位置的映射

bittop示例
bittop示例
代码语言:txt
复制
redis> SETBIT bits-1 0 1        # bits-1 = 1001
(integer) 0

redis> SETBIT bits-1 3 1
(integer) 0
 
redis> SETBIT bits-2 0 1        # bits-2 = 1101
(integer) 0
 
redis> SETBIT bits-2 1 1
(integer) 0
 
redis> SETBIT bits-2 3 1
(integer) 0
 
redis> BITOP AND and-result bits-1 bits-2
(integer) 1
 
redis> GETBIT and-result 0      # and-result = 1001
(integer) 1
 
redis> GETBIT and-result 1
(integer) 0
 
redis> GETBIT and-result 2
(integer) 0

redis> GETBIT and-result 3
(integer) 1

签到实现逻辑

  1. 用户一次签到,就是一条记录,假如有1000万用户,平均每人每年签到次数为10次,则这张表一年的数据量为1亿条
  2. 每签到一次需要使用(8 + 8 + 1 + 1 + 3 + 1)共22字节的内存,一个月则最多需要600多字节

签到功能设计的mysql表

代码语言:txt
复制
create table `tb_sign`(
	`id` bigint(20) unsigned NOT NULL COMMENT '主键id',
	`user_id` bigint(20) unsigned NOT NULL COMMENT '用户id',
	`year` year(4) NOT NULL COMMENT '签到的年',
	`month` tinyint(2) NOT NULL COMMENT '签到的月',
	`date` date NOT NULL COMMENT '签到的日期',
	`is_backup` tinyint(1) unsigned DEFAULT NULL COMMENT '是否签到',
    PRIMARY KEY(`id`)
)ENGINE=INNODB COMMENT='签到表';

我们如何能够简化一点呢?其实可以考虑小时候一个挺常见的方案,就是小时候,咱们准备一张小小的卡片,你只要签到就打上一个勾,我最后判断你是否签到,其实只需要到小卡片上看一看就知道了

某学生签到表
某学生签到表

我们可以采用类似这样的方案来实现我们的签到需求 - bitmap(位图)入肉片;

bitmap签到示例图
bitmap签到示例图

实现签到接口,将当前用户当天签到信息保存到Redis中。

思路:我们可以把年和月作为bitMap的key,然后保存到一个bitMap中,每次签到就到对应的位上把数字从0变成1,只要对应是1,就表明说明这一天已经签到了,反之则没有签到

代码语言:txt
复制
    @PostMapping("/user/sign/{userId}")
    public void sign(@PathVariable(value = "userId") String userId){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        String month = new SimpleDateFormat("yyyyMM").format(new Date());
        String key=signKey+month;
        int monthDay = calendar.get(Calendar.DAY_OF_MONTH);
        redisTemplate.opsForValue().setBit(key,monthDay-1,true);
    }

实现连续签到

什么叫做连续签到天数?

  • 从最后一次签到开始向前统计,直到遇到第一次未签到为止,计算总的签到次数,就是连续签到天数Java逻辑代码:获得当前这个月的最后一次签到数据,定义一个计数器,然后不停的向前统计,直到获得第一个非0的数字即可,每得到一个非0的数字计数器+1,直到遍历完所有的数据,就可以获得当前月的签到总天数了。

如何得到本月到今天为止的所有签到数据?(使用BITFIELD)

  • 假设今天是10号,那么我们就可以从当前月的第一天开始,获得到当前这一天的位数,是10号,那么就是10位,去拿这段时间的数据,就能拿到所有的数据了,那么这10天里边签到了多少次呢?统计有多少个1即可
代码语言:txt
复制
BITFIELD key GET u[dayOfMonth] 0

如何从后向前遍历每个bit位?

  • bitMap返回的数据是10进制,假如说返回一个数字8,那么我哪儿知道到底哪些是0,哪些是1呢?我们只需要让得到的10进制数字和1做与运算就可以了,因为1只有遇见1 才是1,其他数字都是0 ,我们把签到结果和1进行与操作,每与一次拿到最后一个bit位,如果不为0,就把签到结果向右移动一位,计数器+1,继续循环。如果为0,直接结束循环。依次内推,我们就能完成逐个遍历的效果了

代码实现

代码语言:txt
复制
	@PostMapping("/user/signCount/{userId}")
    public void signCount(@PathVariable(value = "userId") String userId){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        String month = new SimpleDateFormat("yyyyMM").format(new Date());
        String key=signKey+month;
        int monthDay = calendar.get(Calendar.DAY_OF_MONTH);
        // 5.获取本月截止今天为止的所有的签到记录,返回的是一个十进制的数字 BITFIELD sign:5:202203 GET u20 0
        List<Long> result = redisTemplate.opsForValue().bitField(
                key,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(monthDay)).valueAt(0)
        );
        if (result == null || result.isEmpty()) {
            // 没有任何签到结果
            return;
        }
        Long num = result.get(0);
        if (num == null || num == 0) {
            return;
        }
        // 6.循环遍历
        int count = 0;
        while (true) {
            // 6.1.让这个数字与1做与运算,得到数字的最后一个bit位  // 判断这个bit位是否为0
            if ((num & 1) == 0) {
                // 如果为0,说明未签到,结束
                break;
            }else {
                // 如果不为0,说明已签到,计数器+1
                count++;
            }
            // 把数字右移一位,抛弃最后一个bit位,继续下一个bit位
            num >>>= 1;
        }
        System.out.println(count);
    }

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 常见的四种统计
  • bitmap位图 - 概述
  • bitmap位图 - 作用
  • 基本命令
    • 基本命令汇总
  • 签到实现逻辑
    • 实现连续签到
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档