Build a Memory-Efficient Sign-In System with Redis BitMap and Spring Boot
This article explains how to implement a memory-efficient sign-in system and continuous-sign-in statistics using Redis BitMap, covering basic BitMap commands, Spring Boot integration, core source code, and a BitMap-based approach to mitigate cache-penetration attacks.
Introduction
Many projects need a sign-in and statistics feature; using Redis BitMap can store daily sign-in flags with minimal memory.
1. Redis BitMap Basics
Key commands
SETBIT – set a bit at a given offset
GETBIT – get the bit value at an offset
BITCOUNT – count bits set to 1
BITFIELD – read or modify arbitrary bit fields
BITFIELD_RO – read bit fields as unsigned integers
BITOP – perform bitwise operations on multiple BitMaps
BITPOS – find first occurrence of 0 or 1
Using a BitMap, each user's daily sign-in can be represented by a single bit; 31 days fit into 2 bytes, dramatically reducing memory compared with a relational table.
2. Spring Boot Integration – Sign-In API
Requirement
Store the current user's sign-in status for the day in Redis.
Key design: sign:{userId}:{yyyyMM}, where each bit corresponds to a day of the month.
Core source code
@PostMapping("sign")
public Result sign() {
return userService.sign();
} public Result sign() {
Long userId = UserHolder.getUser().getId();
LocalDateTime now = LocalDateTime.now();
String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;
int dayOfMonth = now.getDayOfMonth();
stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
return Result.ok();
}3. Sign-In Statistics – Continuous Days
Problem: How to calculate consecutive sign-in days?
Retrieve the month's BitMap as an unsigned integer, then iterate from the current day backward, counting consecutive 1 bits until a 0 is encountered.
Core source code
@GetMapping("/signCount")
public Result signCount() {
return userService.signCount();
} public Result signCount() {
Long userId = UserHolder.getUser().getId();
LocalDateTime now = LocalDateTime.now();
String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;
int dayOfMonth = now.getDayOfMonth();
List<Long> result = stringRedisTemplate.opsForValue().bitField(
key,
BitFieldSubCommands.create()
.get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
if (result == null || result.isEmpty() || result.get(0) == null) {
return Result.ok(0);
}
long num = result.get(0);
int count = 0;
while (true) {
if ((num & 1) == 0) {
break;
} else {
count++;
}
num >>>= 1;
}
return Result.ok(count);
}4. Using BitMap to Mitigate Cache Penetration
Instead of storing a large list of existing IDs, a BitMap can represent the presence of an ID with a single bit calculated by id % bitmapSize. This reduces memory usage but introduces a false‑positive rate due to hash collisions, which must be considered.
Conclusion
Combining Spring Boot with Redis BitMap provides an efficient way to implement sign-in and statistical features, saving memory and enabling quick queries for daily activity and continuous sign-in streaks.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
