一、Redisson介绍
Redisson是一个在Redis基础上实现的Java驻内存数据网格。它不仅提供了一系列的分布式和可扩展的Java数据结构,还提供了许多分布式服务。Redisson作为Redis的Java客户端,不仅仅是一个简单的Redis连接池,而是一个功能丰富的分布式和可扩展的Java数据结构集合。
Redisson的主要特点包括:
- 基于Netty框架实现,支持异步和同步操作
- 提供了分布式和可扩展的Java数据结构
- 内置高性能分布式缓存解决方案
- 支持Redis的多种部署模式(单机、主从、哨兵、集群)
- 内置了多种分布式锁实现
- 提供了丰富的分布式服务(如分布式调度任务、分布式MapReduce等)
二、Redisson的核心特性
分布式对象
Redisson提供了多种分布式对象,这些对象在Redis中都有对应的实现:
// 获取Redisson客户端实例
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
// 分布式对象示例
RList<String> list = redisson.getList("myList"); // 分布式List
RSet<String> set = redisson.getSet("mySet"); // 分布式Set
RMap<String, String> map = redisson.getMap("myMap"); // 分布式Map
RBucket<String> bucket = redisson.getBucket("myBucket"); // 分布式Bucket
三、Redisson缓存存储详解
Redisson提供了强大的分布式缓存解决方案,包括多种缓存策略、缓存加载机制和缓存事件监听等功能。
1. 基本缓存操作(RBucket)
RBucket是Redisson中最基本的缓存存储单元,用于存储单个对象:
// 获取Bucket对象
RBucket<User> userBucket = redisson.getBucket("user:1001");
// 存储对象到缓存
User user = new User(1001, "张三", "zhangsan@example.com");
userBucket.set(user);
// 设置过期时间(1小时后过期)
userBucket.set(user, 1, TimeUnit.HOURS);
// 从缓存获取对象
User cachedUser = userBucket.get();
System.out.println("从缓存获取用户: " + cachedUser);
// 删除缓存
userBucket.delete();
// 检查缓存是否存在
boolean exists = userBucket.isExists();
2. 分布式Map缓存(RMapCache)
RMapCache是带有过期功能的分布式Map,适合用作缓存存储:
// 获取带过期功能的Map
RMapCache<String, User> userMapCache = redisson.getMapCache("userMapCache");
// 存储对象并设置过期时间(30分钟后过期)
User user1 = new User(1001, "张三", "zhangsan@example.com");
userMapCache.put("1001", user1, 30, TimeUnit.MINUTES);
// 存储对象并设置最大空闲时间(10分钟内没有被访问则过期)
User user2 = new User(1002, "李四", "lisi@example.com");
userMapCache.put("1002", user2, 1, TimeUnit.HOURS, 10, TimeUnit.MINUTES);
// 获取对象
User cachedUser = userMapCache.get("1001");
if (cachedUser != null) {
System.out.println("从缓存获取用户: " + cachedUser);
}
// 获取所有键
Collection<String> keys = userMapCache.keySet();
// 移除特定键
userMapCache.remove("1002");
// 清空缓存
userMapCache.clear();
3. 缓存加载器(自动加载)
Redisson支持通过CacheLoader自动加载缓存,当缓存不存在时自动从数据源加载:
// 创建带缓存加载器的MapCache
RMapCache<String, User> userMapCache = redisson.getMapCache("userMapCache");
// 模拟从数据库加载用户数据
CacheLoader<String, User> loader = key -> {
System.out.println("从数据库加载用户: " + key);
// 这里应该是从数据库查询的逻辑
if ("1001".equals(key)) {
return new User(1001, "张三", "zhangsan@example.com");
} else if ("1002".equals(key)) {
return new User(1002, "李四", "lisi@example.com");
}
return null;
};
// 获取对象,如果不存在则自动加载
User user = userMapCache.getOrDefault("1001", loader);
System.out.println("获取用户: " + user);
// 再次获取,这次会从缓存读取
User cachedUser = userMapCache.getOrDefault("1001", loader);
System.out.println("再次获取用户(应来自缓存): " + cachedUser);
4. 多级缓存策略
Redisson可以与本地缓存结合实现多级缓存:
// 本地缓存(Caffeine示例)
Cache<String, User> localCache = Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES) // 本地缓存1分钟
.maximumSize(1000)
.build();
// Redis分布式缓存
RMapCache<String, User> redisCache = redisson.getMapCache("userCache");
// 多级缓存获取
public User getUserWithMultiLevelCache(String userId) {
// 1. 先查本地缓存
User user = localCache.getIfPresent(userId);
if (user != null) {
System.out.println("从本地缓存获取用户: " + userId);
return user;
}
// 2. 查Redis分布式缓存
user = redisCache.get(userId);
if (user != null) {
System.out.println("从Redis缓存获取用户: " + userId);
// 回填本地缓存
localCache.put(userId, user);
return user;
}
// 3. 查数据库(模拟)
System.out.println("从数据库加载用户: " + userId);
user = loadFromDatabase(userId);
if (user != null) {
// 写入Redis缓存(设置30分钟过期)
redisCache.put(userId, user, 30, TimeUnit.MINUTES);
// 写入本地缓存
localCache.put(userId, user);
}
return user;
}
// 模拟数据库加载
private User loadFromDatabase(String userId) {
// 实际应用中这里是从数据库查询
if ("1001".equals(userId)) {
return new User(1001, "张三", "zhangsan@example.com");
}
return null;
}
5. 缓存事件监听
Redisson支持监听缓存事件,如缓存添加、移除等:
// 获取MapCache
RMapCache<String, User> userMapCache = redisson.getMapCache("userMapCache");
// 添加缓存事件监听器
userMapCache.addListener(new EntryAddedListener<String, User>() {
@Override
public void onCreated(EntryEvent<String, User> event) {
System.out.println("缓存添加事件 - Key: " + event.getKey() + ", Value: " + event.getValue());
}
});
userMapCache.addListener(new EntryRemovedListener<String, User>() {
@Override
public void onRemoved(EntryEvent<String, User> event) {
System.out.println("缓存移除事件 - Key: " + event.getKey() + ", Value: " + event.getValue());
}
});
// 触发事件
userMapCache.put("1001", new User(1001, "张三", "zhangsan@example.com"));
userMapCache.remove("1001");
6. 缓存统计信息
Redisson提供了缓存统计功能,可以监控缓存命中率等指标:
// 获取带统计功能的MapCache
RMapCache<String, User> statsMapCache = redisson.getMapCache("statsMapCache");
// 启用统计(需要Redisson配置中启用)
// statsMapCache = redisson.getMapCache("statsMapCache",
// new MapOptions<String, User>().statisticsEnabled(true));
// 存储数据
statsMapCache.put("1001", new User(1001, "张三", "zhangsan@example.com"));
// 注意:获取统计信息需要通过Redis命令或管理工具
// 实际应用中可以通过Redis的INFO命令或Redisson的管理API获取
四、Redisson缓存高级特性
1. 缓存预热
在应用启动时预先加载热点数据到缓存:
@PostConstruct
public void initCache() {
// 应用启动时预热热门用户数据
List<String> hotUserIds = Arrays.asList("1001", "1002", "1003");
for (String userId : hotUserIds) {
User user = loadFromDatabase(userId);
if (user != null) {
userMapCache.put(userId, user, 2, TimeUnit.HOURS);
}
}
}
2. 缓存雪崩防护
通过为不同的缓存项设置随机的过期时间,避免大量缓存同时失效:
// 为不同的用户设置基础过期时间+随机偏移量,避免雪崩
public void cacheUserWithAvalancheProtection(User user) {
long baseTtl = 30; // 基础30分钟
long randomOffset = ThreadLocalRandom.current().nextLong(0, 10); // 随机0-10分钟偏移
long ttl = baseTtl + randomOffset;
userMapCache.put(user.getId().toString(), user, ttl, TimeUnit.MINUTES);
}
3. 缓存穿透防护
对于不存在的key也进行缓存,避免频繁查询数据库:
public User getUserWithPenetrationProtection(String userId) {
// 1. 先查缓存
User user = userMapCache.get(userId);
if (user != null) {
// 缓存命中,如果是特殊标记的空值,返回null
if (user instanceof NullUser) {
return null;
}
return user;
}
// 2. 查数据库
user = loadFromDatabase(userId);
if (user != null) {
// 3. 数据库存在,写入缓存
userMapCache.put(userId, user, 30, TimeUnit.MINUTES);
} else {
// 4. 数据库不存在,缓存空值防止穿透
userMapCache.put(userId, new NullUser(), 5, TimeUnit.MINUTES);
}
return user;
}
// 空值标记类
private static class NullUser extends User {
public NullUser() {
super(-1, null, null);
}
}
4. 缓存击穿防护(互斥锁)
使用分布式锁防止热点key失效时大量请求直接打到数据库:
public User getUserWithBreakdownProtection(String userId) {
String lockKey = "user_lock:" + userId;
RLock lock = redisson.getLock(lockKey);
try {
// 1. 先查缓存
User user = userMapCache.get(userId);
if (user != null) {
if (user instanceof NullUser) {
return null;
}
return user;
}
// 2. 获取分布式锁
boolean locked = lock.tryLock(1, 10, TimeUnit.SECONDS);
if (locked) {
try {
// 双重检查,可能在等待锁的过程中其他线程已经加载了缓存
user = userMapCache.get(userId);
if (user != null) {
if (user instanceof NullUser) {
return null;
}
return user;
}
// 3. 查数据库
user = loadFromDatabase(userId);
if (user != null) {
// 4. 写入缓存
userMapCache.put(userId, user, 30, TimeUnit.MINUTES);
} else {
// 5. 缓存空值
userMapCache.put(userId, new NullUser(), 5, TimeUnit.MINUTES);
}
return user;
} finally {
lock.unlock();
}
} else {
// 获取锁失败,短暂等待后重试
Thread.sleep(100);
return getUserWithBreakdownProtection(userId);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("获取用户信息被中断", e);
}
}
五、Redisson配置详解(补充缓存相关配置)
1. 缓存配置选项
Config config = new Config();
// 单机模式配置
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
.setPassword("password");
// 缓存相关配置(通过MapOptions设置)
MapOptions<String, User> mapOptions = MapOptions.<String, User>defaults()
.evictionPolicy(EvictionPolicy.LRU) // 淘汰策略: LRU, LFU, FIFO
.maxSize(1000) // 最大缓存条目数
.timeToLive(30, TimeUnit.MINUTES) // 条目存活时间
.maxIdle(10, TimeUnit.MINUTES); // 条目最大空闲时间
// 创建带配置的MapCache
RMapCache<String, User> userMapCache = redisson.getMapCache("userMapCache", mapOptions);
2. 淘汰策略
Redisson支持多种缓存淘汰策略:
// LRU (最近最少使用)
MapOptions<String, User> lruOptions = MapOptions.<String, User>defaults()
.evictionPolicy(EvictionPolicy.LRU);
// LFU (最不经常使用)
MapOptions<String, User> lfuOptions = MapOptions.<String, User>defaults()
.evictionPolicy(EvictionPolicy.LFU);
// FIFO (先进先出)
MapOptions<String, User> fifoOptions = MapOptions.<String, User>defaults()
.evictionPolicy(EvictionPolicy.FIFO);
集群配置:
1.单机模式
Config config = new Config();
// 单机模式
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
.setPassword("password")
.setDatabase(0)
.setConnectionPoolSize(64) // 连接池大小
.setConnectionMinimumIdleSize(10) // 最小空闲连接数
.setIdleConnectionTimeout(10000) // 空闲连接超时时间
.setConnectTimeout(10000) // 连接超时时间
.setTimeout(3000) // 命令等待超时时间
.setRetryAttempts(3) // 命令重试次数
.setRetryInterval(1500) // 命令重试发送时间间隔
.setPingConnectionInterval(30000) // 连接空闲多久后发送ping命令保持连接
.setKeepAlive(true); // 是否保持长连接
RedissonClient redisson = Redisson.create(config);
2. 主从模式配置
Config config = new Config();
config.useMasterSlaveServers()
.setMasterAddress("redis://127.0.0.1:6379")
.addSlaveAddress("redis://127.0.0.1:6380", "redis://127.0.0.1:6381")
.setPassword("password")
.setDatabase(0)
.setSlaveConnectionPoolSize(64)
.setMasterConnectionPoolSize(64)
.setReadMode(ReadMode.SLAVE) // 读操作从从节点读取
.setSubscriptionMode(SubscriptionMode.SLAVE); // 订阅操作从从节点读取
RedissonClient redisson = Redisson.create(config);
3. 哨兵模式配置
Config config = new Config();
config.useSentinelServers()
.setMasterName("mymaster")
.addSentinelAddress("redis://127.0.0.1:26379", "redis://127.0.0.1:26380")
.setPassword("password")
.setDatabase(0)
.setMasterConnectionPoolSize(64)
.setSlaveConnectionPoolSize(64);
RedissonClient redisson = Redisson.create(config);
4. 集群模式配置
Config config = new Config();
config.useClusterServers()
.addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001")
.addNodeAddress("redis://127.0.0.1:7002", "redis://127.0.0.1:7003")
.addNodeAddress("redis://127.0.0.1:7004", "redis://127.0.0.1:7005")
.addNodeAddress("redis://127.0.0.1:7006", "redis://127.0.0.1:7007")
.setPassword("password")
.setScanInterval(2000) // 集群状态扫描间隔时间
.setSlaveConnectionPoolSize(64)
.setMasterConnectionPoolSize(64);
RedissonClient redisson = Redisson.create(config);
六、Redisson缓存最佳实践
1. 缓存设计原则
- 合理设置缓存粒度 :根据业务需求确定缓存对象的大小和粒度
- 选择合适的过期策略 :根据数据特性设置合理的TTL和最大空闲时间
- 避免大Key :单个缓存值不宜过大,建议控制在10MB以内
- 考虑数据一致性 :明确缓存与数据库的一致性要求
2. 性能优化建议
- 批量操作 :尽可能使用批量操作减少网络往返
- 异步操作 :对于非关键路径使用异步缓存操作
- 本地缓存结合 :热点数据可结合本地缓存实现多级缓存
- 监控缓存命中率 :定期分析缓存命中率,优化缓存策略
3. 监控与维护
- 监控缓存指标 :关注缓存大小、命中率、淘汰率等指标
- 定期清理 :对于长期不用的缓存数据定期清理
- 容量规划 :根据业务增长预测缓存容量需求
Redisson不仅提供了强大的分布式数据结构支持,还通过其分布式缓存解决方案(如RBucket、RMapCache等)为Java应用提供了高性能、易用的缓存存储能力。通过本文的介绍,我们深入了解了Redisson的缓存存储功能,包括:
- 基本缓存操作 :通过RBucket实现简单的键值缓存
- 高级缓存功能 :通过RMapCache实现带过期策略的分布式Map缓存
- 缓存加载机制 :自动从数据源加载缓存的CacheLoader
- 多级缓存策略 :结合本地缓存实现高效的多级缓存
- 缓存事件监听 :监听缓存变化事件
- 缓存保护机制 :防止缓存雪崩、穿透、击穿等问题的策略