Redis哨兵实现高可用原理及实战

哨兵的介绍

sentinel,中文名是哨兵。哨兵是 redis 集群机构中非常重要的一个组件,主要有以下功能:

  • 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
  • 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
  • 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
  • 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。

哨兵用于实现 redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。

  • 故障转移时,判断一个 master node 是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题。
  • 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了。

哨兵的核心知识

  • 哨兵至少需要 3 个实例,来保证自己的健壮性。
  • 哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只能保证 redis 集群的高可用性。
  • 对于哨兵 + redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。

哨兵集群必须部署 2 个以上节点,如果哨兵集群仅仅部署了 2 个哨兵实例,quorum = 1。

+----+         +----+
| M1 |---------| R1 |
| S1 |         | S2 |
+----+         +----+

配置 quorum=1,如果 master 宕机, s1 和 s2 中只要有 1 个哨兵认为 master 宕机了,就可以进行切换,同时 s1 和 s2 会选举出一个哨兵来执行故障转移。但是同时这个时候,需要 majority,也就是大多数哨兵都是运行的。

2 个哨兵,majority=2
3 个哨兵,majority=2
4 个哨兵,majority=2
5 个哨兵,majority=3
...

如果此时仅仅是 M1 进程宕机了,哨兵 s1 正常运行,那么故障转移是 OK 的。但是如果是整个 M1 和 S1 运行的机器宕机了,那么哨兵只有 1 个,此时就没有 majority 来允许执行故障转移,虽然另外一台机器上还有一个 R1,但是故障转移不会执行。

经典的 3 节点哨兵集群是这样的:

       +----+
       | M1 |
       | S1 |
       +----+
          |
+----+    |    +----+
| R2 |----+----| R3 |
| S2 |         | S3 |
+----+         +----+

配置 quorum=2,如果 M1 所在机器宕机了,那么三个哨兵还剩下 2 个,S2 和 S3 可以一致认为 master 宕机了,然后选举出一个来执行故障转移,同时 3 个哨兵的 majority 是 2,所以还剩下的 2 个哨兵运行着,就可以允许执行故障转移。

Redis哨兵主备切换的数据丢失问题

主备切换的过程,可能会导致数据丢失:

  • 异步复制导致的数据丢失
    因为 master->slave 的复制是异步的,所以可能有部分数据还没复制到 slave,master 就宕机了,此时这部分数据就丢失了

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fuTAgmCj-1575470586928)(D:\TinkingCat\Redis\assets\325120-96b1283188e3dcf0.png)]

  • 脑裂导致的数据丢失
    脑裂,也就是说,某个 master 所在机器突然脱离了正常的网络,跟其他 slave 机器不能连接,但是实际上 master 还运行着。此时哨兵可能就会认为 master 宕机了,然后开启选举,将其他 slave 切换成了 master。这个时候,集群里就会有两个 master ,也就是所谓的脑裂

此时虽然某个 slave 被切换成了master,但是可能 client 还没来得及切换到新的 master,还继续向旧 master 写数据。因此旧 master 再次恢复的时候,会被作为一个 slave 挂到新的 master 上去,自己的数据会清空,重新从新的 master 复制数据。而新的 master 并没有后来 client 写入的数据,因此,这部分数据也就丢失了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PIuwel5z-1575470586929)(D:\TinkingCat\Redis\assets\325120-3fd1f9dcb0aeb800.png)]

数据丢失问题的解决方案

进行如下配置:

min-slaves-to-write 1
min-slaves-max-lag 10

哨兵配置

#以此修改修改sentinel.cnf文件
#vi sentinel.conf
sentinel monitor mymaster 192.168.126.143 6379 2

#启动哨兵服务器
./redis-server ../sentinel.conf --sentinel

#出现警告
第一个警告:The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
第二个警告:overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to/etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
第三个警告:you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix thisissue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain thesetting after a reboot. Redis must be restarted after THP is disabled.

#解决方法
考虑到redis一般都是部署在服务器上作为服务存在的。这里的解决方案都是持久性配置,不是临时配置。
第一个警告

将net.core.somaxconn = 1024添加到/etc/sysctl.conf中,然后执行sysctl -p生效配置。
第二个警告

将vm.overcommit_memory = 1添加到/etc/sysctl.conf中,然后执行sysctl -p生效配置。
第三个警告

将echo never > /sys/kernel/mm/transparent_hugepage/enabled添加到/etc/rc.local中,然后执行source /etc/rc.local生效配置。

##启动哨兵客户端
./redis-cli -h 192.168.126.143 -p 26379
#查看哨兵信息
[root@localhost src]# ./redis-cli -h 192.168.126.143 -p 26379
192.168.126.143:26379> info
DENIED Redis is running in protected mode because protected mode is enabled, no bind address was specified, no authentication password is requested to clients. In this mode connections are only accepted from the loopback interface. If you want to connect from external computers to Redis you may adopt one of the following solutions: 1) Just disable protected mode sending the command 'CONFIG SET protected-mode no' from the loopback interface by connecting to Redis from the same host the server is running, however MAKE SURE Redis is not publicly accessible from internet if you do so. Use CONFIG REWRITE to make this change permanent. 2) Alternatively you can just disable the protected mode by editing the Redis configuration file, and setting the protected mode option to 'no', and then restarting the server. 3) If you started the server manually just for testing, restart it with the '--protected-mode no' option. 4) Setup a bind address or an authentication password. NOTE: You only need to do one of the above things in order for the server to start accepting connections from the outside.
192.168.126.143:26379> exit

#报错解决方法
#关闭保护模式
protected-mode no
#还可以指定日志信息
logfile "/var/log/redis/redis.log"

#哨兵信息
[root@localhost src]# ./redis-cli -h 192.168.126.143 -p 26379
192.168.126.143:26379> info
# Server
redis_version:4.0.0
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:6fac7fea124ff93f
redis_mode:sentinel
os:Linux 3.10.0-862.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:4.8.5
process_id:1578
run_id:4de74ad107517a858e02e626567e659034c648d2
tcp_port:26379
uptime_in_seconds:41
uptime_in_days:0
hz:14
lru_clock:14737320
executable:/usr/local/src/redis-4.0.0/src/./redis-server
config_file:/usr/local/src/redis-4.0.0/sentinel.conf

# Clients
connected_clients:3
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0

# CPU
used_cpu_sys:0.18
used_cpu_user:0.01
used_cpu_sys_children:0.00
used_cpu_user_children:0.00

# Stats
total_connections_received:3
total_commands_processed:120
instantaneous_ops_per_sec:3
total_net_input_bytes:7037
total_net_output_bytes:748
instantaneous_input_kbps:0.19
instantaneous_output_kbps:0.02
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0

# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.126.143:6379,slaves=2,sentinels=3

#简单分析下日志
老大下线了
疯狂的发送心跳进行询问(老大,你死了没?没回答,就应该是死了,哨兵进程辅助slave(s)建立新的老大)
自己成为老大 MASTER MODE enabled
其他小弟进行数据请求
新的主从建立
  • 依次启动所有的哨兵,使用java代码进行测试

    pom.xml

    <dependencies>
        <dependency>
            <groupId>commons-pool</groupId>
            <artifactId>commons-pool</artifactId>
            <version>1.5.6</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.7.0</version>
        </dependency>
    </dependencies>
    

    Main.java

    package com.ddm.test;
    
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisSentinelPool;
    
    import java.util.HashSet;
    import java.util.Set;
    
    public class Main {
    
        public static void main(String[] args) {
            Set<String> sentinels = new HashSet<String>();
            String host1 = "192.168.126.153:26379";
            String host2 = "192.168.126.151:26379";
            String host3 = "192.168.126.152:26379";
            sentinels.add(host1);
            sentinels.add(host2);
            sentinels.add(host3);
            String clusterName = "mymaster" ;
            JedisSentinelPool pool = new JedisSentinelPool(clusterName,sentinels);
            Jedis jedis = null;
            try {
                //JedisPool
                jedis = pool.getResource();
                jedis.set("key2", "aaa");
                System.out.println(jedis.get("key"));
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }
            pool.close();
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值