Redis is often pigeonholed as just a caching layer, but it's a versatile data structure server that can solve many distributed systems problems. Here are 10 practical use cases with Spring Data Redis.
1. Distributed Rate Limiting
Scenario: Limit "Forgot Password" to 3 tries/hour.
String key = "rate:" + userId;
redis.opsForValue().setIfAbsent(key, "1", Duration.ofHours(1));
Long count = redis.opsForValue().increment(key);
if (count > 3) throw new RateLimitException();
2. Distributed Locks
Scenario: Prevent double-booking the last airline seat.
String uuid = UUID.randomUUID().toString();
Boolean locked = redis.opsForValue()
.setIfAbsent("lock:seat:" + id, uuid, Duration.ofSeconds(10));
if (Boolean.TRUE.equals(locked)) {
try {
bookSeat();
} finally {
// Lua script to delete lock ONLY if value == uuid
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) else return 0 end";
redis.execute(new DefaultRedisScript<>(script, Long.class),
Collections.singletonList("lock:seat:" + id), uuid);
}
}
3. Job Queues
Scenario: Async PDF invoice generation.
// Producer
redis.opsForList().rightPush("queue:invoices", orderId);
// Consumer (Blocking)
String job = redis.opsForList()
.leftPop("queue:invoices", 30, TimeUnit.SECONDS);
4. Real-Time Leaderboards
Scenario: Strava weekly step challenge rankings.
redis.opsForZSet().incrementScore("lb:steps", userId, stepsAdded);
Set<TypedTuple<String>> top10 = redis.opsForZSet()
.reverseRangeWithScores("lb:steps", 0, 9);
Long rank = redis.opsForZSet().reverseRank("lb:steps", userId);
5. Session Storage
Scenario: Share user sessions across multiple API pods.
redis.opsForValue().set("sess:" + tokenId, userJson, Duration.ofHours(8));
String user = redis.opsForValue().get("sess:" + tokenId);
6. Pub/Sub Messaging
Scenario: Uber "Driver is arriving" WebSocket push.
// Publisher
redis.convertAndSend("channel:ride:" + riderId, "Driver arriving");
// Subscriber (implements MessageListener)
public void onMessage(Message msg, byte[] p) {
pushToWebSocket(new String(msg.getBody()));
}
7. Feature Flags
Scenario: Instantly disable crypto trading without deploying code.
// Admin Toggle
redis.opsForHash().put("flags", "crypto_trade", String.valueOf(false));
// API Check
boolean isEnabled = Boolean.parseBoolean(
redis.opsForHash().get("flags", "crypto_trade").toString()
);
8. Health Monitoring
Scenario: PagerDuty alert if payment declines spike.
long bucket = System.currentTimeMillis() / 120_000; // 2-min bucket
String key = "mon:declines:" + bucket;
Long count = redis.opsForValue().increment(key);
if (count == 1) redis.expire(key, Duration.ofMinutes(2));
if (count == 50) triggerAlert();
9. Geospatial Search
Scenario: DoorDash finding 5 nearest drivers.
redis.opsForGeo().add("geo:drivers", new Point(lon, lat), driverId);
Circle circle = new Circle(
new Point(restLon, restLat),
new Distance(3, Metrics.KILOMETERS)
);
GeoResults<GeoLocation<String>> nearest = redis.opsForGeo()
.radius("geo:drivers", circle, args.limit(5));
10. Event Sourcing (Streams)
Scenario: Immutable banking transaction audit log.
Map<String, String> event = Map.of("account", "123", "amount", "50.00");
redis.opsForStream().add("stream:ledger", event); // Auto-generates ID
List<MapRecord<String, Object, Object>> history = redis.opsForStream()
.range("stream:ledger", Range.unbounded());
Key Takeaways
- Atomic operations: Redis provides atomic primitives for distributed coordination
- Data structures: Choose the right structure (String, Hash, List, Set, ZSet, Geo, Stream)
- TTL: Always set expiration to prevent memory leaks
- Performance: In-memory operations make Redis ideal for low-latency use cases
- Scalability: Single Redis instance can be replaced with Redis Cluster for horizontal scaling
Redis is more than a cache—it's a Swiss Army knife for distributed systems challenges.