Redis——缓存穿透、击穿、雪崩、双写一致性、持久化、数据淘汰策略、数据删除、分布式锁

9 篇文章 0 订阅
3 篇文章 0 订阅

Redis

1. 谈谈对Redis的认识

        Redis(Remote Dictionary Server)是一个使用C语言编写的高性能的内存数据库,基于Key/Value结构存储数据,支持多种数据结构,一般用来做缓存、消息队列、分布式锁,同时还支持事务、持久化、主从和集群架构等。

  • 内存数据库:Redis主要将数据存储在内存中,这使得它具有非常高的读写性能。也意味着Redis适合用作需要快速访问的数据存储,尤其是对于那些对延迟要求严格的应用。

  • 支持多种数据结构:包括字符串、哈希、列表、集合、有序集合等。这些数据结构使得Redis不仅可以用作简单的键值存储,还可以用于更复杂的数据处理场景、

  • 持久化:除了将数据存储在内存中,Redis还提供了持久化功能,可以将数据保存到磁盘上,以防止数据丢失。Redis支持两种持久化方式:RDB(Redis DataBase)和AOF(Append Only File)。

  • 分布式:Redis可以通过主从复制(Master-Slave Replication)和分片(Sharding)来扩展性能和提高可用性。这使得Redis可以处理大规模的数据和高并发请求

  • 事务支持:Redis通过MULTI、EXEC、DISCARD和WATCH等命令,可以将多个命令打包成一个事务执行,保证这些命令的原子性。

  • 发布订阅:Redis可以用于构架消息队列、实时通讯等场景。发布者将消息发布到指定的频道,订阅者则可以订阅这些频道以接收消息。

  • 应用场景广泛:由于Redis具有高性能、灵活的数据结构和丰富的功能,因此被广泛应用于缓存、会话存储、实时统计、消息队列等各种场景中。

2. Redis的基础数据类型有哪些

  • String:以字符串形式存储数据,经常用于记录用户的访问次数、文章访问量等

  • hash:以对象形式存储数据,比较方便的就是操作对象中的某个字段

  • list:以列表形式存储数据,可记录添加顺序,允许元素重复

  • set:以集合形式存储数据,不记录添加顺序,元素不能重复,也不能保证存储顺序

  • zset:有序集合,可对数据基于某个权重进行排序。可做排行榜,取TOP N操作

3. Redis的全局指令

  • keys:查看所有键

  • dbsize:键总数

  • exists key:检查键是否存在

  • del key [key …]:删除键

  • expire key seconds:键过期

  • ttl key :观察键的剩余过期时间

  • type key:键的数据结构类型

4. 什么是缓存穿透,怎么解决

  • 缓存穿透是指查询一个一定不存在的数据,如果从数据库中查不到数据则不写入缓存,导致这个不存在的数据每次请求都要到数据库中去查询,就像缓存不存在一样穿透过去,可能导致数据库挂掉。这种情况大概率是遭受到了攻击。

  • 解决方案:接口校验,缓存空数据,布隆过滤器

    • 接口校验:正常用户操作虽然可能会发生缓存穿透,但是数量较少,出现大量穿透现象时一般就是遭到攻击。可以在最外先设置一层校验,如设置用户鉴权、数据合法性校验等

    • 缓存空数据:查询返回的数据为空,但是扔把这个空结果存储在缓存中

      • 优点:简单
      • 缺点:消耗内存,可能会发生不一致的问题
    • 布隆过滤器:主要是用于检索一个元素是否在一个集合中,可以使用redisson实现布隆过滤器。

      底层原理
              先去初始化一个比较大的数组,里面存放的是二进制0或1。在开始都是0,当一个key来了之后经过3次hash计算,模于数组长度找到数据的下表然后把数据中原来的0改为1.这样的话,3个数组的位置就能标明一个key的存在。查找的过程也是一样。

              不过,布隆过滤器有缺点存在,可能产生一定的误判,一般是可以设置这个误判率的,大概不会超过5%。其实这个误判是必然存在的,要不就要增加数组的长度,5%以内的误判率一般的项目都可以接受,不至于在高并发下压倒数据库。

5. 什么是缓存击穿,如何解决

  • 缓存击穿时对于设置了过期时间的key,缓存在某个时间点过期时,恰好在这个时间点对这个key有大量的并发请求发过来,这些请求发现缓存过期,一般都会从数据库中加载数据并回设到缓存,这个时候高并发的请求可能会瞬间把数据库压垮。

  • 解决方案:使用互斥锁,设置逻辑过期

    • 使用互斥锁:当缓存失效时,不立即去load db,先使用如Redis的setnx去设置一个互斥锁,当操作成功返回时再进行load db的操作并回设到缓存,否则重试get缓存的方法。

    • 设置key的逻辑过期,大致思路如下:

      1. 在设置key的时候,设置一个过期时间字段一起存入缓存中,不给当前key设置过期时间
      2. 当查询的时候,从redis取出数据后判断时间是否过期
      3. 如果过期则开通另外一个线程进行数据同步,当前线程正常返回数据,这个数据不是最新数据
  • 利弊

    • 如果选择数据的强一致性,建议使用分布式锁的方案,性能上可能没有那么高,锁需要等,也有可能产生死锁的问题
    • 如果选择key的逻辑过期,则优先考虑的是高可用性,性能比较高,但是数据同步做不到强一致性。

6. 什么是缓存雪崩,如何解决

  • 缓存雪崩是设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到数据库,数据库瞬间压力过重雪崩。与缓存击穿的区别是,雪崩是很多key同时失效,击穿是某一个key失效。

  • 解决方案:给不同的key的ttl添加随机值,给业务添加多级缓存,设置key永不过期,给缓存业务添加降级限流策略,利用redis集群提高服务的可用性

    • 添加随机值:在原有过期时间上添加随机值,将缓存失效时间分散开来,这样就很难引发集体过期,这也解决雪崩的主要方案

    • 设置key永不过期:对于一些不会频繁变化的数据,可以将其设置为永久有效,避免在缓存失效时需要重新加载数据

    • 使用多级缓存:采用多级缓存架构,将热点数据缓存在多个层级的缓存中,当某一级缓存失效时,可以从其他缓存中获取数据,避免大量请求直接访问数据库

    • 限流和降级:在缓存失效时,可以对请求进行限流或者降级处理,减少对数据库的访问压力。可以通过设置并发请求的最大量、请求的排队机制等方式来控制请求的流量。降级限流可以作为缓存系统的保底策略,适用于穿透、击穿、雪崩。

7. Redis作为缓存,MySQL的数据如何于Redis进行同步(双写一致性)

  • 强一致性

    • 就说我最近在做的项目中有抢券功能,需要让数据库与Redis保持高度一致。因为要求时效性比较高,所以我们采用读写锁保证的强一致性。
    • 当时我们采用redisson实现的读写锁,在读的时候添加共享锁,可以保证读读不互斥,读写互斥;在更新数据的时候,添加排他锁,它是读写,读读都互斥。这样就能保证在写数据的同时是不会让其他线程读数据的,避免了脏数据。值得注意的是这里面的读方法和写方法上需要使用同一把锁才行。
  • 允许延时一致的业务

    • 就说我最近在做的项目,里面有将文章热点数据都写入缓存的功能,虽然是热点数据,但是实时性要求没有那么高,所以采用异步方案同步数据。
    • 我们当时采用的是阿里的cannal组件实现的数据同步:不需要更改业务逻辑代码,部署一个cannal服务,cannal服务把自己伪装成mysql的一个从节点,当mysql数据更新以后,cannal会读取binlog文件数据,然后再通过cannal的客户端获取到数据,更新缓存。

8. 排他锁是如何保证读写、读读互斥的(强一致性)

        排他锁底层使用的是setnx,保证了同时只能有一个线程操作加锁的方法。

9. 听说过延时双删吗?为什么不用它?(强一致性)

        延时双删,如果是写操作,先把缓存中的数据删除,然后更新数据库,最后在延时删除缓存中的数据,其中这个延时多久不太好确定,在延时的过程中可能会出现脏数据,并不能保证强一致性,所以没有采用。

10. Redis做缓存,数据的持久化是如何做的

        在Redis中提供了两种数据持久化方式:分别是RDB和AOF

  • RDB(Redis Database Backup file,Redis数据备份文件)

    定义:RDB也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录在磁盘中。当Redis实例故障重启后,从磁盘读取快照文件,恢复数据
    在这里插入图片描述

    执行过程
    bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入RDB文件。fork采用的是copy-on-write技术:

    • 当主进程进行读操作时,访问共享内存
    • 当主进程执行写操作时,则会拷贝一份数据,执行写操作
  • AOF(Append Only File,追加文件)

    • Redis处理器的每一个命令都会记录在AOF文件,可以看作是命令日志文件。
    • AOF默认是关闭的,要通过redis.conf配置文件开启AOF
      在这里插入图片描述
    • AOF的命令记录频率也可以通过redis.conf来配置
      在这里插入图片描述
    • always:同步刷盘,可靠性高,几乎不丢失数据,性能影响大
    • everysec:每秒刷盘,性能适中,最多丢失1秒数据
    • no:操作系统控制,性能最好,可靠性较差,可能丢失大量数据

11. 两种数据持久化方式有什么区别

  • RDB是一个快照文件,它是把Redis内存存储的数据写到磁盘上,当redis实例宕机恢复数据时,方便从RDB的快照文件中恢复数据;
  • AOF是追加文件,当redis操作写命令时都会存储在这个文件中,当redis实力当即恢复数据时,会从这个文件中再执行一遍命令来恢复数据。

12. 两种数据持久化方式哪种恢复数据比较快

        RDB因为是二进制文件,在保存的时候体积也是比较小的,它恢复的比较快,但是他有可能会丢失数据,通常我们在项目中也会使用AOF来恢复数据,虽然AOF恢复的速度慢一点,但是他丢失数据的风险要小很多,在AOF文件中可以设置刷盘策略,设置为每秒批量写入一次命令。

13. Redis的key过期后会立即删除吗(数据删除策略)

        Redis对数据设置的过期时间,当数据过期之后,就需要将数据从内存中删除。可以按照不同的规则进行删除,这种删除规则就是数据的删除策略(数据过期策略)

14. Redis的数据过期策略有哪些

        redis有两种数据过期策略,分别是惰性删除和定时删除

  • 惰性删除:在设置该key过期时间后,不再去管它,当需要改key时,再检查其是否过期,如果过期就删除,反之返回该key
  • 定时删除:定期删除有两种模式(SLOW模式和FAST模式)
    • SLOW模式是定时任务,执行频率默认是10Hz,每次不超过25ms,以通过修改配置文件redis.conf的hz选项来调整。
    • FAST模式执行频率不固定,每次事件循环会尝试执行,但两次间隔不低于2ms,每次耗时不超过1ms

        Redis的过期删除策略经常使用惰性删除+定期删除两种策略配合使用

15. 假如缓存过多,内存是有限的,内存被占满该怎么办(Redis的数据淘汰策略)

        数据淘汰策略:当Redis中的内存不足时,此时再向Redis中添加新的key,那么Redis就会按照某一种规则将内存中的数据删除,这种数据的删除规则被称为内存的淘汰策略。

Redis支持8种不同的数据淘汰策略

  • noeviction:不淘汰任何key,但是内存满时不允许写入新数据。默认策略
  • volatile-ttl:对设置了ttl的key,比较key的剩余ttl值,ttl越小越先被淘汰
  • allkeys-random:对全体key随机进行淘汰
  • volatile-random:对设置了TTL的key随即进行淘汰
  • allkeys-LRU:对全体key基于LRU算法进行淘汰
  • volatile-LRU:对设置了TTL的key,基于LRU算法进行淘汰
  • allkeys-LFU:对全体key,基于LFU算法进行淘汰
  • volatile-LFU:对设置了TTL的key,基于LFU算法进行淘汰

数据淘汰策略选择建议

  • 优先使用allkeys-LRU策略。充分利用LRU算法的优势,把最近最常访问的数据留在缓存中。如果业务有明显的冷热数据区分,建议使用
  • 如果业务中数据访问频率差别不大,没有明显冷热数据区分,建议使用allkeys-random,随机选择淘汰
  • 如果业务中有置顶的需求,可以使用volatile-LRU策略,同时置顶数据不设置过期时间,这些数据就一直不被删除,会淘汰其他设置过期时间的数据
  • 如果业务中有短时高频访问的数据,可以使用allkeys-LFUvolatile-LFU策略

16. 数据淘汰策略有哪些

        在Redis中提供了8种数据淘汰策略,默认是noviction,不删除任何数据,内存不足时直接报错。不过可以在配置文件中设置淘汰策略。有两个很重要概念:LRU和LFU。LRU即最近最少使用,用当前时间减去最后一次访问时间,这个值越大则淘汰优先级越高。LFU即最少频率使用,会统计每个key的访问频率,值越小淘汰月线及越高。在项目中我们使用的是allkeys-LRU,挑选最近最少使用的数据淘汰,把一些经常访问的key留在redis中

  • 数据库有1000万数据,redis只能缓存20w数据,如何保证Redis中的数据都是热点数据
  • 使用allkeys-LRU淘汰策略,留下来的的都是经常访问的热点数据
  • Redis的内存用完了会发生什么
    • 主要看数据淘汰策略是什么,如果是默认的配置noviction,会直接报错。其他策略会按相应的淘汰规则淘汰数据。

17. Redis分布式锁如何实现(分布式锁)

        在Redis中提供了一个命令setnx(SET if not exists)。由于redis是单线程的,用了命令之后,只能有一个客户端对某一个key设置值,在没有过期或删除key的时候,其他客户端时不能设置这个key的

18. 如何控制分布式锁的有效时长

        redis的setnx指令不好控制锁的有效市场,我们采用的是redis的一个框架redisson实现。

        在redisson中需要手动加锁,并且可以控制锁的失效时间和等待时间,当锁住的一个业务还没有执行完成时,在redisson中引入一个看门狗watchDog机制,每隔一段时间就检查当前业务是否还持有锁,如果持有锁就增加加锁的持有时间,当业务执行完成之后释放锁就可以了。

        另外,还有一个好处是在高并发下,一个业务有可能会执行很快,先客户1持有锁的时候,客户2来了以后并不会马上拒绝,它会自旋不断尝试获取锁,如果客户1释放锁之后,客户2就可以马上持有锁,性能也得到了提升。

19. Redisson实现的分布式锁是可重入的吗

        是可重入的。这样做是为了避免死锁的产生。这个重入其实在内部就是判断是否是当前线程持有的锁,如果是当前线程持有的锁就会计数,如果释放锁就会在计算上减一。在存储数据的时候采用的hash结构,大Key可以按照自己的业务进行定制,其中小key是当前线程的唯一标识,value是当前线程重入的次数。

20. Redisson实现的分布式锁能解决主从一致问题吗

        不能。比如当线程1加锁成功后,master节点数据会异步复制到slave节点,此时当前持有redis锁的master节点宕机,slave节点被提升为新的master,假如现在来了一个线程2,再次加锁,会在新的master节点上加锁成功,这个时候就会出现两个节点同时持有一把锁的问题。

        对于这个问题,我们可以用redisson提供的红锁来解决,他的主要作用是,不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁,并且要求在大多数redis节点上都成功创建所,红锁中要求redis的节点数量要过半。这样就能避免线程1加锁成功后master节点宕机导致线程2成功加锁到新的master节点上的问题。

        但是,如果使用了红锁,因为需要同时在多个节点上都添加锁,性能就变得很低,并且运维成本也非常高,所以,我们一般在项目中也不会直接使用红锁,并且官方也暂时废弃了这个红锁。

21. 如果业务非要保持数据的强一致性,这个该怎么解决

后master节点宕机导致线程2成功加锁到新的master节点上的问题。

        但是,如果使用了红锁,因为需要同时在多个节点上都添加锁,性能就变得很低,并且运维成本也非常高,所以,我们一般在项目中也不会直接使用红锁,并且官方也暂时废弃了这个红锁。

21. 如果业务非要保持数据的强一致性,这个该怎么解决

        redis本身就是支持高可用的,做到强一致性,就非常影响性能,所以,如果有强一致性要求高的业务员,建议使用zookeeper实现的分布式锁,它是可以保证强一致性的。

  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨尘儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值