Java实现分布式缓存案例

wen java案例 2

本文目录导读:

Java实现分布式缓存案例

  1. 基于Redis的分布式缓存实现
  2. 缓存击穿解决方案 - 互斥锁
  3. 使用Caffeine + Redis二级缓存
  4. 缓存工具类封装
  5. 监控和统计

我将为您提供一个完整的Java分布式缓存实现案例,包含多种主流方案。

基于Redis的分布式缓存实现

Maven依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

Redis配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        // 使用String序列化器处理key
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        // 使用Jackson序列化器处理value
        GenericJackson2JsonRedisSerializer jacksonSerializer = new GenericJackson2JsonRedisSerializer();
        template.setValueSerializer(jacksonSerializer);
        template.setHashValueSerializer(jacksonSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

缓存服务实现

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class CacheServiceImpl implements CacheService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private ObjectMapper objectMapper;
    private static final String CACHE_PREFIX = "cache:";
    private static final long DEFAULT_EXPIRE_TIME = 3600; // 默认1小时
    @Override
    public void set(String key, Object value) {
        set(key, value, DEFAULT_EXPIRE_TIME);
    }
    @Override
    public void set(String key, Object value, long expireSeconds) {
        try {
            redisTemplate.opsForValue().set(CACHE_PREFIX + key, value, expireSeconds, TimeUnit.SECONDS);
        } catch (Exception e) {
            // 异常处理,可以记录日志
            throw new CacheOperationException("Failed to set cache: " + key, e);
        }
    }
    @Override
    public Object get(String key) {
        try {
            return redisTemplate.opsForValue().get(CACHE_PREFIX + key);
        } catch (Exception e) {
            throw new CacheOperationException("Failed to get cache: " + key, e);
        }
    }
    @Override
    public <T> T get(String key, Class<T> clazz) {
        Object value = get(key);
        if (value == null) return null;
        try {
            return objectMapper.convertValue(value, clazz);
        } catch (Exception e) {
            throw new CacheOperationException("Failed to convert cache value", e);
        }
    }
    @Override
    public boolean delete(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.delete(CACHE_PREFIX + key));
        } catch (Exception e) {
            throw new CacheOperationException("Failed to delete cache: " + key, e);
        }
    }
    @Override
    public boolean expire(String key, long timeout, TimeUnit timeUnit) {
        try {
            return Boolean.TRUE.equals(redisTemplate.expire(CACHE_PREFIX + key, timeout, timeUnit));
        } catch (Exception e) {
            throw new CacheOperationException("Failed to expire cache: " + key, e);
        }
    }
    @Override
    public boolean exists(String key) {
        try {
            return Boolean.TRUE.equals(redisTemplate.hasKey(CACHE_PREFIX + key));
        } catch (Exception e) {
            throw new CacheOperationException("Failed to check cache existence: " + key, e);
        }
    }
}

缓存管理接口

import java.util.concurrent.TimeUnit;
public interface CacheService {
    void set(String key, Object value);
    void set(String key, Object value, long expireSeconds);
    Object get(String key);
    <T> T get(String key, Class<T> clazz);
    boolean delete(String key);
    boolean expire(String key, long timeout, TimeUnit timeUnit);
    boolean exists(String key);
}
public class CacheOperationException extends RuntimeException {
    public CacheOperationException(String message, Throwable cause) {
        super(message, cause);
    }
}

使用Spring Cache注解

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
    // 查询用户并缓存
    @Cacheable(value = "users", key = "#userId")
    public User getUserById(Long userId) {
        return queryUserFromDatabase(userId);
    }
    // 更新用户缓存
    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
        updateUserInDatabase(user);
        return user;
    }
    // 删除用户缓存
    @CacheEvict(value = "users", key = "#userId")
    public void deleteUser(Long userId) {
        deleteUserFromDatabase(userId);
    }
    // 批量清空缓存
    @CacheEvict(value = "users", allEntries = true)
    public void clearAllUsers() {
        // 业务逻辑
    }
    private User queryUserFromDatabase(Long userId) {
        // 模拟数据库查询
        return new User(userId, "user" + userId);
    }
    private void updateUserInDatabase(User user) {
        // 模拟数据库更新
    }
    private void deleteUserFromDatabase(Long userId) {
        // 模拟数据库删除
    }
}

缓存击穿解决方案 - 互斥锁

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class CachePenetrationService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    private static final String LOCK_PREFIX = "lock:";
    public Object getDataWithMutex(String key, long expireTime) {
        // 从缓存获取数据
        Object data = redisTemplate.opsForValue().get(key);
        if (data != null) {
            return data;
        }
        // 尝试获取分布式锁
        String lockKey = LOCK_PREFIX + key;
        String requestId = UUID.randomUUID().toString();
        boolean gotLock = redisTemplate.opsForValue()
                .setIfAbsent(lockKey, requestId, 10, TimeUnit.SECONDS);
        if (gotLock) {
            try {
                // 再次检查缓存(双重检查锁)
                data = redisTemplate.opsForValue().get(key);
                if (data != null) {
                    return data;
                }
                // 查询数据库
                data = queryFromDatabase(key);
                // 写入缓存
                redisTemplate.opsForValue().set(key, data, expireTime, TimeUnit.SECONDS);
                return data;
            } finally {
                // 释放锁
                releaseLock(lockKey, requestId);
            }
        } else {
            // 等待重试
            try {
                Thread.sleep(100);
                return getDataWithMutex(key, expireTime);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return null;
            }
        }
    }
    private void releaseLock(String lockKey, String requestId) {
        // 使用Lua脚本原子性释放锁
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                        "return redis.call('del', KEYS[1]) " +
                        "else return 0 end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId);
    }
    private Object queryFromDatabase(String key) {
        // 模拟数据库查询
        return new Object();
    }
}

使用Caffeine + Redis二级缓存

Maven依赖

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>3.1.8</version>
</dependency>

二级缓存实现

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class TwoLevelCacheService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    // 本地缓存(一级缓存)
    private Cache<String, Object> localCache;
    public TwoLevelCacheService() {
        // 初始化本地缓存
        localCache = Caffeine.newBuilder()
                .maximumSize(10000)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .recordStats()
                .build();
    }
    public Object get(String key) {
        // 1. 先从本地缓存获取
        Object value = localCache.getIfPresent(key);
        if (value != null) {
            return value;
        }
        // 2. 本地缓存未命中,从Redis获取
        value = redisTemplate.opsForValue().get(key);
        if (value != null) {
            // 写回本地缓存
            localCache.put(key, value);
            return value;
        }
        // 3. Redis未命中,查询数据库
        value = queryFromDatabase(key);
        if (value != null) {
            // 写入两级缓存
            redisTemplate.opsForValue().set(key, value, 1, TimeUnit.HOURS);
            localCache.put(key, value);
        }
        return value;
    }
    public void put(String key, Object value) {
        // 更新两级缓存
        redisTemplate.opsForValue().set(key, value, 1, TimeUnit.HOURS);
        localCache.put(key, value);
    }
    public void evict(String key) {
        // 删除两级缓存
        redisTemplate.delete(key);
        localCache.invalidate(key);
    }
    public void clear() {
        // 清空所有缓存
        Set<String> keys = redisTemplate.keys("*");
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
        localCache.invalidateAll();
    }
    private Object queryFromDatabase(String key) {
        // 模拟数据库查询
        return new Object();
    }
}

缓存工具类封装

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
@Component
public class CacheUtils {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 带失效时间的缓存获取
     */
    public Object getOrLoad(String key, Supplier<Object> loader, long expireSeconds) {
        Object value = redisTemplate.opsForValue().get(key);
        if (value != null) {
            return value;
        }
        synchronized (key.intern()) {
            // 双重检查
            value = redisTemplate.opsForValue().get(key);
            if (value != null) {
                return value;
            }
            value = loader.get();
            if (value != null) {
                redisTemplate.opsForValue().set(key, value, expireSeconds, TimeUnit.SECONDS);
            }
            return value;
        }
    }
    /**
     * 布隆过滤器防止缓存穿透
     */
    public boolean mightContain(String key) {
        // 使用Redis的BitMap实现简单的布隆过滤器
        return Boolean.TRUE.equals(redisTemplate.opsForValue()
                .getBit("bloom:" + key.hashCode(), hash(key)));
    }
    private long hash(String key) {
        return Math.abs(key.hashCode() % 1024);
    }
}

监控和统计

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class CacheMonitorService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    public Map<String, Object> getCacheStats() {
        Map<String, Object> stats = new HashMap<>();
        // 获取Redis内存信息
        Properties memoryInfo = redisTemplate.getConnectionFactory()
                .getConnection().info("memory");
        stats.put("memory", memoryInfo);
        // 获取所有缓存key数量
        Set<String> keys = redisTemplate.keys("cache:*");
        stats.put("cacheKeys", keys != null ? keys.size() : 0);
        // 其他统计信息
        return stats;
    }
}

这个分布式缓存案例包含了:

  1. Redis基本缓存操作
  2. Spring Cache注解支持
  3. 缓存击穿防护(互斥锁)
  4. 多级缓存(本地+Redis)
  5. 缓存工具类封装
  6. 缓存监控

根据实际需求,您可以选择合适的方案组合使用,建议在高并发场景下考虑使用二级缓存,在数据一致性要求高的场景注意缓存策略的设计。

抱歉,评论功能暂时关闭!