本文目录导读:

我将为您提供一个完整的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;
}
}
这个分布式缓存案例包含了:
- Redis基本缓存操作
- Spring Cache注解支持
- 缓存击穿防护(互斥锁)
- 多级缓存(本地+Redis)
- 缓存工具类封装
- 缓存监控
根据实际需求,您可以选择合适的方案组合使用,建议在高并发场景下考虑使用二级缓存,在数据一致性要求高的场景注意缓存策略的设计。