Bitmap位图:节省内存的统计利器
Bitmap原理 本质:一串二进制位(0和1) 位图:[0][1][1][0][1][0][0][1] 索引: 0 1 2 3 4 5 6 7 1亿个位 = 1亿 bits = 12.5 MB 优势: 极致节省内存(1个用户1bit) 快速统计(位运算) 核心命令 # 设置位 SETBIT key offset value # 获取位 GETBIT key offset # 统计1的个数 BITCOUNT key [start end] # 位运算 BITOP AND|OR|XOR|NOT destkey key [key ...] # 查找第一个0或1 BITPOS key bit [start] [end] 实战案例 案例1:用户签到 @Service public class SignInService { @Autowired private RedisTemplate<String, Object> redis; // 签到 public void signIn(Long userId, LocalDate date) { String key = "sign:" + userId + ":" + date.format(DateTimeFormatter.ofPattern("yyyyMM")); int offset = date.getDayOfMonth() - 1; redis.opsForValue().setBit(key, offset, true); } // 检查是否签到 public boolean isSignedIn(Long userId, LocalDate date) { String key = "sign:" + userId + ":" + date.format(DateTimeFormatter.ofPattern("yyyyMM")); int offset = date.getDayOfMonth() - 1; return Boolean.TRUE.equals(redis.opsForValue().getBit(key, offset)); } // 统计本月签到天数 public Long countSignIn(Long userId, String month) { String key = "sign:" + userId + ":" + month; return redis.execute((RedisCallback<Long>) connection -> connection.bitCount(key.getBytes()) ); } // 连续签到天数 public int getContinuousSignInDays(Long userId) { LocalDate today = LocalDate.now(); String key = "sign:" + userId + ":" + today.format(DateTimeFormatter.ofPattern("yyyyMM")); int days = 0; for (int i = today.getDayOfMonth() - 1; i >= 0; i--) { Boolean signed = redis.opsForValue().getBit(key, i); if (Boolean.TRUE.equals(signed)) { days++; } else { break; } } return days; } } 案例2:用户在线状态 @Service public class OnlineStatusService { @Autowired private RedisTemplate<String, Object> redis; // 设置用户在线 public void setOnline(Long userId) { String key = "online:" + LocalDate.now(); redis.opsForValue().setBit(key, userId, true); redis.expire(key, 2, TimeUnit.DAYS); } // 设置用户离线 public void setOffline(Long userId) { String key = "online:" + LocalDate.now(); redis.opsForValue().setBit(key, userId, false); } // 检查用户是否在线 public boolean isOnline(Long userId) { String key = "online:" + LocalDate.now(); return Boolean.TRUE.equals(redis.opsForValue().getBit(key, userId)); } // 统计今日在线人数 public Long countOnlineUsers() { String key = "online:" + LocalDate.now(); return redis.execute((RedisCallback<Long>) connection -> connection.bitCount(key.getBytes()) ); } } 案例3:用户标签系统 @Service public class UserTagService { @Autowired private RedisTemplate<String, Object> redis; // 标签定义 private static final int TAG_VIP = 0; private static final int TAG_ACTIVE = 1; private static final int TAG_VERIFIED = 2; // 添加标签 public void addTag(Long userId, int tagId) { String key = "user:tags:" + userId; redis.opsForValue().setBit(key, tagId, true); } // 移除标签 public void removeTag(Long userId, int tagId) { String key = "user:tags:" + userId; redis.opsForValue().setBit(key, tagId, false); } // 检查标签 public boolean hasTag(Long userId, int tagId) { String key = "user:tags:" + userId; return Boolean.TRUE.equals(redis.opsForValue().getBit(key, tagId)); } // 查找VIP且活跃的用户 public Set<Long> findVIPActiveUsers(Set<Long> userIds) { Set<Long> result = new HashSet<>(); for (Long userId : userIds) { if (hasTag(userId, TAG_VIP) && hasTag(userId, TAG_ACTIVE)) { result.add(userId); } } return result; } } 案例4:布隆过滤器(简化版) @Service public class SimpleBloomFilter { @Autowired private RedisTemplate<String, Object> redis; private static final String KEY = "bloom_filter"; private static final int BIT_SIZE = 10000000; // 1000万位 // 添加元素 public void add(String element) { int hash1 = Math.abs(element.hashCode()) % BIT_SIZE; int hash2 = Math.abs((element + "salt").hashCode()) % BIT_SIZE; int hash3 = Math.abs((element + "salt2").hashCode()) % BIT_SIZE; redis.opsForValue().setBit(KEY, hash1, true); redis.opsForValue().setBit(KEY, hash2, true); redis.opsForValue().setBit(KEY, hash3, true); } // 检查元素是否存在 public boolean mightExist(String element) { int hash1 = Math.abs(element.hashCode()) % BIT_SIZE; int hash2 = Math.abs((element + "salt").hashCode()) % BIT_SIZE; int hash3 = Math.abs((element + "salt2").hashCode()) % BIT_SIZE; return Boolean.TRUE.equals(redis.opsForValue().getBit(KEY, hash1)) && Boolean.TRUE.equals(redis.opsForValue().getBit(KEY, hash2)) && Boolean.TRUE.equals(redis.opsForValue().getBit(KEY, hash3)); } } 位运算操作 多日期统计 # 连续3天都签到的用户 BITOP AND result sign:202501 sign:202502 sign:202503 BITCOUNT result # 3天内至少签到1次的用户 BITOP OR result sign:202501 sign:202502 sign:202503 BITCOUNT result Java实现: ...