SuperAPI AI Directory

redis-redisson-patterns

Coding & development

View repository

Updated 10-15 21:48

About this skill

Redis + Redisson caching and distributed locks

Source:https://redisson.org/docs/
GitHub:https://github.com/redisson/redisson
Redis:https://redis.io/documentation

Capability Boundaries

✅ Strong Suits

  1. Cache key conventions — format {业务名}:{类型}:{ID}, unified prefix
  2. Expiration time principles — long TTL for hot data, short TTL for non-hot data, never set -1
  3. Serialization choices — comparison and configuration of the three options: JSON vs JDK vs String
  4. Redisson distributed locks — tryLock/watchDog/leaseTime/reentrant/fair lock/RedLock
  5. Protection against the three major cache problems — penetration (Bloom filter/null value)/breakdown (mutex lock)/avalanche (random TTL)
  6. Caffeine vs Redis selection — when to use local cache and when to use Redis
  7. Correct RedisTemplate configuration — unified Key/Value serialization

❌ Out of Scope

  1. Message queue/pub-sub → kafka / RabbitMQ
  2. Spring Session sharing → spring-security
  3. Jedis/Lettuce client selection → this document recommends Lettuce (Spring Boot default)

Serialization choice decision

Option Pros Cons Applicable scenarios
StringRedisTemplate Simple, readable Can only store String, objects need manual serialization Pure string caching
RedisTemplate + Jackson2JsonRedisSerializer Readable, cross-language Slightly lower performance ✅ Recommended (suitable for most scenarios)
RedisTemplate + JdkSerializationRedisSerializer Default (includes type information) Unreadable, large size, Java-only ❌ Avoid (it is the default)
RedisTemplate + GenericJackson2JsonRedisSerializer Contains @class type Many redundant fields Caching complex objects

The mistakes LLMs make most often

# Mistake Correct approach
1 Cache has no expiration time (permanent) TTL must be set to prevent memory overflow
2 Mixing StringRedisTemplate and RedisTemplate Use one consistently, RedisTemplate + JSON serialization is recommended
3 Cache penetration (querying non-existent data goes straight through to DB) Null value caching (Boolean.FALSE, TTL 5 minutes) or Bloom filter
4 Using Redis for high-frequency hot caching (over 1000 qps each time) Add a Caffeine local cache layer (L1 local + L2 Redis)
5 lock.lock() without try-finally must finally { lock.unlock() }
6 Cache keys have no naming convention Unified format:saas:user:10001、saas:order:list
7 Redisson specifies leaseTime but still expects the watchdog to renew When leaseTime is specified, the watchdog does not take effect!
8 Cache avalanche is not handled (a large number of keys expire at the same time) Add a random value to TTL (±30%)
9 Hot key cache breakdown Mutex lock or never expire + asynchronous update
10 Using RedisTemplate default JDK serialization Change to JSON serialization (readable, cross-language)

Core patterns

Pattern 1: Unified RedisTemplate configuration (JSON serialization)

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        // ✅ 推荐:Jackson2JsonRedisSerializer
        Jackson2JsonRedisSerializer<Object> serializer =
            new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LazySerializationTypeValidator.getDefaultTyper(),
            ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);

        template.setKeySerializer(new StringRedisSerializer());     // Key: String
        template.setValueSerializer(serializer);                    // Value: JSON
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }
}

Pattern 2: Cache penetration protection (null value caching)

public User getUser(Long id) {
    String key = String.format("saas:user:%s", id);
    User user = (User) redisTemplate.opsForValue().get(key);

    // 空值标记,直接返回
    if (user == NULL_PLACEHOLDER) return null;
    // 缓存命中
    if (user != null) return user;

    // 查询DB
    user = userMapper.selectById(id);
    if (user == null) {
        // ✅ 空值缓存(防穿透),TTL 5分钟
        redisTemplate.opsForValue().set(key, NULL_PLACEHOLDER, 5, TimeUnit.MINUTES);
        return null;
    }
    // ✅ 正常缓存,30分钟
    redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES);
    return user;
}

Pattern 3: The three major Redisson distributed lock patterns

Pattern A: tryLock + watchDog (recommended) — has wait time + automatic renewal

RLock lock = redissonClient.getLock("lock:order:" + orderId);
// ✅ 只指定 waitTime,不指定 leaseTime → watchDog 生效(默认30s续期)
if (lock.tryLock(10, TimeUnit.SECONDS)) {  // 等10秒获取锁
    try {
        // 业务处理(可能超过30秒,watchDog自动续期)
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

Pattern B: tryLock + leaseTime — hard timeout (watchdog does not take effect)

RLock lock = redissonClient.getLock("lock:order:" + orderId);
// ⚠️ 指定了 leaseTime → watchDog 不生效
if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
    try {
        // 必须在30秒内完成,否则锁自动释放
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

Pattern C: lock() — blocking wait + watchDog

RLock lock = redissonClient.getLock("lock:order:" + orderId);
lock.lock();  // 阻塞直到获取锁,watchDog 生效
try {
    // 业务处理
} finally {
    if (lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

Pattern 4: Cache avalanche protection (random TTL)

// 相同业务场景的缓存 TTL 加随机值,避免同时过期
int baseTtl = 3600;  // 1小时
int randomTtl = baseTtl + new Random().nextInt(600);  // +0~10分钟随机
redisTemplate.opsForValue().set(key, value, randomTtl, TimeUnit.SECONDS);

Pattern 5: Cache Key conventions

// 命名格式: {业务}:{领域}:{实体}:{唯一标识}
private static final String USER_CACHE_KEY = "saas:user:detail:%s";
private static final String ORDER_LIST_KEY = "saas:order:list:%s:%d"; // userId + page
private static final String CONFIG_KEY = "saas:config:global";

String key = String.format(USER_CACHE_KEY, userId);

Gotchas

  1. Caches must have an expiration time set — never setting a TTL = memory leak
  2. Redisson lock must be unlocked with try-finally — otherwise deadlock
  3. watchDog automatically renews the lease — when leaseTime is not specified, defaults to 30s, renewing every 10s
  4. If leaseTime is specified, the watchdog does not take effect — if you need automatic renewal, do not specify leaseTime
  5. Redis Cluster does not support batch mget (cross-Slot) — switch to a loop of get or use hash tags to pin the Slot
  6. Cache avalanche: a large number of Keys expire at the same time — add a random value to the TTL (±30%)
  7. Cache breakdown: a hot Key expires and a large number of requests penetrate at that instant — mutex lock or never expire + asynchronous update
  8. Caffeine is better suited for high-frequency caching in a single service — Redis is used for distributed sharing, Caffeine is used for local acceleration
  9. isHeldByCurrentThread() must be checked before unlock — to avoid IllegalMonitorStateException
  10. RedisTemplate defaults to JDK serialization — switch to JSON serialization (readable, cross-language)
  11. Lettuce (Spring Boot default) is more recommended than Jedis — asynchronous, connection pooling, active community
  12. Bloom filter (RedissonBloomFilter) is used to prevent penetration — suitable for a large number of keys and when 100% accuracy is not required

Data Privacy

This skill does not collect, store, or transmit any user data.