Unlocking Redis: 9 Real‑World Patterns from KV Cache to Bloom Filters
This article walks through nine practical Redis use cases—including basic KV caching, distributed locking, delayed queues, rate limiting, service discovery, bitmap storage, HyperLogLog counting, roaring bitmaps, and Bloom filters—explaining the underlying concepts, configuration tips, and code examples for robust backend development.
Business Background
The author shares nine classic Redis scenarios encountered in daily backend development, aiming to help engineers apply Redis's advanced features effectively.
KV Cache
Redis is used to cache user, session, and product data. Setting an appropriate expiration time aligns with a user's session length and prevents stale data.
def get_user(user_id):
user = redis.get(user_id)
if not user:
user = db.get(user_id)
redis.setex(user_id, ttl, user) # set expiration
return user
def save_user(user):
redis.setex(user.id, ttl, user) # set expiration
db.save_async(user) # async write to DBWhen memory usage grows, scaling is achieved with Codis or Redis‑Cluster.
Redis provides several eviction policies. The author’s production uses allkeys‑lru , which evicts any key based on recent usage, while volatile‑lru only evicts keys with an expiration.
Distributed Lock
A simple lock is implemented with SET key value NX EX ttl. The lock must store an owner_id to ensure only the lock holder can release it, avoiding deadlocks caused by process crashes.
# acquire lock
set lock:$user_id owner_id nx ex=5
# release lock (Lua script)
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
endAlthough Redis’ official recommendation is RedLock, the author prefers the simple lock for lower operational cost.
Delayed Queue
When a lock conflict occurs, the task is pushed to a delayed queue implemented with a sorted set (zset) where the score is the future execution timestamp.
# produce delayed message
zadd queue-key now_ts+5 task_json
# consume delayed message
while True:
task_json = zrevrangebyscore(queue-key, now_ts, 0, 0, 1)
if task_json:
if zrem(queue-key, task_json):
process_task(task_json)
else:
sleep(1)To avoid race conditions in multi‑threaded consumers, the polling and removal are wrapped in a Lua script for atomicity.
Rate Limiting
Rate limiting is achieved by storing each user’s actions in a zset, trimming entries older than the time window, and counting the remaining members.
hist_key = "ugc:${user_id}"
with redis.pipeline() as pipe:
pipe.zadd(hist_key, ts, uuid)
pipe.zremrangebyscore(hist_key, 0, now_ts-3600)
pipe.zcard(hist_key)
pipe.expire(hist_key, 3600)
_, _, count, _ = pipe.execute()
return count > NService Discovery
Services register their heartbeat in a zset ( zadd service_key heartbeat_ts address). Consumers poll the zset and clean up entries that haven’t refreshed within a timeout.
zadd service_key heartbeat_ts addr
zrem service_key addr
zremrangebyscore service_key 0 now_ts-30 # remove stale entriesA version key is incremented on any change, allowing clients to poll a global version and reload only affected service lists.
Bitmap for Sign‑In
Initially, sign‑in data was stored in hashes, which consumed excessive memory. Switching to a bitmap reduced storage to a few bytes per user per month.
# set sign‑in bits (2 bits per day)
setbit sign:${user_id} ${offset} ${value}For very sparse large bitmaps, a roaring bitmap (segment‑based) further compresses storage.
HyperLogLog (Fuzzy Counting)
HyperLogLog provides approximate unique counts with ~0.81% error while using only ~12KB, ideal for UV statistics.
pfadd sign_uv_${day} user_id
pfcount sign_uv_${day}Bloom Filter
A Bloom filter offers a space‑efficient probabilistic set to filter out non‑existent users, reducing cache‑penetration queries.
def get_user_state(user_id):
if not bloomfilter.is_user_exists(user_id):
return {}
return get_user_state0(user_id)
def save_user_state(user_id, state):
bloomfilter.set_user_exists(user_id)
save_user_state0(user_id, state)Overall, the article demonstrates how Redis can replace many specialized components—locks, queues, rate limiters, discovery services, and probabilistic data structures—while keeping operations simple and performant.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
