白色衬衫净少年 - bitmap
type: channel
link: https://bscsjsn.com/tag/bitmap/
items: Redis中BitMap应用场景及实现

Redis中BitMap应用场景及实现
type: item
date: 2025-04-23T07:28:00+00:00
description: Redis BitMap是一种高效的位操作数据结构，它将字符串看作是由二进制位组成的数组。 在Redis中，一个BitMap最大可存储2^32个位，约512MB，而操作单个位的时间复杂度为O(1)。 这种结构在处理海量数据的布尔型状态时尤其高效，能在极小的内存占用下完成高性能的统计与分析任务。 Redis BitMap基础 基本概念 BitMap本质上是一个位数组，数组的每个元素只能是0或1。在Redis中，BitMap是基于String类型实现的，一个字符串的每个字节(8位)可以表示8个不同位，从而实现了位数组的功能 核心命令 Redis提供了一系列操作BitMap的命令： SETBIT key offset value : 设置key在offset处的位值 GETBIT key offset : 获取key在offset处的位值 BITCOUNT key [start end] : 统计指定范围内1的数量 BITPOS key bit [start end] : 返回第一个被设置为bit值的位的位置 BITOP operation destkey key [key ...] : 对多个BitMap执行位操作(AND, OR, XOR, NOT) BITFIELD key [GET type offset] [SET type offset value] : 原子操作多个位域 应用场景1：用户签到系统 场景描述 在许多应用中，需要记录用户每天是否签到，并支持查询用户连续签到天数、当月签到总天数等统计功能。 传统的方案可能使用关系型数据库存储每日签到记录，但这种方式既耗费存储空间，查询效率也低。 BitMap解决方案 使用BitMap，我们可以用一个位表示一天的签到状态，一个月只需30-31位，非常节省空间。 实现示例 import redis.clients.jedis.Jedis; import java.time.LocalDate; import java.time.format.DateTimeFormatter; public class SignInSystem { private Jedis jedis; private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyyMM"); public SignInSystem(String host, int port) { this.jedis = new Jedis(host, port); } // 用户签到 public void signIn(long userId, LocalDate date) { String signKey = getSignKey(userId, date); int dayOfMonth = date.getDayOfMonth() - 1; // Redis BitMap是0-based jedis.setbit(signKey, dayOfMonth, true); } // 检查用户是否签到 public boolean hasSignedIn(long userId, LocalDate date) { String signKey = getSignKey(userId, date); int dayOfMonth = date.getDayOfMonth() - 1; return jedis.getbit(signKey, dayOfMonth); } // 获取用户当月签到次数 public long getMonthlySignCount(long userId, LocalDate date) { String signKey = getSignKey(userId, date); return jedis.bitcount(signKey); } // 获取用户当月首次签到日期 public int getFirstSignInDay(long userId, LocalDate date) { String signKey = getSignKey(userId, date); long pos = jedis.bitpos(signKey, true); return pos == -1 ? -1 : (int) pos + 1; // 转换回自然日 } // 获取用户当月连续签到天数 public int getConsecutiveSignDays(long userId, LocalDate date) { String signKey = getSignKey(userId, da…