Day20

1. 说说Redis的主从同步机制

  1. Redis主从同步是指任意数量的从节点都可以从主节点上同步数据。而除了多个slave可以连接到同一个master之外,slave还可以作为根节点接受其他slave的连接,这样就形成了一个树形结构,使得Redis可以执行单层树复制
  2. 从2.8版本开始,当启动一个slave node节点,他会发送一个PSYNC命令给master node。如果slave node是第一次连接到master node,那么会触发一次全量复制。此时master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将由客户端client新收到的写命令缓存在内存中。RDB文件生成完毕后,master会将这个RDB发送给从节点,slave会先写入本地磁盘,然后再从本地磁盘加载到内存中,接着master会将内存中缓存的写命令发送到slave,slave也会同步这些数据。
  3. slave node如果跟master node有网络故障,会自动重连,之后master node也会发生全量复制
  4. 增量复制是在全量复制之后进行的,当从节点已经追赶上主节点的数据状态后,它可以通过增量复制来获取主节点的最新更新。

2. 说说Redis的缓存淘汰策略

  1. 惰性删除:客户端访问一个key的时候,Redis会先检查它的过期时间,如果发现过期就立刻删除这个key
  2. 定期删除:redis会将设置了过期时间的key放到一个独立字典中,并对该字典进行每秒10次的过期扫描,过期扫描不会遍历字典中所有key,而是采用了一种简单的贪心策略,该策略如下:
    1. 从过期字典中随机选择20个key
    2. 删除这20个key中已经过期的key
    3. 如果已经过期的key的比例超过了25%,则重复步骤1
  3. 当写入数据超出max-memory限制的时候,Redis会采用max-memory-policy所制定的策略进行数据淘汰。最常用的还是LRU和LFU算法。
  4. LRU即最近最少使用原则。但该算法有一个明显的不足之处在于,若一个key很少被访问,只是刚刚被偶尔访问了一次,则它就会被认为是热点数据,短时间内不会被淘汰
  5. LFU算法是根据key的最近访问频率进行淘汰。LFU在LRU的基础上,为每个数据增加了一个计数器,来统计这个数据的访问次数。当使用LFU策略淘汰数据时,首先会根据数据的访问次数进行筛选,把访问次数最低的数据淘汰出内存。如果两个数据的访问次数相同,LFU再比较这两个数据的访问时间,把访问时间更早的数据淘汰出内存

3. 说说缓存穿透、击穿、雪崩的区别

  1. 缓存穿透:是指客户端查询了根本不存在的数据,使得这个请求直达存储层,导致其负载过大甚至造成宕机。这种情况可能是由于业务层误将缓存和库中的数据删除造成的,当然也不排除有人恶意攻击,专门访问库中不存在的数据导致缓存穿透。我们可以通过缓存空对象和布隆过滤器的方式来解决。
  2. 缓存空对象是指当存储层也未命中,仍然将空值存入缓存层,当客户端再次访问数据时,缓存层直接放回空值
  3. 布隆过滤器是在访问缓存前,由过滤器拦截,若请求的数据不存在则直接返回空值
  4. 缓存击穿:当一份访问量非常大的热点数据缓存失效的瞬间,大量的请求直达存储层,导致服务崩溃。缓存击穿可以通过热点数据不设置过期时间来解决,这是“物理”上的永不过期。或者为每个数据设置逻辑过期时间,当发现该数据逻辑过期时,使用单独的线程重建缓存。我们也可以加互斥锁来解决缓存击穿问题,当一个线程通过缓存击穿访问到数据库,阻塞其他访问线程,同时缓存中的数据将被重建,之后其他被阻塞的线程就可以直接从缓存中取值
  5. 缓存雪崩:是指当某一时刻缓存层无法继续提供服务,导致所有请求直达存储层,导致数据库宕机。可能是缓存中有大量数据同时过期,也可能是Redis节点发生故障,导致大量请求无法得到处理。
  6. 缓存雪崩的解决方式有三种:第一种是在设置过期时间时,附加一个随机数,避免大量key同时过期。第二种是启动降级和熔断措施,即发生雪崩时,若应用访问的不是核心数据,则直接放回预定义信息/空值/错误信息。或者在发生雪崩时,对于访问缓存接口中的请求,客户端不要把请求发给Redis,而是直接放回。第三种是构建高可用的Redis服务,也就是采用哨兵或集群模式,部署多个Redis实例,这样即使个别节点宕机,依然可以保持服务的整体可用

4. Redis如何与数据库保持双写一致性

  1. 共有四种同步策略

    1. 先更新数据库再更新缓存。缺点是在多线程并发下会存在数据库中数据和缓存不一致的现象

    2. 先更新缓存再更新数据库,优点是每次数据变化都可以及时更新缓存,但是消耗很大,影响服务器性能

    3. 先删除缓存再更新数据库,缺点是仍然会导致数据库和缓存不一致

    4. 先更新数据库再删除缓存,缺点是仍然可能出现缓存和数据库中的数据不一致的情况,但是我们可以使用重试机制操作,包括业务层进行重试和消息队列重试,业务层重试会对代码有侵入性,所以更常用的还是使用中间件进行重试删除,所以这是效果最好的解决方案

    5. 综上,以上四种都不能保证数据库和缓存绝对的一致性,比较好的策略就是牺牲一些性能,做延时双删,但是延时时间不好确定

    6. 延时双删的具体步骤如下

      • 在修改数据库数据前,需要先删除一次redis,此时是为了保证在数据库修改之前的时间间隔内,如有命中,保证数据不存在于redis中,如果没有这一次删除,会出现脏读
      • 第二次删除时在修改数据库之后删除,这一次删除是由于在第一次redis删除和数据库修改之间,如有命中,那么旧数据又会重新缓存到redis中,也会出现脏读
      • 延时是为了等待脏数据写入到缓存,然后进行第二次删除脏数据
    7. 根据综合考虑,即使先修改数据库,在删除缓存,有一定的时间会导致读取到旧数据,这通常是可以被忍受的。
      只要及时将缓存删除,其他线程就可以读取到最新的值。

      同时为了保证缓存一定会被删除,可以采用mq,来保证缓存会被删除

      如果在mq中消息没有被重复消费,还会交由给其他消费者消费(将缓存删除)

5. 如何实现Redis高可用

  1. 主从复制:写一定是在主服务器上,然后主服务器同步给从服务器。缺点是当主服务器挂掉的时候,不能自动切换到从服务器上。主从服务器存储数据一样,内存可用性差。优点是在一定程度上分担主服务器读的压力。
  2. 哨兵模式:构建多个哨兵节点监视主从服务器,当主服务挂掉的时候,自动将对应的从服务器切换成主服务器。优点是实现自动切换,可用性高。缺点是主从服务器存储数据一致,内存可用性差。还要额外维护一套哨兵系统。
  3. 集群模式:采用无中心节点的方式实现。多个主服务器相连,一个主服务器又可以有多个从服务器,不同主服务器存储不同的数据。优点是可用性更高,内存可用性更高
最后修改:2024 年 06 月 08 日
如果觉得我的文章对你有用,请随意赞赏