Redis热点问题

55 阅读12分钟
缓存穿透
  • 当向redis或者数据库发起请求查询数据时,在redis缓存和数据库中都查询不到数据,即无效请求。称为缓存穿透。

  • 解决方案

    • 缓存空对象

      • 当从redis中查询不到数据,向数据库发起请求也查不到数据时,往redis中写入一个空对象,避免后续的请求全部到达数据库。
    • 布隆过滤器

      • 在客户端发起请求查询时,先通过布隆过滤器判断数据库中是否存在该数据,如果数据库中不存在,则拒绝该请求。redis和数据库就不会再处理该请求。
缓存雪崩
  • redis中大量的key在同一时间过期或者redis服务器宕机,导致大量的请求同时到达数据库,导致数据库压力过大。称为缓存雪崩。

  • 解决方案

    • 集群部署redis,当主节点过期时,可以临时调用从节点进行工作,避免redis直接瘫痪。
    • 设置多级缓存机制
    • 进行缓存限流
    • 在为key设置过期时间(TTL)时在后面加上一个随机时间,从而避免大量的key同时过期。
缓存击穿
  • 当一个被高并发访问或者重建缓存业务较复杂的热key失效,导致大量的请求到达数据库。称为缓存击穿,也叫热key问题

  • 解决方法

    • 增加互斥锁,当从redis中查询不到数据时,首先获取锁然后再转向数据中查询,在查询到数据后,将查询到的数据写入redis缓存,再释放锁。
    • 逻辑过期
持久化
AOF
  • 客户端发送写命令到redis,redis第一步会执行写命令,写入到内存,第二步会将命令记录到日志,写入到硬盘。在redis服务重启时,首先会读取日志文件,将数据写入到内存中,从而实现持久化。

  • redis的三种写回策略,redis.conf 配置文件中的 appendfsync可以设置三个参数

    • Always,在每次执行完写操作之后,同步执行将日志写入到硬盘
    • Everysec,在写操作执行完后,先将命令写入到AOF日志的内核缓冲区,然后每隔一秒将缓冲区的内容写入到硬盘
    • No,在写操作执行完后,先将命令写入到AOF日志的内核缓冲区,然后交由操作系统去决定何时写入硬盘
  • 三种写回策略如何选择

    • 如果要高性能,选择No
    • 如果要高可靠,选择Always
    • 如果允许数据丢失一点,又想要性能高,选择Everysec
  • 为避免AOF日志文件过大,从而造成性能降低,redis提供了AOF重写机制,当AOF文件大小超过设定的阈值,redis就会启用AOF重写机制来压缩AOF文件。如下所示,在压缩后set name zhangsan就不存在了。

  • >>set name zhangsan
    >>set name lisi
    >>set age 18
    >>set score 90
    
RDB快照
  • 所谓快照,就是记录某一瞬间的东西,所以RDB快照记录的就是某一瞬间内存中的数据,记录的是实际的数据,而AOF文件记录的是命令操作的日志。因此在 Redis 恢复数据时, RDB 恢复数据的效率会比AOF高些,直接将RDB文件读入内存就可以,而AOF还需要执行指令才能将数据写入内存。

  • redis提供了两个指令来生成RDB文件,save和bgsave

    • save

      • 执行了save命令,会在主线程生成RDB文件,如果文件较大,则进程会发生线程
    • bgsave

      • 执行了bgsave命令,会创建一个子线程来生成RDB文件,从而避免了线程阻塞
主从复制
  • 虽然redis提供了AOF和RDB两种持久化方式,但在单服务器模式下,如果服务器宕机,在恢复这段时间内便无法提供工作,或者硬盘损坏,数据则会直接丢失。redis提供了主从复制来解决这个问题,将数据备份到其他服务器上,这样即使主服务器发生异常,从服务也依旧可以继续工作。

    写操作只发生在主服务器,当发生写操作时自动同步到从服务器,也就是说,数据的修改只发生在主服务器,然后将最新的数据同步到从服务器。

    • 主从服务器之间的第一次同步

      • 如何区分主服务器和从服务器?

      • //服务器A   服务器B
        服务器B>>replicaof 服务器A_IP redis端口
        //这样便可以把服务器B作为服务器A的从服务器
        
      • 第一次同步分为三个阶段

        • 1.建立连接,协商同步

          • 执行replicaof命令后,从服务器会发送一个psync指令给主服务器,表示要同步,psync指令包含两个参数,主服务器runID和复制进度offset。主服务器收到指令后,会返回fullresync(全量复制)指令给对方
        • 2.主服务器同步数据给从服务器

          • 主服务器执行bgsave生成RDB文件,然后发送给从服务器。从服务器收到RDB文件后会先清空当前数据,然后载入RDB文件。主服务器生成RDB文件期间,并不会发生阻塞,因为bgsave是产生了一个子进程来工作,redis依然可以正常处理命令。在主服务器生成 RDB 文件期间主服务器发送 RDB 文件给从服务器期间「从服务器」加载 RDB 文件期间收到的写操作命令都会写入replication buffer缓冲区
        • 3.主服务器发送新的写操作到从服务器

          • 从服务器完成RDB文件的载入后,会回复一个确认消息给主服务器,接着主服务器就会将replication buffer缓冲区的写操作命令同步到从服务器,从而达到数据的一致性。
      • 如果从服务器数量较多,主服务器与各个从服务器都进行全量复制的话就会影响redis性能。就像一个公司,刚成立时人少,老板一人便可以直接管理,但如果人员扩充,老板管理的压力便会增大,要解决这个问题,便可以设立一个经理,让经理来管理普通员工,老板管理经理就好。redis也一样,可以让从服务器来承担经理角色,它不仅接收主服务器同步的数据,又可以将数据同步给自己的从服务器。将主服务器生成和传输RDB文件的压力分摊到从服务器。

      • 增量复制

        • 如果主服务器和从服务器之间突然断开网络,那么就无法进行命令传播,这时主从服务器之间就无法保持数据的一致性,客户端便可能从从服务器读取到旧的数据。

        • 若断开的网络突又恢复正常,要怎么保持数据的一致性呢?

          • 在redis2.8之前,主从服务器之间会进行一次全量复制,以保持数据的一致性,但此方式开销较大
          • redis2.8以后,主从服务器之间采用增量复制的方式继续同步,也就是把网络断开期间主服务器收到的写操作命令同步给从服务器。
        • 网络恢复后的增量复制

          • 从服务器在恢复网络后,会发送 psync 命令给主服务器,此时psync命令里的offset参数不是-1
          • 主服务器接收到该命令后,然后用CONTINUE响应命令告诉从服务器接下来采用增量复制的方式进行同步
          • 然后主服务器会将网络断开期间的写操作命令发送给从服务器,然后从服务器执行执行这些命令同步数据
        • 那么主服务器怎么知道要将哪些增量数据发送给从服务器呢?

          • repl_backlog_buffer是一个环形缓冲区,用于主从服务器断开后,从中找到差异数据

          • replication_offset标记repl_backlog_buffer缓冲区的同步进度,主服务器用master_repl_offset来记录自己写到的位置,从服务器使用slave_repl_offset来记录自己读到的位置

          • repl_backlog_buffer缓冲区是何时写入的?

            • 主服务器在进行命令传播时,不仅会将写命令发送到从服务器,还会将命令写入repl_backlog_buffer缓冲区,网路断开后,当服务器重新连接上主服务器时,从服务器会通过psync命令将自己的偏移量slave_repl_offset发送给主服务器,主服务器根据自己的master_repl_offset和slave_repl_offset之间的差距,来决定采取哪种同步操作

              • 如果判断出从服务器要读取的数据还在repl_backlog_buffer缓冲区,则增量复制
              • 如果判断出从服务器要读取的数据不在repl_backlog_buffer缓冲区,则全量复制
          • repl_backlog_buffer的大小只有1MB,由于它是一个环形缓冲区,所以当缓冲区写满后,主服务器继续写入的话,就会覆盖之前的数据。因此当主服务器的写入速度远超于从服务器的读取速度,缓冲区里的数据就会被一下覆盖掉。那么在网络恢复时,从服务器想要读取的数据已经被覆盖,便会采取全量复制。因此,为避免在网络恢复时,主服务器频繁使用全量复制,应调整repl_backlog_buffer缓冲区大小,尽量大些

哨兵
  • 在redis的主从架构中由于主从模式是读写分离的,客服端的写操作请求只发生在主节点,然后主节点再将数据同步到从节点。如果主服务器挂了,那么客户端的写操作便无法执行,数据便无法同步。如果要恢复,便需要人工介入,将某个从节点重新作为主节点,并告知客户端以及别的从节点。在redis2.8版本以后,提供了哨兵机制来解决这个问题。它的作用是主从节点故障转移,在主节点挂掉后,选取新的主节点,并告知客户端以其他从节点。

  • 作用:

    • 监控,选主,通知
  • 哨兵节点是如何监控节点的?又是如何判断主节点真的挂了?

    • 哨兵每隔一秒会给所有主从节点发送PING命令,当主从节点接收到命令后会发送一个响应给哨兵,这样便可以判断它们是否在正常运行。
    • 如果主节点或者从节点没在规定时间(down-after-milliseconds)响应PING命令,那么哨兵就会将他们标记为主观下线。除了主观下线,还有客观下线(针对主节点而言),因为主节点可能会因为网络阻塞而未在规定时间响应,从而发生误判。为了减少误判,哨兵部署时一般会部署多个节点(奇数)部署成哨兵集群,共同决策。
    • 当一个哨兵节点挂了(主观下线)后,就会向其它哨兵发起命令,其他哨兵收到命令后,就会根据自身和主节点的网络状况,做出赞成或者反对。当赞同票数达到哨兵配置文件中的 quorum 配置项设定的值后,主节点就会被认为客观下线。(quorum 配置项设定的值一般设置为哨兵个数的一半+1)
  • 那么该由哪一个哨兵进行故障转移?

    • 在哨兵集群中选出一个leader,由它来执行主从切换。选取leader前有一个候选者,当哪个哨兵节点判断主节点为客观下线,这个哨兵节点就是候选者,所谓候选者就是想成为leader的哨兵。
  • 候选者如何成为leader?

    • 候选者会向其他哨兵发出命令,表示希望成为leader来执行主从切换,并让其他哨兵进行投票。每个哨兵只有投一票的机会。用完后就不能参与投票了,可以投给自己或投给别人,但是只有候选者才能把票投给自己。候选者如果满足:1.拿到半数以上赞成票2.拿到的赞成票数大于等于哨兵配置文件中的quorum 值,便可以成为leader.
  • 为什么哨兵节点至少要有三个?

    • 如果集群中只存在两个哨兵节点,此时若有一个哨兵想成为leader,必须获得2票,所以,若果哨兵集群中有个哨兵挂掉,那就只剩一个,如果有哨兵想成为leader,就只能拿到一票,无法成为leader,无法进行主从切换。因此通常至少会配置3个节点。
  • 主从故障转移的过程是怎样的?

    • 1、在已下线的主节点的所有从节点中挑选出一个从节点,将其转换为主节点。
    • 2、让已经下线的主节点下的所有从节点修改复制目标为新主节点。
    • 3、将新主节点的IP地址和信息通过发布者/订阅者机制通知给客户端。
    • 4、继续监视旧的主节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点。
  • 如何从众多从节点中选取出新的主节点呢?

    • 1、首先过滤掉网络连接状态不好的从节点。redis有个叫down-after-milliseconds*10的配置项,其down-after-milliseconds是主从节点断连的最大连接超时时间。如果down-after-milliseconds毫秒内,主从节点没有通过网络联系上,就可以认为主从节点断连了。如果断连次数超过10次,就可以认为网络状况不好,不适合作为新的主节点。
    • 2、考察优先级、复制进度、ID号,首先选取优先级较高的从节点,优先级高的节点胜出。如果优先级相同,则考虑复制进度,复制进度靠前的节点胜出。如果复制进度相同,则考虑ID号,ID号小的从节点胜出。