zoukankan      html  css  js  c++  java
  • Redis分布式原理

    1、Redis集群

      redis集群主要有以下优势:

      (1)提升性能:虽然redis本身的QPS已经很高,但是在并发量非常高的情况下,性能还是会受影响,通过集群可以进一步提升服务的性能。

      (2)存储扩展:redis是内存数据库,数据量较大时,需要横向扩展来增加存储量。

      (3)提高服务可用性和数据安全性:如果只有一个redis服务,一旦宕机所有客户端都无法访问,而且如果硬件发生故障,单机的数据无法恢复,也会造成很大的影响。

      可用性、数据安全、性能都可以通过搭建多个Reids服务实现。其中有一个是主节点(master),可以有多个从节点(slave)。主从之间通过数据同步,存储完全相同的数据。如果主节点发生故障,则把某个从节点改成主节点。

    2、Redis主从复制

    2.1、主从复制配置

      例如一主多从,192.168.8.203是主节点,在每个slave节点的redis.conf配置文件增加如下一行配置:

    slaveof 192.168.8.203 6379

      或者在启动服务时通过参数指定master节点:

    ./redis-server --slaveof 192.168.8.203 6379

      或在客户端直接执行slaveof xx xx,使该Redis实例成为从节点。

      启动后查看集群状态:info replication。

      从节点不能写入数据(只读),只能从master节点同步数据,主节点写入后,slave会自动从master同步数据。断开复制:slaveof no one,此时从节点变成自己的主节点,不再复制数据。

    2.2、主从复制原理

    (1)连接阶段

      1)slave node启动时(执行slaveof命令),会在自己本地保存master node的信息,包括master node的host和ip。

      2)slave node内部有个定时任务replicationCron(源码replication.c),每隔1秒钟检查是否有新的 master node 要连接和复制,如果发现,就跟master node 建立socket网络连接,如果连接成功,从节点为该socket建立一个专门处理复制工作的文件事件处理器,负责后续的复制工作,如接

    收RDB文件、接收命令传播等。

      当从节点变成了主节点的一个客户端之后,会给主节点发送ping请求(心跳机制)。

    (2)数据同步阶段

      3)master node 第一次执行全量复制,通过bgsave命令在本地生成一份RDB快照,将RDB快照文件发给slavenode(如果超时会重连,可以调大repl-timeout 的值)。slave node首先清除自己的旧数据,然后用RDB文件加载数据。

    *  生成RDB期间,master接收到的命令怎么处理?

      开始生成RDB文件时,master会把所有新的写命令缓存在内存中。在 slavenode保存了RDB之后,再将新的写命令复制给 slave node。

    (3)命令传播阶段

      4)master node 持续将写命令,异步复制给slave node

    *  延迟问题

      延迟是不可避免的,只能通过优化网络。可以通过参数设置:

    repl-disable-tcp-nodelay no

      当设置为yes时,TCP 会对包进行合并从而减少带宽,但是发送的频率会降低,从节点数据延迟增加,一致性变差;具体发送频率与 Linux内核的配置有关,默认配置为40ms。当设置为no时,TCP会立马将主节点的数据发送给从节点,带宽增加但延迟变小。

      一般来说,只有当应用对 Redis数据不一致的容忍度较高,且主从节点之间网络状况不好时,才会设置为yes;多数情况使用默认值no。

    *  如果从节点有一段时间断开了与主节点的连接是不是要重新全量复制一遍?如果可以增量复制,怎么知道上次复制到哪里?

      根据master_repl_offset记录的偏移量,通过info replication可以查看偏移量。

    2.3、主从复制的不足之处

      主从模式解决了数据备份和性能(通过读写分离)的问题,但是还是存在一些不足:

      1)RDB文件过大的情况下,同步非常耗时。

      2)在一主一从或者一主多从的情况下,如果主服务器挂了,对外提供的服务就不可用了,单点问题没有得到解决。如果每次都是手动把之前的从服务器切换成主服务器,这个比较费时费力,还会造成一定时间的服务不可用。

    3、高可用之Sentinel

    3.1、Sentinel 原理  

      通过sentinel如何实现主从自动切换?创建一台监控服务器来监控所有Redis服务节点的状态,比如,master节点超过一定时间没有给监控服务器发送心跳报文,就把master标记为下线,然后把某一个slave变成master。应用每一次都是从这个监控服务器拿到master的地址。

      从Redis2.8版本起,提供了一个稳定版本的Sentinel(哨兵),用来解决高可用的问题。它是一个特殊状态的redis实例。可以启动一个或者多个Sentinel的服务(通过src/redis-sentinel),它本质上只是一个运行在特殊模式之下的Redis,Sentinel通过info命令得到被监听Redis机器master,

    slave等信息。

            

      为了保证监控服务器的可用性,我们会对Sentinel做集群的部署。Sentinel既监控所有的Redis服务,Sentinel之间也相互监控。Sentinel本身没有主从之分,只有Redis服务节点有主从之分。

    (1)服务下线

      Sentinel 默认以每秒钟 1 次的频率向 Redis 服务节点发送 PING 命令。如果在down-after-milliseconds 内都没有收到有效回复,Sentinel 会将该服务器标记为下线(主观下线)。这个时候Sentinel节点会继续询问其他的Sentinel节点,确认这个节点是否下线,如果多数Sentinel节点都认为

    master下线,master才真正确认被下线(客观下线),这个时候就需要重新选举master。

    (2)故障转移

      如果master被标记为下线,就会开始故障转移流程。故障转移流程的第一步就是在Sentinel集群选择一个Leader,由Leader完成故障转移流程。Sentinle通过Raft算法,实现Sentinel选举。

      Raft是一个共识算法,核心思想是:先到先得,少数服从多数。

      Sentinle的Raft 算法和Raft论文略有不同:

        1)master客观下线触发选举,而不是过了election timeout时间开始选举。

        2)Leader 并不会把自己成为Leader 的消息发给其他Sentinel。其他Sentinel 等待 Leader 从 slave 选出 master 后,检测到新的master 正常工作后,就会去掉客观下线的标识,从而不需要进入故障转移流程。

    *  如何让原来的slave节点成为master节点?

      1)选出SentinelLeader之后,由SentinelLeader向某个节点发送slaveofnoone命令,让它成为独立节点。

      2)然后向其他节点发送slaveofx.x.x.x xxxx(本机服务),让它们成为这个节点的子节点,故障转移完成。

    *  这么多从节点,选举哪个成为master节点?

      关于从节点选举,一共有四个因素影响选举的结果:分别是断开连接时长、优先级排序、复制数量、进程id。

      如果与哨兵连接断开的比较久,超过了某个阈值,就直接失去了选举权。如果拥有选举权,那就看谁的优先级高,这个在配置文件里可以设置(replica-priority 100),数值越小优先级越高。如果优先级相同,就看谁从master中复制的数据最多(复制偏移量最大),选最多的那个,如果复制

    数量也相同,就选择进程id最小的那个。

    (3)Sentinel功能总结

      监控:Sentinel会不断检查主服务器和从服务器是否正常运行。

      通知:如果某一个被监控的实例出现问题,Sentinel可以通过API发出通知。

      自动故障转移(failover):如果主服务器发生故障,Sentinel可以启动故障转移过程。把某台服务器升级为主服务器,并发出通知。

      配置管理:客户端连接到Sentinel,获取当前的Redis主服务器的地址。

    (4)哨兵机制的不足

      主从切换的过程中会丢失数据,因为只有一个master。只能单点写,没有解决水平扩容的问题。

    4、Redis集群

      要实现Redis数据分片,有三种方案:

      (1)客户端实现相关逻辑,如Jedis提供的Redis Sharding方案,ShardedJedis。

      (2)使用代理服务,客户端连接代理服务,代理进行请求转发。如Twitter开源的Twemproxy和国内的豌豆荚开源的Codis。

      (3)基于服务端实现。如Redis Cluster。

      Redis Cluster是Redis 3.0正式推出的,用来解决分布式需求,可以实现水平扩展,也可以实现高可用。它是去中心化,客户端可以连接到任意一个可用节点。

    4.1、架构

      Redis Cluster可以看成是由多个Redis实例组成的数据集合。客户端不需要关注数据的子集到底存储在哪个节点,只需要关注这个集合整体。以3主3从为例,节点之间两两交互,共享数据分片、节点状态等信息。

                                              

      集群搭建:https://gper.club/articles/7e7e7f7ff7g5egc7g6d

    4.2、数据分布

      Redis集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被分为16382个槽(slot),每个节点可以处理0个或最多16384个槽,当所有16384个槽都有节点处理时,集群处于上线状态,相反地,集群处于下线状态。

      Redis的每个master节点维护一个16384位(2048bytes=2KB)的位序列(clusterNode结构中的slots属性),比如:序列的第0 位是1,就代表第一个slot 是它负责;序列的第 1 位是0,代表第二个slot不归它负责。

      对象分布到Redis节点上时,对key用CRC16算法计算再%16384,得到一个slot的值,数据落到负责这个slot的Redis节点上。

      注意:key与slot的关系是永远不会变的,会变的只有slot和Redis节点的关系。

    *  怎么让相关的数据落到同一个节点上?

      比如有些multikey操作是不能跨节点的,如果要让某些数据分布到一个节点,在key里面加入{hash tag}即可。Redis在计算槽编号的时候只会获取{}之间的字符串进行槽编号计算,这样由于上面两个不同的键,{}里面的字符串是相同的,因此他们可以被计算出相同的槽。

    4.3、客户端重定向

      客户端连接到一台服务器,访问的数据不在当前节点上,会发生重定向。比如在7291端口的Redis的redis-cli客户端操作:

    127.0.0.1:7291>set qs 1 
    (error)MOVED 13724 127.0.0.1:7293

      服务端返回MOVED,也就是根据key计算出来的slot 不归7191端口管理,而是归7293端口管理,服务端返回MOVED告诉客户端去7293端口操作。这个时候更换端口,用redis-cli–p7293操作,才会返回OK。或者用./redis-cli-c-p port 的命令(c代表cluster)。这样客户端需要连接两次。

      因为每个集群节点通过clusterNode结构保存自己的节点状态,并为集群中其他节点创建一个clusterNode机构来保存所有节点的状态,其slots属性记录了节点负责处理哪些槽,所以能够判断某个slot属于哪个节点。

      Jedis等客户端会在本地维护一份slot——node的映射关系,大部分时候不需要重定向,所以叫做smart jedis(需要客户端支持)。

     4.4、数据迁移

      新增或下线了Master节点,数据怎么迁移(重新分配)?

      因为key和slot的关系是永远不会变的,当新增了节点的时候,需要把原有的slot分配给新的节点负责,并且把相关的数据迁移过来。

      添加新节点(新增一个7297):

    redis-cli --cluster add-node 127.0.0.1:7291 127.0.0.1:7297

      新增的节点没有哈希槽,不能分布数据,在原来的任意一个节点上执行如下命令,然后输入需要分配的哈希槽的数量(比如500),和哈希槽的来源节点(可以输入all或者id)。

    redis-cli --cluster reshard 127.0.0.1:7291

      槽重新分配后,节点会将自己的槽信息(slots数组)通过消息发送给其他节点。

    4.5、高可用和主从切换原理

      只有主节点可以写,一个主节点挂了,从节点怎么变成主节点?

      当slave 发现自己的 master 变为FAIL 状态时,便尝试进行Failover,以期成为新的master。由于挂掉的master可能会有多个slave,从而存在多个slave竞争成为master节点的过程, 其过程如下:

        1)slave发现自己的master变为FAIL

        2)将自己记录的集群currentEpoch加1,并广播FAILOVER_AUTH_REQUEST信息

        3)其他节点收到该信息,只有 master 响应,判断请求者的合法性,并发送FAILOVER_AUTH_ACK,对每一个epoch只发送一次ack

        4)尝试failover的slave收集FAILOVER_AUTH_ACK

        5)超过半数后变成新Master

        6)广播Pong通知其他集群节点。

      Redis Cluster 既能够实现主从的角色分配,又能够实现主从切换,相当于集成了Replication和Sentinal的功能。

    4.6、Redis Cluster优势

      (1)无中心架构。

      (2)数据按照slot存储分布在多个节点,节点间数据共享,可动态调整数据分布。

      (3)可扩展性,可线性扩展到1000 个节点(官方推荐不超过 1000 个),节点可动态添加或删除。

      (4)高可用性,部分节点不可用时,集群仍可用。通过增加Slave做standby数据副本,能够实现故障自动failover,节点之间通过gossip协议交换状态信息,用投票机制完成Slave到Master的角色提升。

      (5)降低运维成本,提高系统的扩展性和可用性。

  • 相关阅读:
    ul内的li横向排列左右滑动
    git使用中常见报错解决方法
    vue 中子组件用watch方法监听父组件传来的参数,用handler方法绑定,当父组件参数不改变时,handler方法不执行
    [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.
    vue 动态更改classname
    react怎么添加运用自定义组件
    app项目中遇到TCP分包,H5端对分包进行拼包
    Error: Invalid CSS after "xxx": expected 1 selector or at-rule, was "{}"
    'touch' 不是内部或外部命令,也不是可运行的程序或批处理文件。
    微信小程序调用快递物流查询API的实现方法
  • 原文地址:https://www.cnblogs.com/jing-yi/p/12932025.html
Copyright © 2011-2022 走看看