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));
}
}