redis本地缓存
为了系统性能的提升,一般会将部分数据放入缓存中,加快访问速度。而db承担数据罗盘工作。
哪些数据适合放在缓存
-
及时性、数据一致性要求不高的
-
访问量大且更新频率不高的数据(读多、写少)
-
导入需要的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
-
在配置文件中配置
redis
主机地址
spring
-
使用时进行自动注入
RedisTemplate
(如下:使用示例)
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
String catalogJson = ops.get("catalogJson");
// 每次查询先在缓存中进行查找,有的话直接查询缓存
if (catalogJson == null) {
// 缓存没有,去数据库中查询,查询之后把结果放在缓存中
Map<String, List<Catalog2Vo>> categoriesDb = getCategoriesDb();
String toJSONString = JSON.toJSONString(categoriesDb);
ops.set("catalogJson",toJSONString);
return categoriesDb;
}
// 从redis中查出的是json数据,需要进行一层转换(序列化与反序列化)
Map<String, List<Catalog2Vo>> listMap = JSON.parseObject(catalogJson, new TypeReference<Map<String, List<Catalog2Vo>>>() {});
return listMap;
Jedis
替换lettuce
当进行压力测试时后期出现堆外内存溢出OutOfDirectMemoryError
产生原因:
-
springboot2.0
以后默认使用lettuce
作为操作redis
的客户端,它使用netty
进行网络通信。 -
lettuce
的bug导致netty
堆外内存溢出。netty
如果没有指定堆外内存,默认使用XMS
的值,可以使用-Dio.netty.maxDirectMemory
进行设置。
解决方案:由于是lettuce
的bug
造成,不要直接使用-Dio.netty.maxDirectMemory
去调大虚拟机堆外内存,治标不治本。
-
升级
lettuce
客户端。但是没有解决的 -
切换使用
jedis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
缓存失效
缓存穿透(查询一个永不存在的数据)
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决:缓存空对象(加一个短暂的过期时间)、布隆过滤器、mvc
拦截器。
缓存雪崩(大面积key同时失效)
缓存雪崩是指在我们设置缓存时key采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
解决方案:
-
规避雪崩:缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
-
如果缓存数据库是分布式部署,将热点数据均匀分布在不同缓存数据库中。
-
设置热点数据永远不过期。
-
出现雪崩:降级 熔断
-
事前:尽量保证整个
redis
集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。 -
事中:本地
ehcache
缓存 +hystrix
限流&降级,避免MySQL
崩掉 -
事后:利用
redis
持久化机制保存的数据尽快恢复缓存
缓存击穿(单点key被高频访问)
缓存雪崩和缓存击穿不同的是:
-
缓存击穿 指 并发查同一条缓存中没有的数据。缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
-
缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案:
-
设置热点数据永远不过期。
-
加互斥锁:业界比较常用的做法,是使用
mutex
。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db去数据库加载,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis
的SETNX
或者Memcache
的ADD)去set一个mutex key
,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。
缓存击穿怎么加锁
加本地锁synchronized(this)
,不能解决分布式服务的问题,有几台机器就会放掉几个线程。
锁时序问题:之前的逻辑是查缓存没有,然后取竞争锁查数据库,这样就造成多次查数据库。
解决方法:竞争到锁后,再次确认缓存中没有,再去查数据库,第一个线程查到之后放到缓存中,后续的查询就可以查缓存了。
如何复制微服务
模拟多台机器服务
右键点击服务,copy configuration
在program arguments: --server.port=10003