Redis 集群技术

## 一:Python 连接 Redis ### 1. 安装包 ```bash [root@python ~]# yum -y install python3-pip.noarch [root@python ~]# pip3 install ipython [root@python ~]# pip3 install redis ``` ### 2. 连接 ```python [root@python ~]# ipython In [1]: import redis In [2]: r = redis.Redis(host='192.168.159.131',port=6379) In [3]: r.set("test1","111") Out[3]: True In [4]: r.get("test1") Out[4]: b'111' In [5]: ret = r.get("test1") In [6]: ret.decode() Out[6]: '111' ``` 注意: ​ 关闭redis的保护模式:protected-mode no ### 3. 连接池 ​ redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销 ​ 默认,每个Redis实例都会维护一个自己的连接池 ​ 可以直接建立一个连接池,然后作为参数传给Redis,这样就可以实现多个 Redis 实例共享一个连接池 ```python [root@python ~]# ipython In [1]: import redis In [2]: pool = redis.ConnectionPool(host='192.168.159.131',port= ...: 6379) In [3]: rs = redis.Redis(connection_pool=pool) In [4]: rs.set("foo","bar") Out[4]: True In [5]: rs.get("foo") Out[5]: b'bar' ``` ## 二:Redis 主从复制集群 ### 1. Redis 复制特性 - 使用异步复制 - 一个主服务器可以有多个从服务器 - 从服务器也可以有自己的从服务器 - 复制功能不会阻塞主服务器 - 可以通过复制功能来让主服务器免于执行持久化操作,由从服务器去执行持久化操作即可 - 关闭主服务器持久化时,复制功能的数据是安全的 ​ 当配置Redis复制功能时,强烈建议打开主服务器的持久化功能。;否则的话,由于延迟等问题应该要避免部署的服务自动拉起 ### 2. Redis 主从复制原理 ​ redis 主从同步有两种方式(或者所两个阶段):全同步和部分同步 ​ 从刚刚连接的时候,进行全同步;全同步结束后,进行部分同步。当然,如果有需要,slave 在任何时候都可以发起全同步 主从同步的机制: ![](http://182.92.143.66:40072/directlink/img/redis/image-202503140006.png) - 从服务器向主服务器发送 SYNC 命令 - 接到 SYNC 命令的主服务器会调用BGSAVE 命令,创建一个 RDB 文件,并使用缓冲区记录接下来执行的所有写命令 - 当主服务器执行完 BGSAVE 命令时,它会向从服务器发送 RDB 文件,而从服务器则会接收并载入这个文件 - 主服务器将缓冲区储存的所有写命令发送给从服务器执行 ### 3. Redis 命令传播 ​ 在主从服务器完成同步之后,主服务器每执行一个写命令,会将被执行的写命令发送给从服务器执行,这个操作被称为“命令传播” ​ 命令传播是一个持续的过程:只要复制仍在继续,命令传播就会一直进行,使得主从服务器的状态可以一直保持一致 ![](http://182.92.143.66:40072/directlink/img/redis/image-202503140007.png) ### 4. Redis 复制一致性问题 ![](http://182.92.143.66:40072/directlink/img/redis/image-202503140008.png) ​ 在读写分离环境下,客户端向主服务器发送写命令 SET n 10086,主服务器在执行这个写命令之后,向客户端返回回复,并将这个写命令传播给从服务器 ​ 接到回复的客户端继续向从服务器发送读命令 GET n ,并且因为网络状态的原因,客户端的 GET命令比主服务器传播的SET 命令更快到达了从服务器 ​ 因为从服务器键 n 的值还未被更新,所以客户端在从服务器读取到的将是一个错误(过期)的 n值 ### 5. Redis 复制安全性提升 ​ 主服务器只在有至少 N 个从服务器的情况下,才执行写操作从 Redis 2.8 开始, 为了保证数据的安全性, 可以通过配置, 让主服务器只在有至少 N 个当前已连接从服务器的情况下, 才执行写命令 ​ 不过, 因为 Redis 使用异步复制, 所以主服务器发送的写数据并不一定会被从服务器接收到, 因此, 数据丢失的可能性仍然是存在的 通过以下两个参数保证数据的安全: ```shell min-slaves-to-write min-slaves-max-lag ``` ​ 要求至少有1个slave,数据复制和同步的延迟不能超过10秒,如果说一旦所有的slave,数据复制和同步的延迟都超过了10秒钟,那么这个时候,master就不会再接收任何请求了 减少异步复制的数据丢失: ​ 有了min-slaves-max-lag这个配置,就可以确保说,一旦slave复制数据和ack延时太长,就认为可能master宕机后损失的数据太多了,那么就拒绝写请求,这样可以把master宕机时由于部分数据未同步到slave导致的数据丢失降低的可控范围内 减少脑裂的数据丢失: ​ 如果一个master出现了脑裂,跟其他slave丢了连接,那么上面两个配置可以确保说,如果不能继续给指定数量的slave发送数据,而且slave超过10秒没有给自己ack消息,那么就直接拒绝客户端的写请求,这样脑裂后的旧master就不会接受client的新数据,也就避免了数据丢失 总结: ​ 上面的配置就确保了,如果跟任何一个slave丢了连接,在10秒后发现没有slave给自己ack,那么就拒绝新的写请求。因此在脑裂场景下,最多就丢失10秒的数据 ### 6. 主从复制实战 实验环境 ​ 三台服务器,一台master,两台slave 部署 ```bash [root@redis-master ~]# yum -y install redis [root@redis-slave1 ~]# yum -y install redis [root@redis-slave2 ~]# yum -y install redis ``` 修改配置文件 ```bash 主配置: [root@redis-master ~]# vim /etc/redis.conf bind 0.0.0.0 protected-mode no requirepass master@123456 slave1配置文件: [root@redis-slave1 ~]# vim /etc/redis.conf bind 0.0.0.0 # 关闭保护模式 protected-mode no # 设置为从节点,指向主节点IP和端口 slaveof 192.168.159.131 6379 # 主节点密码(如果主节点设置了密码) masterauth master@123456 slave2配置文件: [root@redis-slave2 ~]# vim /etc/redis.conf bind 0.0.0.0 # 关闭保护模式 protected-mode no # 设置为从节点,指向主节点IP和端口 slaveof 192.168.159.131 6379 # 主节点密码(如果主节点设置了密码) masterauth master@123456 ``` 注意: ​ 配置文件参数地址:https://xingdian-file.oss-cn-hangzhou.aliyuncs.com/redis-config.txt 启动服务 ```bash [root@redis-master ~]# systemctl restart redis [root@redis-slave1 ~]# systemctl restart redis [root@redis-slave2 ~]# systemctl restart redis ``` 验证服务 ```bash [root@redis-master ~]# redis-cli -h 192.168.159.131 -a "master@123456" info replication # Replication role:master connected_slaves:2 slave0:ip=192.168.159.133,port=6379,state=online,offset=309,lag=1 slave1:ip=192.168.159.132,port=6379,state=online,offset=309,lag=1 master_repl_offset:309 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:308 [root@redis-master ~]# redis-cli -a "master@123456" 127.0.0.1:6379> set test1 111 OK 127.0.0.1:6379> get test1 "111" [root@redis-slave1 ~]# redis-cli 127.0.0.1:6379> get test1 "111" [root@redis-slave2 ~]# redis-cli 127.0.0.1:6379> get test1 "111" ``` ## 三:Redis HA Sentinel 集群 ​ Redis-Sentinel 是 Redis 官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实现自动进行主备切换,而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换 ​ Sentinel 是一个监视器,它可以根据被监视实例的身份和状态来判断应该执行何种动作 ![](http://182.92.143.66:40072/directlink/img/redis/image-202503140009.png) ### 1. Redis Sentinel 更能 ​ Sentinel的主要功能包括主节点存活检测、主从运行情况检测、自动故障转移(failover)、主从切换。Redis的Sentinel最小配置是一主一从;Redis的Sentinel系统可以用来管理多个Redis服务器 监控 ​ Sentinel会不断的检查主服务器和从服务器是否正常运行 通知 ​ 当被监控的某个Redis服务器出现问题,Sentinel通过API脚本向管理员或者其他的应用程序发送通知 自动故障转移 ​ 当主节点不能正常工作时,Sentinel会开始一次自动的故障转移操作,它会将与失效主节点是主从关系的其中一个从节点升级为新的主节点, 并且将其他的从节点指向新的主节点 配置提供者 ​ 在Redis Sentinel模式下,客户端应用在初始化时连接的是Sentinel节点集合,从中获取主节点的信息 ### 2. Redis Sentinel 工作流程 ​ 由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求 ![](http://182.92.143.66:40072/directlink/img/redis/image-202503140010.png) ​ Sentinel负责监控集群中的所有主、从Redis,当发现主故障时,Sentinel会在所有的从中选一个成为新的主。并且会把其余的从变为新主的从。同时那台有问题的旧主也会变为新主的从,也就是说当旧的主即使恢复时,并不会恢复原来的主身份,而是作为新主的一个从 ​ 在Redis高可用架构中,Sentinel往往不是只有一个,而是有3个或者以上。目的是为了让其更加可靠,毕竟主和从切换角色这个过程还是蛮复杂的 ### 3. Redis Sentinel 服务器连接 **发现并连接主服务器** ​ Sentinel 通过用户给定的配置文件来发现主服务器 ![](http://182.92.143.66:40072/directlink/img/redis/image-202503140011.png) Sentinel 会与被监视的主服务器创建两个网络连接 - 命令连接用于向主服务器发送命令 - 订阅连接用于订阅指定的频道,从而发现监视同一主服务器的其他 Sentinel **发现并连接从服务器** ​ Sentinel 通过向主服务器发送 INFO 命令来自动获得所有从服务器的地址 ​ 跟主服务器一样,Sentinel 会与每个被发现的从服务器创建命令连接和订阅连接 ### 4. Redis Sentinel 命令操作 | **命令** | **描述** | | ---------------------------------------------- | ------------------------------------------------------------ | | PING | 返回 PONG | | SENTINEL masters | 列出所有被监视的主服务器 | | SENTINEL slaves | 列出所有被监视的从服务器 | | SENTINEL get-master-addr-by-name | 返回给定名字的主服务器的 IP 地址和端口号。 | | SENTINEL reset | 重置所有名字和给定模式 pattern 相匹配的主服务器 | | SENTINEL failover | 当主服务器失效时, 在不询问其他 Sentinel 意见的情况下,强制开始一次自动故障迁移。 | ### 5. Redis Sentinel (哨兵) **环境说明** | **主机名称** | **IP地址** | **redis版本和角色说明** | | ------------- | --------------------- | ----------------------- | | redis-master | 192.168.152.133:6379 | redis 6.0.5(主) | | redis-slave01 | 192.168.152.134:6379 | redis 6.0.5(从) | | redis-slave02 | 192.168.152.135:6379 | redis 6.0.5(从) | | redis-master | 192.168.152.133:26379 | Sentinel01 | | redis-slave01 | 192.168.152.134:26379 | Sentinel02 | | redis-slave02 | 192.168.152.135:26379 | Sentinel03 | **安装(所有节点安装)** ```bash [root@redis-master ~]# yum -y install redis [root@redis-slave1 ~]# yum -y install redis [root@redis-slave2 ~]# yum -y install redis ``` **部署主从(略)** **部署Sentinel** ```bash [root@redis-master ~]# vim /etc/redis-sentinel.conf protected-mode no daemonize yes sentinel auth-pass mymaster master@123456 sentinel monitor mymaster 192.168.159.131 6379 2 [root@redis-slave1 ~]# vim /etc/redis-sentinel.conf protected-mode no daemonize yes sentinel auth-pass mymaster master@123456 sentinel monitor mymaster 192.168.159.132 6379 2 [root@redis-slave2 ~]# vim /etc/redis-sentinel.conf protected-mode no daemonize yes sentinel auth-pass mymaster master@123456 sentinel monitor mymaster 192.168.159.133 6379 2 ``` 启动哨兵服务: ```bash [root@redis-master ~]# redis-sentinel /etc/redis-sentinel.conf [root@redis-slave1 ~]# redis-sentinel /etc/redis-sentinel.conf [root@redis-slave2 ~]# redis-sentinel /etc/redis-sentinel.conf ``` 验证哨兵状态: ```bash [root@redis-master ~]# redis-cli -p 26379 127.0.0.1:26379> sentinel master mymaster # 查看监控的主节点信息 1) "name" 2) "mymaster" 3) "ip" 4) "192.168.159.131" 5) "port" 6) "6379" 7) "runid" 8) "ccbbb31aef496bcd64f9b3f7ee40aac9fd1e6075" 9) "flags" 10) "master" 11) "link-pending-commands" 12) "0" 13) "link-refcount" 14) "1" 15) "last-ping-sent" 16) "0" 17) "last-ok-ping-reply" 18) "890" 19) "last-ping-reply" 20) "890" 21) "down-after-milliseconds" 22) "30000" 23) "info-refresh" 24) "10047" 25) "role-reported" 26) "master" 27) "role-reported-time" 28) "30126" 29) "config-epoch" 30) "0" 31) "num-slaves" 32) "2" 33) "num-other-sentinels" 34) "2" 35) "quorum" 36) "2" 37) "failover-timeout" 38) "180000" 39) "parallel-syncs" 40) "1" 127.0.0.1:26379> sentinel slaves mymaster # 查看从节点信息 1) 1) "name" 2) "192.168.159.133:6379" 3) "ip" 4) "192.168.159.133" 5) "port" 6) "6379" 7) "runid" 8) "0ddbb5f35e94425d2a5d32371d123fe9509e9fe4" 9) "flags" 10) "slave" 11) "link-pending-commands" 12) "0" 13) "link-refcount" 14) "1" 15) "last-ping-sent" 16) "0" 17) "last-ok-ping-reply" 18) "153" 19) "last-ping-reply" 20) "153" 21) "down-after-milliseconds" 22) "30000" 23) "info-refresh" 24) "3575" 25) "role-reported" 26) "slave" 27) "role-reported-time" 28) "43904" 29) "master-link-down-time" 30) "0" 31) "master-link-status" 32) "ok" 33) "master-host" 34) "192.168.159.131" 35) "master-port" 36) "6379" 37) "slave-priority" 38) "100" 39) "slave-repl-offset" 40) "3521" 2) 1) "name" 2) "192.168.159.132:6379" 3) "ip" 4) "192.168.159.132" 5) "port" 6) "6379" 7) "runid" 8) "43767026feff948fa4f3cca0e92fbc07f8f590a9" 9) "flags" 10) "slave" 11) "link-pending-commands" 12) "0" 13) "link-refcount" 14) "1" 15) "last-ping-sent" 16) "0" 17) "last-ok-ping-reply" 18) "154" 19) "last-ping-reply" 20) "154" 21) "down-after-milliseconds" 22) "30000" 23) "info-refresh" 24) "3576" 25) "role-reported" 26) "slave" 27) "role-reported-time" 28) "43905" 29) "master-link-down-time" 30) "0" 31) "master-link-status" 32) "ok" 33) "master-host" 34) "192.168.159.131" 35) "master-port" 36) "6379" 37) "slave-priority" 38) "100" 39) "slave-repl-offset" 40) "3521" 127.0.0.1:26379> sentinel sentinels mymaster # 查看哨兵节点信息 1) 1) "name" 2) "4bcba2dee21e44ef9dce0f175ee8ddc43780df78" 3) "ip" 4) "192.168.159.132" 5) "port" 6) "26379" 7) "runid" 8) "4bcba2dee21e44ef9dce0f175ee8ddc43780df78" 9) "flags" 10) "sentinel" 11) "link-pending-commands" 12) "0" 13) "link-refcount" 14) "1" 15) "last-ping-sent" 16) "0" 17) "last-ok-ping-reply" 18) "352" 19) "last-ping-reply" 20) "352" 21) "down-after-milliseconds" 22) "30000" 23) "last-hello-message" 24) "810" 25) "voted-leader" 26) "?" 27) "voted-leader-epoch" 28) "0" 2) 1) "name" 2) "28c3b1bead760b3e0dca0cb1f2d223eb16081af5" 3) "ip" 4) "192.168.159.133" 5) "port" 6) "26379" 7) "runid" 8) "28c3b1bead760b3e0dca0cb1f2d223eb16081af5" 9) "flags" 10) "sentinel" 11) "link-pending-commands" 12) "0" 13) "link-refcount" 14) "1" 15) "last-ping-sent" 16) "0" 17) "last-ok-ping-reply" 18) "844" 19) "last-ping-reply" 20) "844" 21) "down-after-milliseconds" 22) "30000" 23) "last-hello-message" 24) "1045" 25) "voted-leader" 26) "?" 27) "voted-leader-epoch" 28) "0" 127.0.0.1:26379> ``` 注意: ​ 如果旧主重新加入后数据不同步解决方案 ​ 可能之前是主节点,没有配置从节点的连接信息 如:masterauth 连接密码,当master转变为slave后,由于他没有密码,所以他不能从新的master同步数据,随之导致 info replication 的时候,同步状态为 down ,所以只需要修改 redis.conf 中的 masterauth 为 对应的密码