reids优化系列(五)

        后面更新的这些文章是将所学的redis知识进行巩固并且分享,也希望这些知识不仅仅是用于复习面试,更能在实际的应用中起到优化的作用。

有时候我们会存储一些查询多,修改少,业务逻辑较弱的数据,并且还能高效查询,那么redis就是一种选择。

redis缓存三件套

        所谓的缓存三件套,说白了就是在高并发的场景下,因为一些奇奇怪怪的操作或者原因导致大量的请求没有去请求redis,而是去请求数据库了,而数据库的连接是有限的,一旦超过了就会出事。因此我们的最终目的就是减少或者尽量不去访问数据库。其实解决方法我觉得可以分为外在和内在,外分为如何控制请求量,内在于如何处理这些请求量。

内在操作

缓存穿透

        大量并发去访问一个数据库不存在的数据,由于缓存中没有该数据导致大量并发查询数据库,这个现象要缓存穿透。缓存穿透可以造成数据库瞬间压力过大,连接数等资源用完,最终数据库拒绝连接不可用。

解决

如何解决缓存穿透?

1、对请求增加校验机制

如果请求参数不符合我们规定的条件就直接让他滚蛋,返回,恶意攻击拜拜。

2、使用布隆过滤器

百度百科:

布隆过滤器可以用于检索一个元素是否在一个集合中。如果想要判断一个元素是不是在一个集合里,一般想到的是将所有元素保存起来,然后通过比较确定。链表,树等等数据结构都是这种思路. 但是随着集合中元素的增加,我们需要的存储空间越来越大,检索速度也越来越慢(O(n),O(logn))。不过世界上还有一种叫作散列表(又叫哈希表,Hash table)的数据结构。它可以通过一个Hash函数将一个元素映射成一个位阵列(Bit array)中的一个点。这样一来,我们只要看看这个点是不是1就可以知道集合中有没有它了。这就是布隆过滤器的基本思想。

布隆过滤器的特点是,高效地插入和查询,占用空间少;查询结果有不确定性,如果查询结果是存在则元素不一定存在,如果不存在则一定不存在;另外它只能添加元素不能删除元素,因为删除元素会增加误判率。

我们可以利用如果不存在则一定不存在的特性来运用过滤器去查询。

比如:将商品id写入布隆过滤器,如果分3次hash此时在布隆过滤器有3个点,当从布隆过滤器查询该商品id,通过hash找到了该商品id在过滤器中的点,此时返回1,如果找不到一定会返回0。

所以,为了避免缓存穿透我们需要缓存预热将要查询的内容提前存入布隆过滤器,添加数据时将信息的id也存入过滤器,当去查询一个数据时先在布隆过滤器中找一下如果没有到到就说明不存在,此时直接返回。

实现方法有:

Google工具包Guava实现。

redisson 。

2、缓存空值或特殊值

请求通过了第一步的校验,查询数据库得到的数据不存在,此时我们仍然去缓存数据,缓存一个空值或一个特殊值的数据。

但是要注意:如果缓存了空值或特殊值要设置一个短暂的过期时间。

缓存击穿

缓存击穿是指大量并发访问同一个热点数据,当热点数据失效后同时去请求数据库,瞬间耗尽数据库资源,导致数据库无法使用。

解决

1、使用同步锁控制查询数据库的线程

使用同步锁控制查询数据库的线程,只允许有一个线程去查询数据库,查询得到数据后存入缓存。

锁的话就会想到sychronized,这锁很明显,重量级锁,效率太低。相比于sychronized,这种读多写少的情况更适合用ReentrantReadWriteLock,Semaphore,StampedLock读写锁。

同时我们应该尽量减少锁控制的代码块,可以使用双重校验,也可以用乐观锁来提高效率。

2、热点数据不过期

可以由后台程序提前将热点数据加入缓存,缓存过期时间不过期,由后台程序做好缓存同步。

缓存雪崩

缓存雪崩是缓存中大量key失效后当高并发到来时导致大量请求到数据库,瞬间耗尽数据库资源,导致数据库无法使用。

造成缓存雪崩问题的原因是是大量key拥有了相同的过期时间,如果高并发查询大量的课程信息,那么固定时间后就会过期一旦失效将同时失效,造成雪崩问题。

解决

我们要知道,缓存雪崩的原因出现在,请求量大,大量key过期时间相同。所以我们应该对症下药:

1、使用同步锁控制查询数据库的线程

主题的做法在击穿里已经说明

2、缓存预热

不用等到请求到来再去查询数据库存入缓存,可以提前将数据存入缓存。使用缓存预热机制通常有专门的后台程序去将数据库的数据同步到缓存。

3、对同一类型信息的key设置不同的过期时间

通常对一类信息的key设置的过期时间是相同的,这里可以在原有固定时间的基础上加上一个随机时间使它们的过期时间都不相同。

总结下来就是,尽量做到,控制请求量,防止数据不存在,热点数据过期时间设置,过期时间key随机。

外在操作

网关

网关不仅仅可以用来处理负载均衡,权限认证,跨域还可以限流。

全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是固定的;而GlobalFilter的逻辑需要自己写代码实现。

定义方式是实现GlobalFilter接口。

public interface GlobalFilter {
    /**
     *  处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
     *
     * @param exchange 请求上下文,里面可以获取Request、Response等信息
     * @param chain 用来把请求委托给下一个过滤器 
     * @return {@code Mono<Void>} 返回标示当前过滤器业务结束
     */
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

在filter中编写自定义逻辑,可以实现下列功能:

登录状态判断,权限校验,请求限流等

sentinel

这也是一个微服务中间件,这里先不再详细介绍。

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值