11 Essential Redis Use Cases Every Backend Engineer Should Know
This article walks through eleven practical Redis scenarios—from classic caching and distributed locks to rate limiting, leaderboards, timelines, social graph operations, lightweight queues, Bloom filters, hash‑based object storage, unique‑counting, and delayed tasks—providing code samples, advantages, drawbacks, and when to apply each pattern.
Scenario 1: High‑speed Cache (Classic)
Business case
Cache frequently read data such as product details or user profiles to offload read pressure from the database.
Code example (Cache‑aside pattern)
@Service
public class ProductService {
@Autowired
private RedisTemplate<String, Product> redisTemplate;
@Autowired
private ProductMapper productMapper;
public Product getProduct(Long id) {
String cacheKey = "product:" + id;
// 1. read cache
Product product = redisTemplate.opsForValue().get(cacheKey);
if (product != null) {
return product;
}
// 2. cache miss, query DB
product = productMapper.selectById(id);
if (product != null) {
// 3. write cache with random expiration to avoid cache avalanche
int expire = 300 + new Random().nextInt(60);
redisTemplate.opsForValue().set(cacheKey, product, expire, TimeUnit.SECONDS);
}
return product;
}
}Pros : Latency drops from tens of milliseconds to sub‑millisecond; handles extreme concurrency.
Cons : Short‑term data inconsistency (eventual consistency).
Applicable : Read‑heavy, write‑light data such as product details, configuration, user sessions.
Scenario 2: Distributed Lock
Business case
Seckill inventory deduction, order deduplication, scheduled‑task idempotency—situations where multiple processes compete for the same resource need a global lock.
Recommended solution: Redisson (auto‑renewal, re‑entrant)
@Service
public class SeckillService {
@Autowired
private RedissonClient redissonClient;
public void doSeckill(Long userId, Long productId) {
String lockKey = "lock:seckill:" + productId;
RLock lock = redissonClient.getLock(lockKey);
try {
// try lock, wait up to 3 seconds, lock expires after 30 seconds (watchdog auto‑renewal)
if (lock.tryLock(3, TimeUnit.SECONDS)) {
int stock = getStock(productId);
if (stock > 0) {
updateStock(productId, stock - 1);
} else {
throw new RuntimeException("已售罄");
}
} else {
throw new RuntimeException("系统繁忙,请稍后再试");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}⚠️ Newcomer pitfall: Using SET NX EX directly often forgets renewal or release. Strongly recommend Redisson.
Pros : Auto‑renewal, re‑entrant, works with master‑slave, sentinel, or cluster.
Cons : Adds an extra client library, slightly heavier than raw commands.
Applicable : Any operation that requires mutual exclusion across distributed nodes.
Scenario 3: Counter & Rate Limiter
Business case
Track page views, API call counts, SMS sending frequency.
Fixed‑window rate limiting (simplest implementation)
public boolean isAllowed(String userId) {
String key = "rate:sendSms:" + userId;
Long count = redisTemplate.opsForValue().increment(key);
if (count == 1) {
redisTemplate.expire(key, 60, TimeUnit.SECONDS);
}
return count <= 5; // max 5 requests per minute
}Pros : Atomic operation, extremely fast.
Cons : Fixed window can cause burst spikes at boundaries; sliding‑window (ZSET) is stricter.
Applicable : API throttling, SMS verification, login‑error limits.
Scenario 4: Leaderboard / Sorted Set
Business case
Game scoreboards, hot‑search lists, sales rankings.
Using Sorted Set
// Add player scores
redisTemplate.opsForZSet().add("rank:game:202605", "player:1001", 9980);
redisTemplate.opsForZSet().add("rank:game:202605", "player:1002", 10200);
// Get top 3 (descending)
Set<ZSetOperations.TypedTuple<String>> top3 =
redisTemplate.opsForZSet().reverseRangeWithScores("rank:game:202605", 0, 2);Pros : Automatic ordering, O(logN) query speed.
Cons : Memory grows linearly with data size.
Applicable : Real‑time leaderboards, hot articles, product hot‑sale lists.
Scenario 5: Latest Feed / Timeline
Business case
Weibo‑style timelines, activity feeds, recent comments.
Using List (LPUSH + LTRIM)
// Publish a new feed item
String feedKey = "feeds:user:" + userId;
redisTemplate.opsForList().leftPush(feedKey, JSON.toJSONString(post));
// Keep only the latest 100 items
redisTemplate.opsForList().trim(feedKey, 0, 99);Pros : Fast writes, easy pagination.
Cons : No complex sorting; only chronological reverse order.
Applicable : Simple "latest N" lists such as user activity or notifications.
Scenario 6: Social Graph (Set Operations)
Business case
Mutual follows, friend recommendations, people you may know.
// Users followed by A
redisTemplate.opsForSet().add("follow:1001", "1002", "1003", "1004");
// Users followed by B
redisTemplate.opsForSet().add("follow:1002", "1003", "1005");
// Mutual follows
Set<String> intersect = redisTemplate.opsForSet().intersect("follow:1001", "follow:1002");
// Recommend follows (B follows but A does not)
Set<String> diff = redisTemplate.opsForSet().difference("follow:1002", "follow:1001");Pros : Set operations run in milliseconds; code is concise.
Cons : Very large keys (e.g., millions of followers) can cause slow queries.
Applicable : Social relationships, permission tags, black/white lists.
Scenario 7: Lightweight Message Queue (Stream / List)
Business case
Asynchronous processing, traffic smoothing, log collection.
Using Redis 5.0+ Stream with consumer groups
// Producer
Map<String, String> msg = new HashMap<>();
msg.put("orderId", "ORD-123");
redisTemplate.opsForStream().add("order:stream", msg);
// Consumer
List<MapRecord<String, Object, Object>> records =
redisTemplate.opsForStream().read(
Consumer.from("order-group", "c1"),
StreamReadOptions.empty().count(10),
StreamOffset.create("order:stream", ReadOffset.lastConsumed()));Pros : No extra components needed; supports ACK and consumer groups.
Cons : Persistence reliability is lower than dedicated MQs like RocketMQ or Kafka.
Applicable : Internal task dispatch, async notifications, scenarios without strict ordering requirements.
Scenario 8: Bloom Filter (Cache‑Penetration Protection)
Business case
Block malicious requests, filter definitely non‑existent data, avoid unnecessary DB hits.
Using Redisson Bloom Filter
RBloomFilter<String> bloom = redissonClient.getBloomFilter("filter:productId");
// Initialize with capacity 1,000,000 and 1% false‑positive rate
bloom.tryInit(1000000L, 0.01);
// Populate with legitimate IDs at startup
for (Long id : allProductIds) {
bloom.add(id.toString());
}
// Check before DB query
if (!bloom.contains(productId.toString())) {
return null; // skip DB
}Pros : Extremely low memory (≈120 MB for 100 M entries).
Cons : Possibility of false positives (better to err on the side of blocking).
Applicable : Prevent cache penetration, deduplicate crawler IPs, blacklist filtering.
Scenario 9: Lightweight NoSQL (Hash for Object Storage)
Business case
Store dynamic attributes, user sessions, configuration data.
// Store user info
Map<String, String> user = new HashMap<>();
user.put("name", "张三");
user.put("age", "28");
redisTemplate.opsForHash().putAll("user:1001", user);
// Get a single field
String name = (String) redisTemplate.opsForHash().get("user:1001", "name");
// Update a single field
redisTemplate.opsForHash().put("user:1001", "age", "29");Pros : Saves memory compared with full JSON serialization; supports partial updates.
Cons : Not suitable for deeply nested structures.
Applicable : User profiles, shopping carts (field = product ID, value = quantity).
Scenario 10: De‑duplication / Uniqueness Check
Business case
UV counting, lottery participation, preventing duplicate submissions.
Using Set or HyperLogLog
// Method 1: Set (exact but memory‑heavy)
Boolean success = redisTemplate.opsForSet().add("draw:202605", userId);
if (Boolean.TRUE.equals(success)) {
// first participation
}
// Method 2: HyperLogLog (tiny memory, approximate)
redisTemplate.opsForHyperLogLog().add("uv:20260501", userId);
Long uv = redisTemplate.opsForHyperLogLog().size("uv:20260501");Pros : Set guarantees exactness; HyperLogLog uses ~12 KB memory.
Cons : HyperLogLog has ~0.81 % error, unsuitable for precise counts.
Applicable : Lottery de‑duplication, daily active user stats, crawler URL deduplication.
Scenario 11: Delayed Queue (Sorted Set)
Business case
Automatically cancel unpaid orders after 30 minutes, delayed notifications.
// Add delayed task (score = execution timestamp)
long execTime = System.currentTimeMillis() + 30 * 60 * 1000;
redisTemplate.opsForZSet().add("delay:order", orderId, execTime);
// Consumer scans periodically
Set<String> tasks = redisTemplate.opsForZSet().rangeByScore("delay:order", 0, System.currentTimeMillis());
for (String orderId : tasks) {
if (redisTemplate.opsForZSet().remove("delay:order", orderId) > 0) {
cancelOrder(orderId);
}
}Pros : Millisecond precision, simple implementation.
Cons : Requires periodic polling; performance degrades with large data volumes.
Applicable : Order timeout handling, scheduled reminders, session expiration cleanup.
Conclusion
Redis provides more than a simple cache. When ultra‑fast reads, distributed coordination, de‑duplication, ranking, or lightweight queuing are required, selecting the appropriate Redis data structure—String, Hash, List, Set, Sorted Set, Stream, Bloom Filter, or HyperLogLog—can yield dramatic efficiency gains.
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.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
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.
