当前位置: 首页 > news >正文

Redis BitMap(位图)

这里是小咸鱼的技术窝(CSDN板块),我又开卷了

在这里插入图片描述

之前经手的项目运行了10多年,基于重构,里面有要实现一些诸如签到的需求,以及日历图的展示,可以用将签到信息存到传统的关系型数据库(MySql、Oracle) 然后用 Sql 进行统计、也可以借助 Redis 中的 BitMap来实现签到需求。俩者的区别就在于,一个签到了100万年的项目,你用Mysql无论如何进行分库分表,落地到磁盘的上的数据体量都很大,且查询速度会变慢,但是用 Redis 存储数据占用的内存小,且统计效率更快。当然如果你怕Redis挂,也可以存俩份数据,一份放Mysql一份放Redis,利用 Redis 缓存考勤结果加快查询效率,但是占用的机器磁盘过大的问题还是没解决,下面介绍用 BitMap 如何实现下面这个签到日历。

在这里插入图片描述

这个项目的签到用到了海康的人脸识别SDK靠每天进行人脸识别打卡,然后记录签到信息的,就拿手动点击签到来说。整个流程如下。

用户点击签到按钮,发起一个签到请求给我们,先调用 getbit 命令,判断今天有没有进行签到过,没有然后调用 setbit 命令,把签到结果存入 BitMap(期间用户重复点击签到也没事,BitMap多次重复设置值的操作是幂等的)

BitMap 介绍 https://redis.io/commands/bitop/

下面的这段洋文是官网对BitMap的介绍

Bitmaps are not an actual data type, but a set of bit-oriented operations defined on the String type which is treated like a bit vector. Since strings are binary safe blobs and their maximum length is 512 MB, they are suitable to set up to 2^32 different bits.

在这里插入图片描述

位图不是实际的数据类型,而是在字符串类型上定义的一组面向位的操作,该操作被视为位向量。由于字符串是二进制安全blob,字符串最大为512 MB,因此它们适合设置为2^32个不同的位。从这段话中我们可以知道,BitMap 其实就是一个二进制的容器,并且他的二进制位最大为 2^32 位(512Mb = 512 * 1024 * 1024 * 8 = 2^32个bit位),当然我们签到只用的到31 bit 位,因此针对于签到需求来说,Bitmap初始结构长这样

0000000000|0000000000|0000000000|0

联想一下我们一个月最大 31 天,1号签到了我们在第一bit位设置为1就好了,同理在第2,3,4,5天签到了,在对应的bit位设置值为1就好了,BitMap是不是很适合签到场景。并且它提供了很多统计API。

在这里插入图片描述

SETBIT key offset value:将指定偏移量上的位设置为10GETBIT key offset:获取指定偏移量上的位的值。
BITCOUNT key start end:计算指定key的Bitmap中1的数量。
BITOP operation destkey key [key...]:对多个key进行位运算,并将结果存储到destkey中。operation可以是ANDORXORNOT等。
BITPOS key 1 start end:查找指定key的Bitmap中第一个1的偏移量。
BITPOS key 0 start end:查找指定key的Bitmap中第一个0的偏移量。
BITPOS key 0 start:从start的位置开始,查找指定key的Bitmap中第一个0的偏移量。
SET key value:将指定key的Bitmap设置为指定的值。
GET key:获取指定key的Bitmap的值。
//类型u代表无符号十进制,i代表带符号十进制
BITFIELD key get u3 0:从偏移量offset=0开始取3位,获取无符号整数的值,返回的值是一个十进制数
INCRBY u4 4 1:从偏移量offset=4后开始取4位,得到一个对应的二进制数,然后转换成10进制加1
  • 利用 bitcount 命令我们可以快速统计出用户,本月签到了多少天
  • 利用 setbit 命令我们可以快速记录签到信息
  • 利用 getbit 命令我们可以快速判断用户今天是否已经签到

值得注意的是 BITCOUNT key start end 命令中的 start end 指的是 byte 的区间,而 1 byte = 8 bit。那么执行如下命令

BITCOUNT key start end

bitcount char 0 8的结果是10就不奇怪了,统计范围是 0-8 byte区间,转换成 bit 区间就是 0-64。0-64范围内有 10个1。因此结果是10而不是3!!!!!

在这里插入图片描述
在这里插入图片描述

签到场景里面一般都不会直接去给Bitmap设置字符串,设置字符串还要去转换成对应的ACSII码然后存入BitMap,而是用 setbit 命令按bit设置值。

在这里插入图片描述

注意点二 例如 执行 setbit key 8 1 命令,存储的是 0000000010000000 而不是 000000001,低bit 位会用 0 补齐,因为 byte 是最小的存储单元。这些个个细节要搞清楚哦。

在这里插入图片描述

另外BitMap也支持二进制数(与、或运算),平常很少用这个。跟着我大声朗读运算法则,与运算都是1才为1,或运算有1就是1。

mac:0>set a a (01100001)
"OK"
mac:0>set b b (01100010)
"OK"
mac:0>bitop and andres a b (a、b与运算,结果设置到anres)
"1"
mac:0>bitop or orres a b (a、b或运算,结果设置到orres)

BitMap的源码,自行去翻阅redis安装目录下的 src里面的c文件,蹲一个c语言大佬

在这里插入图片描述

在这里插入图片描述

BitMap签到脚手架

接着用Jedis实现,因为Api命名和原始redis命令很接近。实际开发中合理织入自己的业务逻辑即可。

@Slf4j
@Api(tags = "Bit签到")
@RestController
@RequestMapping("/bit")
public class BitController {@Autowiredprivate Jedis jedis;@ApiOperation("获取签到天数")@PostMapping("/userSignCount")public Result userSignCount(@RequestBody RedisRequest redisRequest) {//相当于执行 bitcount user:sign:uid:yyyyMM 0 31 命令return Result.success(jedis.bitcount(buildSignKey(redisRequest.getUid(),getDate(redisRequest.getDateStr())), 0, 31));}@ApiOperation("签到/补签")@PostMapping("/userSign")public Result sign(@RequestBody RedisRequest redisRequest) {//日期字符串转DateDate date = getDate(redisRequest.getDateStr());//该日期该月的第几天int offset = DateUtil.dayOfMonth(date) - 1;//user:sign:uid:yyyyMMString signKey = buildSignKey(redisRequest.getUid(), date);Assert.isFalse(jedis.getbit(signKey, offset), "今天以及签到过了");//相当于执行 setbit user:sign:uid:yyyyMM offset 1 命令jedis.setbit(signKey, offset, true);return Result.success(getContinuousSignCount(redisRequest.getUid(), date));}/*** 统计截止日之前连续签到的天数* @param userId 用户id* @param date 截止日* @return*/private int getContinuousSignCount(Integer userId, Date date) {int dayOfMonth = DateUtil.dayOfMonth(date);//相当于执行 bitfield user:sign:uid:yyyyMM u10 0 命令List<Long> list = jedis.bitfield(buildSignKey(userId, date), "get", "u" + dayOfMonth, "0");if (list == null || list.isEmpty()) {return 0;}int signCount = 0;long v = list.get(0) == null ? 0 : list.get(0);// i 表示位移操作次数for (int i = dayOfMonth; i > 0; i--) {// 右移再左移,如果等于自己说明最低位是 0,表示未签到if (v >> 1 << 1 == v) {// 低位 0 且非当天说明连续签到中断了if (i != dayOfMonth) {break;}} else {signCount++;}// 右移一位并重新赋值,相当于把最低位丢弃一位v >>= 1;}return signCount;}private String buildSignKey(Integer userId, Date date) {return String.format("user:sign:%d:%s", userId,DateUtil.format(date, "yyyyMM"));}/*** 获取日期*/private Date getDate(String dateStr) {if (StrUtil.isBlank(dateStr)) {return new Date();}try {return DateUtil.parseDate(dateStr);} catch (Exception e) {throw new RuntimeException("请传入yyyy-MM-dd的日期格式");}}}

BitMaq去重

因为字符串最大为512mb,因此可以算出BitMap的最大长度是2的32次方。约42亿个bit位。去重也很简单,就拿我的QQ号去重来说,QQ: 3273448110,在 3273448110 的bit位置上设置为1就好了,同理其他QQ号在自己的Bit位上设置为1,最终统计BitMap有多少个1就得到了,不重复QQ号的个数。

总结

本文涵盖了BitMap从Api使用,到落地到常用场景的上的真实业务体现。以及使用上的注意事项。

这里是小咸鱼的技术窝

在这里插入图片描述

相关文章:

Redis BitMap(位图)

这里是小咸鱼的技术窝&#xff08;CSDN板块&#xff09;&#xff0c;我又开卷了 之前经手的项目运行了10多年&#xff0c;基于重构&#xff0c;里面有要实现一些诸如签到的需求&#xff0c;以及日历图的展示&#xff0c;可以用将签到信息存到传统的关系型数据库&#xff08;MyS…...

使用eclipse创建一个java文件并运行

启动 Eclipse 并创建一个新的 Java 项目: 打开 Eclipse。 选择 “File” > “New” > “Java Project”&#xff08;文件 > 新建 > Java 项目&#xff09;。 在弹出的窗口中&#xff0c;为你的项目命名&#xff0c;比如 MyJavaProject。 点击 “Finish”&#xff…...

C#上位机与欧姆龙PLC的通信05---- HostLink协议

1、介绍 Hostlink协议是欧姆龙PLC与上位机链接的公开协议。上位机通过发送Hostlink命令&#xff0c;可以对PLC进行I/O读写、可以对PLC进行I/O读写、改变操作模式、强制置位/复位等操作。由于是公开协议&#xff0c;即便是非欧姆龙的上位设备&#xff08;软件&#xff09;&…...

Uniapp 开发 BLE

BLE 低功耗蓝牙&#xff08;Bluetooth Low Energy&#xff0c;或称Bluetooth LE、BLE&#xff0c;旧商标Bluetooth Smart&#xff09;&#xff0c;用于医疗保健、运动健身、安防、工业控制、家庭娱乐等领域。在如今的物联网时代下大放异彩&#xff0c;扮演者重要一环&#xff…...

c语言排序算法

C语言代码示例&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09;&#xff1a; void bubbleSort(int arr[], int n) {for (int i 0; i < n-1; i) {for (int j 0; j < n-i-1; j) {if (arr[j] > arr[j1]) {int temp arr[j];arr[j] arr[j1];arr[j1] temp;…...

【机器学习】模式识别

1 概述 模式识别&#xff0c;简单来讲&#xff0c;就是分类问题。 模式识别应用&#xff1a;医学影像分析、人脸识别、车牌识别、遥感图像 2 模式分类器 分类器的分类&#xff1a;线性分类器、非线性分类器、最近邻分类器 2.1 分类器的训练&#xff08;学习&#xff09;过…...

【Prometheus|报错】Out of bounds

【背景】进入Prometheus地址的9090端口&#xff0c;pushgateway&#xff08;0/1&#xff09;error : out of bounds 【排查分析】 1、out of bounds报错&#xff0c;是由于Prometheus向tsdb存数据出错&#xff0c;与最新存数据的时间序列有问题&#xff0c;有可能当前时间与最…...

【音视频】Mesh、Mcu、SFU三种框架的总结

目录 三种网络场景介绍 【Mesh】 【MCU】(MultiPoint Control Unit) 【SFU】(Selective Forwarding Unit) 三种网络架构的优缺点 Mesh架构 MCU架构(MultiPoint Control Unit) SFU架构(Selective Forwarding Unit) 总结 参考文章 三种网络场景介绍 【Mesh】 Mesh架构…...

高级算法设计与分析(四) -- 贪心算法

系列文章目录 高级算法设计与分析&#xff08;一&#xff09; -- 算法引论 高级算法设计与分析&#xff08;二&#xff09; -- 递归与分治策略 高级算法设计与分析&#xff08;三&#xff09; -- 动态规划 高级算法设计与分析&#xff08;四&#xff09; -- 贪心算法 高级…...

MATLAB - 机器人逆运动学设计器(Inverse Kinematics Designer APP)

系列文章目录 前言 一、简介 通过逆运动学设计器&#xff0c;您可以为 URDF 机器人模型设计逆运动学求解器。您可以调整逆运动学求解器并添加约束条件&#xff0c;以实现所需的行为。使用该程序&#xff0c;您可以 从 URDF 文件或 MATLAB 工作区导入 URDF 机器人模型。调整逆…...

使用OpenCV DNN模块进行人脸检测

内容的一部分来源于贾志刚的《opencv4应用开发、入门、进阶与工程化实践》。这本书我大概看了一下&#xff0c;也就后面几章比较感兴趣&#xff0c;但是内容很少&#xff0c;并没有想像的那种充实。不过学习还是要学习的。 在实际工程项目中&#xff0c;并不是说我们将神经网络…...

C#中使用OpenCV的常用函数

以下是一些C#中使用OpenCV的常用函数例子&#xff1a; 1. 加载图像&#xff1a; using OpenCvSharp;Mat image Cv2.ImRead("path_to_your_image.jpg", ImreadModes.Color); 2. 显示图像&#xff1a; Cv2.NamedWindow("Image Window", WindowFlags.Nor…...

使用Swift Package Manager (SPM)实现xcframework分发

Swift Package Manager (SPM) 是苹果官方提供的用于管理 Swift 项目的依赖关系和构建过程的工具。它是一个集成在 Swift 编程语言中的包管理器&#xff0c;用于解决在开发过程中管理和构建包依赖项的需求。 1、上传xcframework.zip到服务端 压缩xcframeworks成一个zip包&…...

非阻塞 IO(NIO)

文章目录 非阻塞 IO(NIO)模型驱动程序应用程序模块使用 非阻塞 IO(NIO) 上一节中 https://blog.csdn.net/tyustli/article/details/135140523&#xff0c;使用等待队列头实现了阻塞 IO 程序使用时&#xff0c;阻塞 IO 和非阻塞 IO 的区别在于文件打开的时候是否使用了 O_NONB…...

Android应用-flutter使用Positioned将控件定位到底部中间

文章目录 场景描述示例解释 场景描述 要将Positioned定位到屏幕底部中间的位置&#xff0c;你可以使用MediaQuery来获取屏幕的高度&#xff0c;然后设置Positioned的bottom属性和left或right属性&#xff0c;一般我们left和right都会设置一个值让控制置于合适的位置&#xff0…...

Django 简单图书管理系统

一、图书需求 1. 书籍book_index.html中有超链接&#xff1a;查看所有的书籍列表book_list.html页面 2. 书籍book_list.html中显示所有的书名&#xff0c;有超链接&#xff1a;查看本书籍详情book_detail.html(通过书籍ID)页面 3. 书籍book_detail.html中书的作者和出版社&…...

C++内存管理和模板初阶

C/C内存分布 请看代码&#xff1a; int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] { 1, 2, 3, 4 };char char2[] "abcd";const char* pChar3 "abcd";int* ptr1 (int*)mallo…...

QtRO(Qt Remote Objects)分布式对象远程通信

一、什么是QtRO Qt Remote Objects&#xff08;QRO&#xff09;是Qt提供的一种用于实现远程对象通信的机制。 QtRO支持两种类型的通信&#xff1a;RPC&#xff08;远程过程调用&#xff09;和LPC&#xff08;本地进程通信&#xff09;。 RPC&#xff08;远程过程调用&#xf…...

【K8s】1# 使用kuboard-spray安装K8s集群

文章目录 搭建k8s集群1.推荐配置1.1.服务器配置1.2.软件版本 2.使用Kuboard-Spray安装k8s集群2.1.配置要求2.2.操作系统兼容性2.3.安装 Kuboard-Spray2.4.加载离线资源包2.5.规划并安装集群2.6.安装成功2.7.访问集群 3.涉及的命令3.1.linux 4.问题汇总Q1&#xff1a;启动离线集…...

leetCode算法—12. 整数转罗马数字

12. 整数转罗马数字 难度&#xff1a;中等 ** 罗马数字包含以下七种字符&#xff1a; I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如&#xff0c; 罗马数字 2 写做 II &#xff0c;即…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...