java使用Redisson解决缓存穿透、缓存击穿、缓存雪崩

本文介绍了如何使用Redisson在Java中管理和应对缓存问题,包括布隆过滤器防穿透、热点数据永不过期和分布式锁防击穿、以及合理规划过期时间以防止雪崩。通过Redisson提供的分布式工具,降低分布式系统设计复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。

Redisson在基于NIO的Netty框架上,充分的利用了Redis键值数据库提供的一系列优势,在Java实用工具包中常用接口的基础上,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。

在使用Redis过程中常见问题有缓存穿透、缓存击穿、缓存雪崩,以下使用Redisson代码解决这三种问题

public class RedissonManager {
    private static Config config = new Config();
    //声明redisso对象
    private static Redisson redisson = null;
   //实例化redisson
	 static{
	
	     config.useSingleServer().setAddress("redis://127.0.0.1:6379");
	          //得到redisson对象
	        redisson = (Redisson) Redisson.create(config);
	
	}

 //获取redisson对象的方法
    public static Redisson getRedisson(){
        return redisson;
    }
}
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RMap;

public class RedisTest {

	//在系统中常见缓存问题的解决方案
	public static void main(String[] args) throws InterruptedException {
		Redisson redisson = RedissonManager.getRedisson();
		System.out.println("----------------缓存穿透--------------------------");
		//一、缓存穿透
		//当客户端访问一个不存在缓存的key会直接击中数据库,而当数据库中没有该信息时又无法缓存该key-value,所以当客户端大量同时请求一个不存在的key时会全部命中数据库给数据库造成很大压力
		//解决方式
		//1、布隆过滤器
		//它布隆过滤器实际上是一个很长的二进制向量和一系列随机映射函数组成,主要用于判断一个元素是否在一个集合中。
		RBloomFilter<String> bloom = redisson.getBloomFilter("name");
		// 初始化布隆过滤器;  大小:100000,误判率:0.01
		bloom.tryInit(100000L, 0.01);
		List<String> keys = Arrays.asList("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q");
		keys.forEach(key -> bloom.add(key));
		
		//布隆过滤器判定某个元素存在,小概率会误判;布隆过滤器判定某个元素不在,则这个元素一定不在
		System.out.println(bloom.contains("a")); //true
		System.out.println(bloom.contains("q")); //true
		System.out.println(bloom.contains("z")); //false
		
		//2、设置一个空置的value,设一个短期的过期时间
		String key = "z";
		RMap<String, String> rmap = redisson.getMap(key);
		String zValue = rmap.get(key);
		if(zValue == null) {
			rmap.put(key, "EMPTY_KEY");
			rmap.expire(1, TimeUnit.SECONDS);
		}
		
		System.out.println(redisson.getMap(key).get(key)); //EMPTY_KEY
		//休眠1秒
		TimeUnit.SECONDS.sleep(1);
		System.out.println(redisson.getMap(key).get(key)); //null
		
		System.out.println("----------------缓存击穿--------------------------");
		
		//二、缓存击穿
		//当一个缓存数据过期时,在高并发业务下有可能大量请求同时击穿缓存命中数据库给数据造成压力
		//解决方式
		//1.热点数据永不过期
		String hotKey = "a";
		rmap = redisson.getMap(hotKey);
		rmap.put(hotKey, "Hot_Value");
		rmap.clearExpire();
		TimeUnit.SECONDS.sleep(1);
		System.out.println(redisson.getMap(hotKey).get(hotKey)); //Hot_Value
		
		//2.采用分布式锁,缓存失效后只有一个线程更新并写入
		String update = "b";
		rmap = redisson.getMap(update);
		String bValue = rmap.get(update);
		if(bValue == null) {
			final String updateKey = "UPDATE_KEY_" + update;
		    RLock lock = redisson.getLock(updateKey);
		    //2、加锁 默认加锁时间30s
		    lock.lock(); 
		    try {
		        System.out.println("加锁成功,执行查询数据库"  + Thread.currentThread().getId());
		        TimeUnit.SECONDS.sleep(20);
		        rmap.put(update, "b");
		    } catch (InterruptedException e) {
		        e.printStackTrace();
		    } finally {
		        //3、解锁
		        System.out.println("释放锁" + Thread.currentThread().getId());
		        lock.unlock();
		    }
		}
		System.out.println(rmap.get(update));
		
		System.out.println("----------------缓存雪崩--------------------------");
		//三、缓存雪崩
		//大面积缓存击穿
		//解决方式
		//1.Redis部署方式采用哨兵或集群提高Redis的可用性
		
		//2.采用缓存击穿的应对方式
		
		//3.设定错开的过期时间
		Double ran = Math.random()*100;
		String randomKey = "c";
		rmap = redisson.getMap(randomKey);
		rmap.expire(30 + ran.longValue(), TimeUnit.SECONDS);
		rmap.put(randomKey, "c");
		System.out.println(rmap.get(randomKey));
	}
}

<think>嗯,用户想知道怎么用Redisson的分布式锁来解决缓存击穿的问题。首先我得回忆一下缓存击穿是什么情况。缓存击穿通常是指某个热点数据过期后,大量并发请求直接打到数据库,导致数据库压力激增。这时候需要用分布式锁来保证只有一个请求去数据库加载数据,其他请求等待或重试。 接下来,用户需要具体的方案和示例,所以我得按照步骤来分解。首先应该介绍缓存击穿的背景,然后分步骤说明如何用Redisson实现。Redisson的文档里提到过RLock和tryLock方法,可能需要用这些来加锁。然后,代码示例是关键,用户可能需要看到具体的Java代码,比如如何获取锁、查询数据库、设置缓存等步骤。 用户提供的引用里有提到Redisson的配置,比如缓存时间和相关配置类,可能需要引用这些内容。同时,用户之前的问题中有提到有人使用Redisson的tryLock方法但未达到预期效果,这可能需要注意锁的获取方式和等待时间。例如,是否设置了合理的等待时间和超时时间,避免线程长时间阻塞。 另外,引用5中提到了分布式锁防止缓存穿透雪崩的步骤,可能需要参考这个流程:获取锁、查缓存、查数据库、写缓存、释放锁。这应该是一个标准的流程。还需要考虑缓存空值的情况,防止缓存穿透,也就是即使数据库中没有数据,也缓存一个空值,避免频繁查询数据库。 代码示例部分,需要确保语法正确,使用try-finally来释放锁,避免死锁。同时,要处理获取锁失败的情况,比如等待一段时间后重试或者直接返回错误。Redisson的tryLock方法可以指定等待时间和锁的持有时间,这样可以避免死锁问题。 可能还需要解释为什么使用Redisson而不是其他锁机制,比如它基于Redis,支持高可用,自动续期等特性。引用4中提到Redisson是分布式系统中的JUC,所以它的锁机制类似于Java的并发工具,但适用于分布式环境。 最后,相关问题的生成需要考虑用户可能进一步的问题,比如性能优化、锁的可重入性、如何避免死锁,或者与其他缓存问题的区别,比如穿透雪崩的不同解决方案。这些可以作为相关问题列出,帮助用户深入理解。</think>### 如何用 Redisson 分布式锁解决缓存击穿问题? #### 1. **问题背景** 缓存击穿指某个**热点数据过期瞬间**,大量并发请求穿透缓存直接访问数据库,导致数据库瞬时压力骤增。通过分布式锁可控制**仅一个线程查询数据库并重建缓存**,其他线程等待后重试缓存[^5]。 --- #### 2. **解决方案步骤** 1. **查询缓存**:先尝试从缓存获取数据 2. **缓存未命中时加锁**:使用 Redisson 的 `RLock` 获取分布式锁 3. **二次校验缓存**:加锁后再次查询缓存(防止其他线程已更新) 4. **查询数据库并写入缓存**:若仍无数据,从数据库加载后更新缓存 5. **释放锁**:确保锁在 `finally` 块中释放 --- #### 3. **代码示例** ```java public String getData(String key) { String data = redisTemplate.opsForValue().get(key); if (data == null) { RLock lock = redissonClient.getLock(key + "_LOCK"); try { // 尝试获取锁,最多等待5秒,锁持有10秒后自动释放 if (lock.tryLock(5, 10, TimeUnit.SECONDS)) { // 二次校验防止重复查询 data = redisTemplate.opsForValue().get(key); if (data == null) { data = database.query(key); // 模拟数据库查询 redisTemplate.opsForValue().set(key, data, 1, TimeUnit.HOURS); } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); // 确保只有持有锁的线程释放 } } } return data; } ``` --- #### 4. **关键配置说明** - **Redisson 客户端初始化**:需注入 `RedissonClient` 实例 - **Redis 缓存配置**:需设置合理的过期时间(如1小时)并允许缓存空值[^2] - **锁参数优化**:`tryLock` 的等待时间(5秒)和锁持有时间(10秒)需根据业务调整 --- #### 5. **注意事项** - **死锁预防**:锁必须设置超时时间,避免线程崩溃导致锁无法释放[^1] - **缓存空值**:若数据库无数据,应缓存空值或占位符(如 `NULL`),防止反复穿透 - **锁的可重入性**:Redisson 锁支持同一线程多次加锁,避免嵌套调用死锁[^4] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值