简读笔记-Redis设计与实现第三章

第三部分 多机数据库的实现

复制

1.旧版复制功能的实现

  • 旧版复制分为两个阶段 : 同步命令传播

  • 同步过程的执行步骤

    • 从服务器向主服务器发送SYNC命令
    • 收到SYNC命令后,主服务器开始执行BGSAVE操作生成RDB文件,并使用一个缓冲区记录现在开始执行的所有写命令(用于命令传播阶段保持数据库一致性)。
    • 当主服务器的BGSAVE操作执行完时,主服务器会将BGSAVE命令生成的RDB文件发送给从服务器,从服务器接收并载入这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态。
    • 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令。将自己数据库状态更新至主服务器数据库当前状态。
  • 命令传播的过程

    • 主服务器将执行的写命令,发送给从服务器执行,当从服务器执行了相同写命令后,主从服务器将再次回到一致性状态
  • SYNC命令流程图

    image

2.旧版复制功能的缺陷

  • 在从服务器A断线的情况(此时保存了A1 - A99键值对),当服务器重连上主服务器时 (此时主服务器保存到了A120), 从服务器会再次向主服务器发送SYNC命令,此时主服务器又需要将数据库的所有键值对保存到RDB文件中(A1-A120键值对),然而从服务器A其实只需要掉线时期产生的A100-A120数据就能恢复成一致性状态。 所以为了让从服务器补足一小部分缺失的数据,而让主服务器重新执行一次BGSAVE操作,造成断线重连后的效率会很低。

3.新版复制功能的实现

  • 如何提高断线重连后同步的效率呢? 实际上我们只需要保存断线后的那部分写命令,重连后让主服务器再发送过来给从服务器执行就好了。
  • 新版复制解决了旧版复制在处理断线重连时的低效情况。使用PSYNC命令代替SYNC命令执行复制时的同步操作。
  • PSYNC命令具有完整重同步部分重同步两种模式
    • 完整重同步用于处理初次复制的情况:与SYNC命令的执行步骤基本一致 , 都是通过让主服务器创建并发送RDB文件,以及向从服务器发送保存在缓冲区里面的写命令来进行同步。
    • 部分重同步用于处理断线后重复制的情况:当从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,从服务器只要接收并执行这些写命令,就可以将数据库更新至主服务器当前所在状态了。
  • 由于部分重同步只需要将服务器缺少的写命令发送给从服务器执行旧可以了,因此所需资源更少,速度更快。

4.部分重同步的实现

  • 复制偏移量

    • 主服务器和从服务器都维护一个复制偏移量。 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N。

    • 从服务器每次收到主服务器传播来的N个字节的数据时,就将自己的复制偏移量加上N

    • 可以很容易通过判断主从服务器的复制偏移量来判断是否处于一致的状态

      image

    如果发现偏移量不一致时,如何判断接下来要进行完整重同步 or 部分重同步。主服务器又如何补偿掉线期间丢失的数据呢?与复制积压缓冲区有关。

  • 复制积压缓冲区

    • 复制积压缓冲区维护一个固定长度的先进先出队列,默认大小为1MB

      image
    • 当服务器进行命令传播时,不仅将命令发送给所有从服务器,还将写命令入队到复制积压缓冲区中

      image
    • 当从服务器重连主服务器时,从服务器通过PSYNC命令将复制偏移量offset也发送给主服务器,如果offset偏移量之后的数据仍存在复制积压缓冲区中,则执行部分重同步操作 ; 而如果offset偏移量之后的数据已不存在复制积压缓冲区中(掉线太久),则执行完整重同步操作。

    • 复制积压缓冲区的最小大小应为 平均重连时间 * 主服务器每秒产生的写命令数量 ; 为了保证大部分掉线情况都能使用部分重同步, 应将复制积压缓冲区设为 2*平均重连时间 * 主服务器每秒产生的写命令数量

  • 服务器运行ID

    • 用于记录判断 断线重连后的服务器是否为 原先连接的服务器
    • 当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个ID保存起来。 当从服务器重新连接时,从服务器向主服务器发送之前保存的服务器运行ID
      • 如果从服务器保存的ID和 当前连接的主服务器运行ID相同,则说明断线前复制的就是当前的主服务器,则再根据复制积压缓冲区判断进行部分重同步 还是 完整重同步
      • 相反,则说明线前复制的不是当前的主服务器,则需要进行完整重同步操作。

5.PSYNC命令的实现

  • PSYNC <runid> <offset> : runid表示上一次复制的主服务器运行ID , offset表示当前复制偏移量
  • 主服务器返回 FULLRESYNC <runid> <offset> : 表示执行完整重同步操作,offset作为从服务器的初始偏移量
  • 主服务器返回 CONTINUE : 表示执行部分重同步操作。 只需等待主服务器将缺失数据重发即可。

6.复制的实现

SLAVEOF <master_ip> <master_port>

  1. 设置主服务器的地址和端口号
  2. 建立套接字连接(让从服务器成为主服务器客户端)
  3. 发送PING命令 (检测主服务器连接状况)
  4. 身份验证
  5. 发送端口信息(让主服务器保存从服务器的相关信息)
  6. 同步(发送PSYNC命令,执行同步操作。 并且由单工通信编程双工通信,即主从服务器互相成为对方的客户端)
  7. 命令传播
  8. 心跳检测(从服务器会每秒1次的频率,向主服务器发送命令 REPLCONF ACK <从服务器偏移量>
    • 检测主从服务器网络连接状态
    • 复制实现min-slaves功能 (当主服务器的从服务器数量小于min-slaves时,则认为集群挂了)
    • 检测命令丢失(主服务器进行部分重同步时由于网络问题而丢失,则通过心跳检测到后进行补发)

总结

  • 当客户端向从服务器发送SLAVEOF命令,要求从服务器复制主服务器时,从服务器首先需要执行同步操作。将从服务器的数据库丛台更新至主服务器当前所处的数据库状态。 在同步操作完成后,这种一致性的状态不是一成不变的,当主服务器执行客户端的写命令时,主从服务器的状态旧不再一致, 因此主服务器需要对从服务器执行命令传播操作, 即主服务器将执行的写命令,发送给从服务器执行。
  • Redis 2.8以前的复制功能不能高效地处理断线后重复制情况,但Redis2.8新添加的部分重同步功能可以解决这个问题。
  • 部分重同步通过复制偏移量复制积压缓冲区服务器运行ID三个部分来实现。
  • 在复制操作刚开始的时候,从服务器会成为主服务器的客户端,并通过向主服务器发送命令请求来执行复制步骤。 而在复制的后期(同步阶段),主从服务器会互相成为对方的客户端 (主服务器发送命令(命令传播,部分重同步)给从服务器来维护数据一致性)
  • 主服务器通过向从服务器传播命令来更新从服务器的状态,保证主从服务器一致,而从服务器则通过向主服务器发送命令来进行心跳检测,以及命令丢失检测。

Sentinel哨兵机制

Sentinel是Redis的高可用性解决方案:由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器下的从服务器,并在被监视的主服务器进行下线状态时,自动将被下线主服务器下的某个重服务器升级为主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求(失效转移

Sentinel的构成

  • Setinel启动时的执行步骤

    • 初始化服务器(Sentinel本质上只是运行在特殊模式下的Redis服务器, 部分初始化过程不相同)
    • 将普通Redis服务器使用的代码替换成Sentinel专用代码(如没有get,set 而有ping,INFO)
    • 初始化Sentinel状态
    • 初始化Sentinel的监视主服务器列表
    • 创建连向主服务器的网络连接 (创建命令连接和订阅连接; 前者用于向主服务器发送命令 。 而后者是订阅主服务器的 _sentinel_hello频道,用于接收其他主服务器信息的变更的频道信息)
  • Setineal通过读取配置文件,使用字典记录所有的Master; 并通过给主服务器发送INFO命令,得出主服务器下的所有从服务器信息。

image

如何获取主服务器信息

  • Sentinel默认会以每10秒一次的频率,通过命令连接向被监视的主服务器发送INFO命令,并通过分析INFO命令的回复来获取主服务器的当前信息(状态,从服务器slaves信息)

如何获取从服务器信息

  • 当Sentinel发现主服务器有新的从服务器出现时,Sentinel除了会为这个新的从服务器创建相应实例结构外,Sentinel还会创建连接到从服务器的命令连接订阅连接

    • 创建命令连接后,每10秒向从服务器发送INFO命令

      • 获取从服务器的运行ID,角色,优先级,偏移量, 该从服务器对应的主服务器连接信息,状态

向主从服务器发送消息

在默认情况下,Sentinel会以每两秒一次的频率,通过命令连接向所有被监视的主服务器和从服务器发送订阅消息

PUBLISH sentinel_:hello <sentinel的信息,主服务器的信息>

接收来自主服务器和从服务器的频道信息

  • 当Sentinel与一个主服务器或者从服务器建立建立起订阅连接之后,Sentinel就会通过订阅连接,向服务器发送以下命令。一直持续到Sentinel与服务器的连接断开为止。

  • 对于监视同一个服务器的多个Sentinel来说,一个Sentinel发送的信息会被其他Sentinel接收到,这些信息会被用于更新其他Sentinel对发送信息Sentinel的认知,也会被用于更新其他Sentinel对被监视服务器的认知

image

检测主观下线状态

  • 在默认情况下,Sentinel会以每秒一次的频率向所有与它创建了命令连接的实例(包括主服务器,从服务器,其他Sentinel在内)发送PING命令,并通过实例返回的PING命令回复来判断实例是否在线。
  • Sentinel配置文件中的 down-after-milliseconds指定了判断实例主观下线所需的时间长度。如果在down-after-milliseconds时间内连续发送了无效回复,则Sentinel将判定该实例主观下线,并将对应的实例结构的flags设置为 SRI_S_DOWN。 (Subjective 主观)

检测客观下线状态

  • 当Sentinel将一个主服务器判断为主观下线后,为了确认这个主服务器是否真的下线了,它会向同样监视这一主服务器的其他Sentinel进行询问,看它们是否也认为主服务器已经进入了下线状态。 当Sentinel从其他Sentinel那里接收到足够数量的已下线判断后,Sentinel就会将从服务器判断为客观下线,并将主服务器执行故障转移操作。
  • 当判断为客观下线后,将flags设置为 SRI_O_DOWN (Objective 客观)

如何选举领头Setinel

  • 当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个Sentinel会进行协商,选举出一个领头Sentinel,并由领头Sentinel对下线主服务器执行故障转移操作
##请求格式
Sentinel is-master-down-by-addr  <master的ip地址> <端口号>  <配置纪元>(标志是否同一轮选举) <SentinelId>

##响应格式
<上下线状态> <局部领头id> <当前纪元>

故障转移

  1. 在已下线主服务器属下的所有从服务器里面,挑选出一个从服务器,并将其转换为主服务器(升级
  2. 让已下线主服务器属下的所有从服务器改为复制新的主服务器
  3. 将已下线主服务器设置为新的主服务器的从服务器,当这个旧的服务器重新上线时,它就会成为新的主服务器的从服务器(降级

集群

Redis集群是Redis提供的分布式数据库方案,集群通过分片来进行数据共享,并提供复制和故障转移功能。

主要内容有集群中的节点,槽指派,命令执行,重新分片,转向,故障转移,消息等方面

  • 节点通过握手来将其他节点添加到自己所处的集群当中 (clustermeet ip port)

  • 集群中的16384个槽可以分别指派给集群中的各个节点,每个节点都会记录哪些槽指派给了自己,哪些槽又被指派给了其他节点。

  • clusterState.slots数组记录了集群中所有槽的指派信息,使得能以O(1)时间判断某个槽指派给了哪个节点;而clusterNode.slots数组只记录了clusterNode结构所代表的节点的槽指派信息。使得交换槽信息时只需发送clusterNode.slots数组,让接收信息的节点更新字典表中对应的clusterNode即可。而不用遍历clusterState中所有元素,标志当前节点所指派的槽,再将其信息进行传送,时间复杂度为O(n)。下图为节点7000创建的clusterState结构。

image
  • 节点在接收到一个命令请求时,会先检查这个命令请求要处理的键所在的槽是否由自己负责(查看clusterState.slots数组),如果不是的话,节点将向客户端返回一个MOVED操作, MOVED错误携带的信息可以指引客户端转向至正在负责相关槽的节点。

  • 节点还会使用跳跃表来保存槽与键之间的关系 (方便比如 查看 属于某个slot的数据库键由哪些的命令)

  • 对Redis集群的重新分片工作是由redis-trib负责的,重新分片的关键是将属于某个槽的所有键值对从一个节点转移到另一个节点。

    image
  • 如果节点A正在迁移槽i到节点B,那么当节点A没能在自己的数据库中找到命令指定的数据库键时,节点A会向客户端返回一个ASK错误,指引客户端到节点B继续查找指定的数据库键。

  • MOVED错误表示槽的负责权已经从一个节点转移到了另一个节点,而ASK错误只是两个节点在迁移槽的过程中使用的一种临时措施。

  • 集群里的从节点用于复制主节点,并在主节点下线时,代替主节点继续处理请求。

    image
  • 集群中的节点通过发送和接收消息来通信,常见的消息包括MEET,PING,PONG,PUBLISH,FAIL五种。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容