zoukankan      html  css  js  c++  java
  • Redis内核原理及读写一致企业级架构深入剖析1综合组件环境实战

    1 Redis 工作模型

    redis实际上是个单线程工作模型,其拥有较多的数据结构,并支持丰富的数据操作,redis目前是原生支持cluster模式。如果需要缓存能够支持更复杂的结构和操作,基于以上原因,选择线上使用Redis会是不错的选择。

    1.1 Redis 高效的原因:

    • Redis高效的原因:

      1)纯内存操作

      2)核心是基于非阻塞的IO多路复用机制

      3)单线程反而避免了多线程的频繁上下文切换问题

    • mysql单机支撑到2000qps就会出现性能问题了,所以要是你有个系统,高峰期一秒钟过来的请求有1万,那一个mysql单机绝对会死掉。因为 redis单机支撑的并发量轻松一秒几万十几万,支撑高并发so easy。单机承载并发量是mysql单机的几十倍。

    • 使用Redis可能会遇到的问题:

      • 缓存与数据库双写不一致
      • 缓存雪崩
      • 缓存穿透
      • 缓存并发竞争

    1.2 文件事件处理流程

    • (1) redis基于reactor模式开发了网络事件处理器,这个处理器叫做文件事件处理器,file event handler。这个文件事件处理器,是单线程的,redis才叫做单线程的模型,采用IO多路复用机制同时监听多个socket,根据socket上的事件来选择对应的事件处理器来处理这个事件。

    • (2) 文件事件处理器的结构包含4个部分:多个socket,IO多路复用程序,文件事件分派器,事件处理器(命令请求处理器、命令回复处理器、连接应答处理器,等等)。

    • (3) 多个socket可能并发的产生不同的操作,每个操作对应不同的文件事件,但是IO多路复用程序会监听多个socket,但是会将socket放入一个队列中排队,每次从队列中取出一个socket给事件分派器,事件分派器把socket给对应的事件处理器。

    • (4) 一个socket的事件处理完之后,IO多路复用程序才会将队列中的下一个socket给事件分派器。文件事件分派器会根据每个socket当前产生的事件,来选择对应的事件处理器来处理。

    1.3 文件事件

    • 如果一个客户端跟redis发起连接,此时会产生一个AE_READABLE事件,然后由连接应答处理器来处理跟客户端建立连接,创建客户端对应的socket,同时将这个socket的AE_READABLE事件跟命令请求处理器关联起来。

    • 如果是客户端要连接redis,那么会为socket关联连接应答处理器。

    • 如果是客户端要写数据到redis,那么会为socket关联命令请求处理器。

    • 如果是客户端要从redis读数据,那么会为socket关联命令回复处理器。

    1.4 客户端与redis通信流程

    • 创建客户端对应的socket

      在redis启动初始化的时候,redis会将连接应答处理器跟AE_READABLE事件关联起来,接着如果一个客户端跟redis发起连接,此时会产生一个AE_READABLE事件,然后由连接应答处理器来处理跟客户端建立连接,创建客户端对应的socket,同时将这个socket的AE_READABLE事件跟命令请求处理器关联起来。

    • 请求处理,socket产生一个AE_READABLE事件

      当客户端向redis发起请求的时候(不管是读请求还是写请求,都一样),首先就会在socket产生一个AE_READABLE事件,然后由对应的命令请求处理器来处理。这个命令请求处理器就会从socket中读取请求相关数据,然后进行执行和处理。

    • 客户端这读取响应数据

      接着redis这边准备好了给客户端的响应数据之后,就会将socket的AE_WRITABLE事件跟命令回复处理器关联起来,当客户端这边准备好读取响应数据时,就会在socket上产生一个AE_WRITABLE事件,会由对应的命令回复处理器来处理,就是将准备好的响应数据写入socket,供客户端来读取。

    • 命令回复处理器写完之后,就会删除这个socket的AE_WRITABLE事件和命令回复处理器的关联关系。

    2 redis 过期策略及内存淘汰机制

    • 用内存当缓存。内存是无限的吗?内存是很宝贵而且是有限的,磁盘是廉价而且是大量的。可能一台机器就几十个G的内存,但是可以有几个T的硬盘空间。redis主要是基于内存来进行高性能、高并发的读写操作的。

        set key value 过期时间(1小时)
        set进去的key,1小时之后就没了,就失效了,但是数据明明都过期了,怎么还占用着内存呢?
      复制代码
    • 定期删除+惰性删除

      所谓定期删除,指的是redis默认是每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。假设redis里放了10万个key,都设置了过期时间,你每隔几百毫秒,就检查10万个key,那redis基本上就死了,cpu负载会很高的,消耗在你的检查过期key上了。注意,这里可不是每隔100ms就遍历所有的设置过期时间的key,那样就是一场性能上的灾难。实际上redis是每隔100ms随机抽取一些key来检查和删除的。

      所谓惰性删除了,指的是在你获取某个key的时候,redis会检查一下 ,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。

      如果定期删除漏掉了很多过期key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了,也会出现严重的线上事故。

    • 内存淘汰

      如果redis的内存占用过多的时候,此时会进行内存淘汰,有如下一些策略:

        redis 10个key,现在已经满了,redis需要删除掉5个key
        1个key,最近1分钟被查询了100次
        1个key,最近10分钟被查询了50次
        1个key,最近1个小时倍查询了1次
      复制代码

      1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。

      2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)

      3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的key给干掉啊。

      4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key(这个一般不太合适)

      5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key

      6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除

    3 redis 高并发和高可用企业级实践

    • redis高并发:主从架构,一主多从,一般来说,很多项目其实就足够了,单主用来写入数据,单机几万QPS,多从用来查询数据,多个从实例可以提供每秒10万的QPS。

      redis高并发的同时,还需要容纳大量的数据:一主多从,每个实例都容纳了完整的数据,比如redis主就10G的内存量,其实你就最对只能容纳10g的数据量。如果你的缓存要容纳的数据量很大,达到了几十g,甚至几百g,或者是几t,那你就需要redis集群,而且用redis集群之后,可以提供可能每秒几十万的读写并发。

    • redis高可用:如果你做主从架构部署,其实就是加上哨兵就可以了,就可以实现,任何一个实例宕机,自动会进行主备切换。

    4 redis replication基本原理

    • redis采用异步方式复制数据到slave节点,不过redis 2.8开始, slave node会周期性地确认自己每次复制的数据量

    • 一个master node是可以配置多个slave node的

    • slave node也可以连接其他的slave node

    • slave node做复制的时候,是不会阻塞master node正常工作的

    • slave node在做复制的时候,也不会block对自己的查询操作,它会用旧的数据集来提供服务; 但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了

    • slave node主要用来进行横向扩容,做读写分离,扩容的slave node可以提高读的吞吐量。

    5 Redis数据持久性重要性

    5.1 数据丢失风险

    • 如果采用了主从架构,那么建议必须开启master node的持久化,不建议用slave node作为master node的数据热备,因为那样的话,如果你关掉master的持久化,可能在master宕机重启的时候数据是空的,然后可能一经过复制,salve node数据也丢了。

      master -> RDB和AOF都关闭了 -> 全部在内存中

      master宕机,重启,是没有本地数据可以恢复的,然后就会直接认为自己IDE数据是空的

      master就会将空的数据集同步到slave上去,所有slave的数据全部清空。

      100%的数据丢失,因此master节点,必须要使用持久化机制。

    • 采用高可用机制也是有风险的,slave node可以自动接管master node,但是也可能sentinal还没有检测到master failure,master node就自动重启了,还是可能导致上面的所有slave node数据清空故障。

    5.2 主从架构和断点续传原理

    • 当启动一个slave node的时候,它会发送一个PSYNC命令给master node。如果这是slave node重新连接master node,那么master node仅仅会复制给slave部分缺少的数据; 否则如果是slave node第一次连接master node,那么会触发一次full resynchronization

      开始full resynchronization的时候,master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端收到的所有写命令缓存在内存中。RDB文件生成完毕之后,master会将这个RDB 发送给slave,slave会先写入本地磁盘,然后再从本地磁盘加载到内存中。然后master会将内存中缓存的写命令发送给slave,slave也会同步这些数据。

      slave node如果跟master node有网络故障,断开了连接,会自动重连。master如果发现有多个slave node都来重新连接,仅仅会启动一个rdb save操作,用一份数据服务所有slave node。

    • 从redis 2.8开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份

      master node会在内存中常见一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制

      但是如果没有找到对应的offset,那么就会执行一次resynchronization

    • 无磁盘化复制,master在内存中直接创建rdb,然后发送给slave,不会在自己本地落地磁盘了,

      repl-diskless-sync
      repl-diskless-sync-delay,等待一定时长再开始复制,因为要等更多slave重新连接过来
      复制代码
    • 过期key处理

      slave不会过期key,只会等待master过期key。如果master过期了一个key,或者通过LRU淘汰了一个key,那么会模拟一条del命令发送给slave。

    5 Redis主从复制原理深入剖析

    5.1 完整流程

    • slave node启动,仅仅保存master node的信息,包括master node的host和ip,但是复制流程没开始时, master host和ip是从哪儿来的,是在redis.conf里面的slaveof配置的。
    • slave node内部有个定时任务,每秒检查是否有新的master node要连接和复制,如果发现,就跟master node建立socket网络连接
    • slave node发送ping命令给master node
    • 口令认证,如果master设置了requirepass,那么salve node必须发送masterauth的口令过去进行认证
    • master node第一次执行全量复制,将所有数据发给slave node
    • master node后续持续将写命令,异步复制给slave node

    5.2 数据同步

    • master和slave都会维护一个offset

      master会在自身不断累加offset,slave也会在自身不断累加offset slave每秒都会上报自己的offset给master,同时master也会保存每个slave的offset

      这个倒不是说特定就用在全量复制的,主要是master和slave都要知道各自的数据的offset,才能知道互相之间的数据不一致的情况。

    • backlog

      master node有一个backlog,默认是1MB大小

      master node给slave node复制数据时,也会将数据在backlog中同步写一份

      backlog主要是用来做全量复制中断后增量复制的

    • master run id

      info server,可以看到master run id,如果根据host+ip定位master node,是不靠谱的,如果master node重启或者数据出现了变化,那么slave node应该根据不同的run id区分,run id不同就做全量复制 如果需要不更改run id重启redis,可以使用redis-cli debug reload命令

    • psync

      从节点使用psync从master node进行复制,psync runid offset

      master node会根据自身的情况返回响应信息,也可能是FULLRESYNC runid offset触发全量复制,可能是CONTINUE触发增量复制。

    5.3 全量复制

    • master执行bgsave,在本地生成一份rdb快照文件
    • master node将rdb快照文件发送给salve node,如果rdb复制时间超过60秒(repl-timeout),那么slave node就会认为复制失败,可以适当调节大这个参数
    • 对于千兆网卡的机器,一般每秒传输100MB,6G文件,很可能超过60s
    • master node在生成rdb时,会将所有新的写命令缓存在内存中,在salve node保存了rdb之后,再将新的写命令复制给salve node
    • client-output-buffer-limit slave 256MB 64MB 60,如果在复制期间,内存缓冲区持续消耗超过64MB,或者一次性超过256MB,那么停止复制,复制失败
    • slave node接收到rdb之后,清空自己的旧数据,然后重新加载rdb到自己的内存中,同时基于旧的数据版本对外提供服务
    • 如果slave node开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF
    • rdb生成、rdb通过网络拷贝、slave旧数据的清理、slave of rewrite,很耗费时间,如果复制的数据量在4G~6G之间,那么很可能全量复制时间消耗到1分半到2分钟。

    5.4 增量复制

    • 如果全量复制过程中,master-slave网络连接断掉,那么salve重新连接master时,会触发增量复制
    • master直接从自己的backlog中获取部分丢失的数据,发送给slave node,默认backlog就是1MB
    • msater就是根据slave发送的psync中的offset来从backlog中获取数据的。

    5.5 heartbeat

    • 主从节点互相都会发送heartbeat信息,master默认每隔10秒发送一次heartbeat,salve node每隔1秒发送一个heartbeat。

    5.6 异步复制

    master每次接收到写命令之后,先在内部写入数据,然后异步发送给slave node

    6 总结

    在此感谢石杉的讲义,结合大数据在我们工业大数据平台的实践,总结成一篇实践指南,方便以后查阅反思,后续我会根据本篇博客进行代码技术实践实现。

  • 相关阅读:
    联赛练习:好数
    CF703D Mishka and Interesting sum
    POJ2689 Prime Distance
    联赛练习:draw
    题解:luogu P2634 [国家集训队]聪聪可可
    题解:luoguP3806 【模板】点分治1(在线处理询问做法)
    题解:luogu P1073 最优贸易
    题解报告——聪聪与可可
    题解报告——森林
    题解报告——星际战争
  • 原文地址:https://www.cnblogs.com/dashujunihaoa/p/10905700.html
Copyright © 2011-2022 走看看