• Redis缓存雪崩,击穿,穿透以及解决方案


    Redis读写过程

    一般情况下,Redis都是作为client与MySQL间的一层缓存,尽量减少MySQL的读压力,数据流向如图所示:

    Redis的五种数据类型及使用场景

    1. String 这个其实没啥好说的,最常规的set/get操作,value可以是String也可以是数字。一般做一些复杂的计数功能的缓存。

    2. hash 这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。

    3. list 使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好。

    4. set 因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。为什么不用JVM自带的Set进行去重?因为我们的系统一般都是集群部署,使用JVM自带的Set,比较麻烦,难道为了一个做一个全局去重,再起一个公共服务,太麻烦了。 另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。

    5. sorted set 多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取TOP N操作。

    Redis都有哪些功能和使用场景?

    Redis 是一个使用 C 语言开发的高速缓存数据库。

    功能列表:数据缓存功能,分布式锁的功能,支持数据持久化,支持事务,支持消息队列

    使用场景:记录帖子点赞数、点击数、评论数;缓存近期热帖;缓存文章详情信息;记录用户会话信息。

    Redis缓存雪崩,击穿,穿透以及解决方案

    分析:

    这几个问题,说句实在话,一般中小型传统软件企业,很难碰到这个问题。如果有大并发的项目,流量有几百万左右。这两个问题一定要深刻考虑。

    1、缓存雪崩

    在同一时间缓存大面积失效,从而导致大量请求导向数据库。

    1. 大量请求,导致数据库处理不过来,整个系统依赖数据库的功能全部崩溃。
    2. 单系统挂掉,其他依赖于该系统的应用也会出现不稳定甚至崩溃。
    解决方法:
    • 大多数系统设计者考虑用加锁( 最多的解决方案)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。

    • 还有一个简单方案就时讲缓存失效时间分散开。在批量往Redis存数据的时候,把每个Key的失效时间都加个随机值就好了,这样可以保证数据不会在同一时间大面积失效了!

    2、缓存穿透

    大量请求查询缓存中不存在的数据,导致所有的请求都怼到数据库上,从而导致整个系统依赖数据库的功能全部崩溃。

    解决方法:
    • 最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

    • 另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

    3、缓存击穿

    大量请求集中并发在一个key上,当这个key在失效的瞬间,大量并发请求直接都怼到数据库上,从而导致整个系统依赖数据库的功能全部崩溃。

    1. 缓存击穿是指一个Key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。

    2. 就是这个值是数据库新增的,但是缓存中暂时还没有,这个时候刚好并发请求进来了,如果处理不当也会发生。

    解决方法:

    尽量少的线程构建缓存(甚至是一个) + 数据一致性 + 较少的潜在危险。有四种方法来解决这个问题:
    1、使用互斥锁(mutex key)(业界比较常用)
    2、"提前"使用互斥锁(mutex key)
    3、"永远不过期"
    4、资源保护:采用netflix的hystrix,可以做资源的隔离保护主线程池,如果把这个应用到缓存的构建也未尝不可。

    作为一个并发量较大的互联网应用,我们的目标有3个:(没有最好,只有最合适)

    1. 加快用户访问速度,提高用户体验。
    2. 降低后端负载,保证系统平稳。
    3. 保证数据“尽可能”及时更新(要不要完全一致,取决于业务,而不是技术。)

    1、对数据库访问进行限流(控制并发量)。
    2、容错降级,多线程竞争获取令牌,没拿到令牌就等待。类似Lock
    3、针对内存不足问题,采用redis集群方案。

    四种方案对比:
    解决方案 优点 缺点
    简单分布式锁(Tim yang) 1. 思路简单
    2. 保证一致性
    1. 代码复杂度增大
    2. 存在死锁的风险
    3. 存在线程池阻塞的风险
    加另外一个过期时间(Tim yang) 保证一致性 同上
    不过期(本文) 异步构建缓存,不会阻塞线程池 1. 不保证一致性。
    2. 代码复杂度增大(每个value都要维护一个timekey)。
    3. 占用一定的内存空间(每个value都要维护一个timekey)。
    资源隔离组件hystrix(本文) 1. hystrix技术成熟,有效保证后端。
    2. hystrix监控强大。
    部分访问存在降级策略。

    如何解决redis的并发竞争key问题

    分析:

    这个问题大致就是,同时有多个子系统去set一个key。这个时候要注意什么呢?大家思考过么。需要说明一下,博主提前百度了一下,发现答案基本都是推荐用redis事务机制。博主不推荐使用redis的事务机制。因为我们的生产环境,基本都是redis集群环境,做了数据分片操作。你一个事务中有涉及到多个key操作的时候,这多个key不一定都存储在同一个redis-server上。因此,redis的事务机制,十分鸡肋。

    解决方法:

    1. 如果对这个key操作,不要求顺序,这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可,比较简单。
    2. 如果对这个key操作,要求顺序,假设有一个key1,系统A需要将key1设置为valueA,系统B需要将key1设置为valueB,系统C需要将key1设置为valueC. 期望按照key1的value值按照 valueA-->valueB-->valueC的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。假设时间戳如下
    系统A key 1 {valueA  3:00}
    系统B key 1 {valueB  3:05}
    系统C key 1 {valueC  3:10}
    

    那么,假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。

    其他方法,比如利用队列,将set方法变成串行访问也可以。总之,灵活变通。

    Redis 持久化有几种方式?

    Redis 的持久化有两种方式,或者说有两种策略:

    • RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。

    • AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中。

    主服务器写内存快照,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以主服务器最好不要写内存快照。

  • 相关阅读:
    Oracle SQL性能优化
    spring aop简单日志实例
    一个简单的Spring AOP例子
    jQuery的三种$()
    Mac 上的 outlook 一直让输入密码
    idea 中设置成公司规范的代码格式
    Java 中的锁——Lock接口
    TimeUnit枚举类
    Thread.join()的使用
    java线程的等待、通知机制【读书笔记】
  • 原文地址:https://www.cnblogs.com/zhaojinhui/p/16485640.html
Copyright © 2020-2023  润新知