一、redis主从复制的问题:
Redis 复制有一个缺点,当主机 Master 宕机以后,我们需要人工操作来解决切换,比如使用 slaveof no one 。实际上主从复制并没有实现,高可用, 高可用侧重备份机器, 利用集群中系统的冗余,当系统中某台机器发生损坏的时候,其他后备的机器可以迅速的接替它来启动服务。
如下图所示:
一旦主节点宕机,写服务无法使用,就需要手动去切换,重新选取主节点,手动设置主从关系。
那么如何解决呢?如果我们有一个监控程序能够监控各个机器的状态及时作出调整,将手动的操作变成自动的。redis的Sentinel(哨兵)的出现就是为了解决这个问题。
二、哨兵机制的原理及实现
Redis Sentinel(哨兵) 是一个分布式集群架构,其中包含若干个 Sentinel 节点和 Redis 数据节点,每个 Sentinel 节点会对数据节点和其余 Sentinel 节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他 Sentinel 节点进行“协商”,当大多数 Sentinel 节点都认为主节点不可达时,它们会选举出一个 Sentinel 节点来完成自动故障转移的工作,同时会将这个变化实时通知给 Redis 应用方。整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了 Redis 的高可用问题。
注:哨兵(Sentinel) 其实也是一个redis只不过它不是用来存储数据的,而哨兵不止一台,会有多台(最好或者必须 部署多台哨兵)。哨兵是一个独立进程,作为进程,它会独立运行。
一句话解释:哨兵是用来监控redis master主节点的状态的工具,并做出相应反映。比如说 当前redis的主节点挂掉了,哨兵会自动从redis 从节点中选举出一台继续作为主节点,来继续提供redis主从集群服务。
如下图所示:
注意:由上图可知,client客户端并不是直接连接的redis主机,而是连接的哨兵(哨兵是一个集群,有多台哨兵),然后哨兵监控主节点的redis状态(不是实时监控,而是定时监控),然后哨兵会返回主机的相关服务信息
** **
2.1 基本的故障转移流程:
1)主节点出现故障,此时两个从节点与主节点失去连接,主从复制失败。
2)每个 Sentinel 节点通过定期监控发现主节点出现了故障
3)多个 Sentinel 节点对主节点的故障达成一致会选举出其中一个节点作为领导者负责故障转移。
4)Sentinel 领导者节点执行了故障转移,整个过程基本是跟我们手动调整一致的,只不过是自动化完成的。
5)故障转移后整个 Redis Sentinel 的结构,重新选举了新的redis master 主节点。
** **
2.2 Redis Sentinel 具有以下几个功能:
①、监控:Sentinel 节点会定期检测 Redis 数据节点、其余 Sentinel 节点是否可达。(一个哨兵可以监控多套主从结构,主节点)** **
②、通知:Sentinel 节点会将故障转移的结果通知给应用方。
③、主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系。
④、配置提供者:在 Redis Sentinel 结构中,客户端在初始化的时候连接的是 Sentinel 节点集合,从中获取主节点信息。
同时Redis Sentinel 包含了若个 Sentinel 节点(一般起步至少是3台),这样做也带来了两个好处:
①、对于节点的故障判断是由多个 Sentinel 节点共同完成,这样可以有效地防止误判。
②、Sentinel 节点集合是由若干个 Sentinel 节点组成的,这样即使个别 Sentinel 节点不可用,整个 Sentinel 节点集合依然是健壮的。
但是 Sentinel 节点本身就是独立的 Redis 节点,只不过它们有一些特殊,它们不存储数据,只支持部分命令。
三、案例:配置哨兵
环境:
1台master主节点 redis-master(ip:172.10.0.2)
2台slave从节点 redis-slave(ip:172.10.0.3)、redis-slave2(ip:172.10.0.4)
3台哨兵服务节点 redis-sentinel1(ip:172.10.0.9)、redis-sentinel2(ip:172.10.0.10)、redis-sentinel3(ip:172.10.0.11)
架构环境列表如下:
容器名称 容器IP地址 映射端口号 服务运行模式
redis-sentinel1 172.10.0.9 22530->26379 sentinel
redis-sentinel2 172.10.0.10 22531->26379 sentinel
redis-sentinel3 172.10.0.11 22532->26379 sentinel
redis-master 172.10.0.2 6380->6379 Master
redis-slave 172.10.0.3 6381->6379 Slave
redis-slave2 172.10.0.4 6382->6379 Slave
3.1 由于前面几篇博文我们已经创建了一主一从,所以这里在创建一台redis从节点,变成1主2从的架构
#创建一台redis从节点``docker run -itd --name redis-slave2 --net mynetwork -p 6382:6379 --ip 172.10.0.4 redis` `#进入该容器并修改redis配置文件,指定主节点配置``docker ``exec` `-it redis-slave2 ``bash` `#修改配置文件(这里就不演示了。。可看该系列博文的第一篇)
3.2 创建三台哨兵服务节点,并分别进入三台哨兵服务节点容器里面
#创建三台哨兵服务节点``docker run -itd --name redis-sentinel1 --net mynetwork -p 22530:26379 --ip 172.10.0.9 redis``docker run -itd --name redis-sentinel2 --net mynetwork -p 22531:26379 --ip 172.10.0.10 redis``docker run -itd --name redis-sentinel3 --net mynetwork -p 22532:26379 --ip 172.10.0.11 redis` `#分别进入三台哨兵服务节点容器里面``docker ``exec` `-it redis-sentinel1 ``bash``docker ``exec` `-it redis-sentinel2 ``bash``docker ``exec` `-it redis-sentinel3 ``bash
3.3 分别修改三台哨兵配置文件(redis-sentinel.conf)
vi` `/etc/redis-sentinel``.conf` `#1、为了省事方便。。我们做如下这2项修改。。``将 ``# bind 127.0.0.1 192.168.1.1 修改为 bind 0.0.0.0``将 ``# protected-mode no 修改为 protected-mode no 也就是取消前面的#号` `#2、修改哨兵服务要监控的主节点redis的ip地址``将 sentinel monitor mymaster 127.0.0.1 6379 2 修改为 sentinel monitor mymaster 172.10.0.2 6379 2` `#3、保存修改后的文件``#4、redis-sentinel2和redis-sentinel3容器里面的哨兵配置文件redis-sentinel.conf的修改也是这样修改。。
3.4 分别进入三台哨兵服务节点容器里面启动三台哨兵服务
#redis-sentinel1容器里面执行以下命令``redis-sentinel ``/etc/redis-sentinel``.conf &` `#redis-sentinel2容器里面执行以下命令``redis-sentinel ``/etc/redis-sentinel``.conf &` `#redis-sentinel3容器里面执行以下命令``redis-sentinel ``/etc/redis-sentinel``.conf &
3.5 为了验证是否配置成功,可以查看哨兵日志(查看任意一台哨兵里面的日志即可,因为3台哨兵服务节点配置都是一模一样的)
vi` `/var/log/redis/sentinel``.log
3.6 进入redis主节点这台服务容器里面,并kill掉这台服务的进程,使之挂掉。让哨兵服务自动从N台从redis服务节点中选取其中一台从服务节点成为新的主(master)节点
①、进入主节点服务容器里面
docker ``exec` `-it redis-master ``bash
②、查看redis端口占用详情
netstat` `-apn |``grep` `6379
③、kill掉该进程,然后再次查看端口占用情况,如果结果中没有相关端口对应的相关进程则kill成功,具体看以下截图
kill` `31` `netstat` `-apn |``grep` `6379
至此。。我们就把主节点的redis给kill掉了,然后稍等几秒钟(或几十秒钟,具体看配置文件中的配置),查看一下哨兵在这几台从redis节点中选取了那一台从节点使之变成了主节点
3.7 再次查看哨兵日志,看一下哨兵选取了那台从节点变成了主节点
vi` `/var/log/redis/sentinel``.log
从以上图片中可以看到哨兵选举了172.10.0.3这台ip地址的从节点作为redis主从架构中的主节点,也就是使这台没有挂掉的从节点从slave晋升成为了master。ps:172.10.0.3这台ip地址对应的容器名字:redis-slave
3.8 进入redis-slave这台服务容器查看redis相关服务信息验证一下是否已成为主节点
ok。。从以上截图中可以看到该节点确实已经成为了主节点,并且 offset 偏移量也不是从0开始的。。也就是意味着主从复制数据并不是从头开始的。。至此哨兵搭建使用成功。。
四、Sentinel 的核心配置
①、sentinel monitor mymaster 127.0.0.1 7000 2
监控的主节点的名字、IP 和端口,最后一个2的意思是有几台 Sentinel 发现有问题,就会发生故障转移,例如 配置为2,代表至少有2台 Sentinel 节点认为主节点不可达,那么这个不可达的判定才是客观的。对于设置的越小,那么达到下线的条件越宽松,反之越严格。一般建议将其设置为 Sentinel 节点的一半加1
②、sentinel down-after-millseconds mymaster 30000
这个是超时的时间(单位:毫秒)。打个比方,当你去 ping 一个机器的时候,多长时间后仍 ping 不通,那么就认为它是有问题的
③、sentinel parallel-syncs mymaster 1
当 Sentinel 节点集合对主节点故障判定达成一致时,Sentinel 领导者节点会做故障转移操作,选出新的主节点,原来的从节点会向新的主节点发起复制操作,parallel-syncs 就是用来限制在一次故障转移之后,每次向新的主节点发起复制操作的从节点个数,指出 Sentinel 属于并发还是串行。1代表每次只能复制一个,可以减轻 Master 的压力
④、sentinel auth-pass <master-name> <password>
如果 Sentinel 监控的主节点配置了密码,sentinel auth-pass 配置通过添加主节点的密码,防止 Sentinel 节点对主节点无法监控
⑤、sentinel failover-timeout mymaster 180000
表示故障转移的时间
五、Sentinel常用命令
sentinel支持的合法命令如下:
①、sentinel masters 显示被监控的所有master以及它们的状态
②、sentinel master <master name> 显示指定master的信息和状态
③、sentinel slaves <master name> 显示指定master的所有slave以及它们的状态
④、sentinel get-master-addr-by-name <master name> 返回指定master的ip和端口,如果正在进行failover或者failover已经完成,将会显示被提升为master的slave的ip和端口
⑤、sentinel failover <master name> 强制sentinel执行failover,并且不需要得到其他sentinel的同意。但是failover后会将最新的配置发送给其他sentinel
⑥、sentinel failover-timeout mymaster 10000 这个参数设置集群 从 判断节点挂掉,到执行failover操作(即重新选举master节点)的时间
⑦、sentinel remove <name> 命令sentinel放弃对某个master的监听
六、应用端调用
Master可能会因为某些情况宕机了,如果在客户端是固定一个地址去访问,肯定是不合理的,所以客户端请求是请求哨兵,从哨兵获取主机地址的信息,或者是从机的信息。可以简单实现一个例子
1、随机选择一个哨兵连接,获取主机、从机信息
2、模拟客户端定时访问,实现简单轮训效果,轮询从节点
3、连接失败重试访问
代码简单模拟实现(php代码举例)
class` `Round``{`` ``static` `$lastIndex` `= 0;` ` ``public` `function` `select(``$list``)`` ``{`` ``$currentIndex` `= self::``$lastIndex``; ``//当前的index`` ``$value` `= ``$list``[``$currentIndex``];`` ` ` ``if``(``$currentIndex` `+ 1 > ``count``(``$list``) - 1)`` ``{`` ``self::``$lastIndex` `= 0;`` ``}`` ``else`` ``{`` ``self::``$lastIndex``++;`` ``}`` ` ` ``return` `$value``;`` ``}``}` `/********华丽的分割线*********/` `$sentinelConf` `= [`` ``[``'ip'``=>``'47.98.147.49'``,``'port'``=>22530],`` ``[``'ip'``=>``'47.98.147.49'``,``'port'``=>22531],`` ``[``'ip'``=>``'47.98.147.49'``,``'port'``=>22532]``];` `//随机访问``$sentinelInfo` `= ``$sentinelConf``[``array_rand``(``$sentinelConf``)];` `$redis` `= ``new` `Redis();``$redis``->connect(``$sentinelInfo``[``'ip'``], ``$sentinelInfo``[``'port'``]);``$slavesInfo` `= ``$redis``->rawCommand(``'SENTINEL'``, ``'slaves'``, ``'mymaster'``);` `$slaves` `= [];` `foreach` `(``$slavesInfo` `as` `$val``)``{`` ``$slaves``[] = [``'ip'``=>``$val``[3], ``'port'``=>``$val``[5]];``}` `//加载到缓存当中,可以记录这次访问的时间跟上次的访问时间` `//模拟客户端访问(这里用了swoole的定时操作)``swoole_timer_tick(600,``function` `() ``use``(``$slaves``) {`` ` ` ``//轮询`` ``$slave``=(``new` `Round``())->select(``$slaves``);`` ``try`` ``{`` ``$redis` `= ``new` `Redis();`` ``$redis``->connect(``$slave``[``'ip'``], ``$slave``[``'port'``]);`` ``var_dump(``$slave``, ``$redis``->get(``'wzyl'``));`` ``}`` ``catch` `(\RedisException ``$e``)`` ``{` ` ``}` `});` `//阻塞在这里
总结redis主从、哨兵、集群的概念:
【redis主从】:
是备份关系, 我们操作主库,数据也会同步到从库。 如果主库机器坏了,从库可以上。就好比你 D盘的片丢了,但是你移动硬盘里边备份有。
【redis哨兵】:
哨兵保证的是HA(高可用),保证特殊情况故障自动切换,哨兵盯着你的“redis主从集群”,如果主库死了,它会告诉你新的老大是谁。
哨兵:主要针对redis主从中的某一个单节点故障后,无法自动恢复的解决方案。(哨兵 保证redis主从的高可用)
【redis集群】:
集群保证的是高并发,因为多了一些兄弟帮忙一起扛。同时集群会导致数据的分散,整个redis集群会分成一堆数据槽,即不同的key会放到不不同的槽中。
集群主要针对单节点容量、高并发问题、线性可扩展性的解决方案。
集群:是为了解决redis主从复制中 单机内存上限和并发问题,假如你现在的服务器内存为256GB,当达到这个内存时redis就没办法再提供服务,同时数据量能达到这个地步写数据量也会很大,容易造成缓冲区溢出,造成从节点无限的进行全量复制导致主从无法正常工作。