1、主从复制简而言之为将主redis的数据同步到从redis,达到主从数据一致。主从复制应用:
- 读写分离
- 容灾备份
2、怎样设置主从?
- 原则:配从不配主
- 方式:
a、在从redis中使用执行命令 slaveof host port [slaveof no one命令表示禁止和主机的同步]
b、在从redis的配置文件中配置slaveof host port
- 查询主从信息:info replication
3、主从复制原理
全量复制:slave连接上master时候,master将整个快照发给slave,slave加载快照的数据,此时为全量复制
增量复制:master每次接收到在自己数据库的写操作,同时会把写命令传给slave
- 主从复制存在的问题:
由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。
4、常见问题
- 切入点问题?slave1、slave2是从头开始复制还是从切入点开始复制?比如从k4进来,那之前的123是否也可以复制
-->从头开始复制,不是从切入点(创建从的时间),主从数据保持一致了
- 从机是否可以写?set可否?
-->从机一般配置为slave-read-only yes,即为不可set,只可以读
- 主机shutdown后情况如何?从机是上位还是原地待命
-->从机不上位,原地待命
- 主机又回来了后,主机新增记录,从机还能否顺利复制?
-->主机回来后还是主机身份,主机新增,从机仍然顺利复制
- 其中一台从机down后情况如何?依照原有它能跟上大部队吗?
-->如果通过命令slaveof设置的主从,从新启动从机后身份会变成主机,需要从新用命令设置为原来主机的从机,那么此时数据就同步了;
如果是通过当前从机的配置文件配置的,那么启动后仍然是从机,且数据是同步的
- 如果从机中途变更主机,数据变化?
-->清空所有数据,并与新主机的数据同步
5、哨兵模式
- 什么是哨兵模式?
监控主机是否发生故障,如果发生了故障,根据投票数自动将从机切换为主机。一个哨兵可以同时监控多个主机。
- 怎么启动哨兵?
新建配置sentinel.conf文件,并添加内容(这里是最简化的配置):
sentinel monitor 被监控主机名字(随意) 127.0.0.1(主机的IP) 6379(主机redis的端口号) 1(至少1 个 Sentinel 同意才进行故障切换)
启动哨兵:redis-sentinel sentinel.conf
- 每个哨兵(sentinel)定期执行的任务
a、每个 Sentinel 以每秒钟一次的频率向它所知的主服务器、从服务器以及其他 Sentinel 实例发送一个 PING 命令。
b、如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 那么这个实例会被 Sentinel 标记为主观下线。 一个有效回复可以是: +PONG 、 -LOADING 或者 -MASTERDOWN 。
c、如果一个主服务器被标记为主观下线, 那么正在监视这个主服务器的所有 Sentinel 要以每秒一次的频率确认主服务器的确进入了主观下线状态。
d、如果一个主服务器被标记为主观下线, 并且有足够数量的 Sentinel (至少要达到配置文件指定的数量)在指定的时间范围内同意这一判断, 那么这个主服务器被标记为客观下线。
e、在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有主服务器和从服务器发送 INFO 命令。 当一个主服务器被 Sentinel 标记为客观下线时, Sentinel 向下线主服务器的所有从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
f、当没有足够数量的 Sentinel 同意主服务器已经下线, 主服务器的客观下线状态就会被移除。 当主服务器重新向 Sentinel 的 PING 命令返回有效回复时, 主服务器的主管下线状态就会被移除。
- 一个实例:
假设127.0.0.1 6379 6380 6381三个端口分别启动了redis服务,6380为master,其它为slave,然后启动哨兵(只监控一个master)
哨兵的日志:
2793:X 19 Aug 02:02:42.868 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. 2793:X 19 Aug 02:02:42.883 # Sentinel ID is bfbd85c34ae2f4d8f0cd584dda8a90bb44ebe7af 2793:X 19 Aug 02:02:42.883 # +monitor master host6379 127.0.0.1 6380 quorum 1 【监视主机】 2793:X 19 Aug 02:02:42.884 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ host6379 127.0.0.1 6380 【sentinel识别到的从机6379】 2793:X 19 Aug 02:02:42.885 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6380 【sentinel识别到的从机6381】
关闭6380的服务,日志:
2820:X 19 Aug 02:21:32.136 # +sdown master host6379 127.0.0.1 6380 【主观下线】 2820:X 19 Aug 02:21:32.136 # +odown master host6379 127.0.0.1 6380 #quorum 1/1 【客观下线】 2820:X 19 Aug 02:21:32.136 # +new-epoch 2 2820:X 19 Aug 02:21:32.136 # +try-failover master host6379 127.0.0.1 6380 2820:X 19 Aug 02:21:32.147 # +vote-for-leader bfbd85c34ae2f4d8f0cd584dda8a90bb44ebe7af 2 2820:X 19 Aug 02:21:32.147 # +elected-leader master host6379 127.0.0.1 6380 2820:X 19 Aug 02:21:32.147 # +failover-state-select-slave master host6379 127.0.0.1 6380 【故障转移操作现在处于select-slave状态-sentinel选择可以升级为主的从机】 2820:X 19 Aug 02:21:32.206 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6380 【已经找到可以升级为主的从机】 2820:X 19 Aug 02:21:32.207 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6380【故障转移操作现在处于send-slaveof-noone状态-执行slaveof no one命令将从升级为主】 2820:X 19 Aug 02:21:32.292 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6380【故障转移操作现在处于wait-promotion状态-等待从升级为主】 2820:X 19 Aug 02:21:32.853 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6380 2820:X 19 Aug 02:21:32.853 # +failover-state-reconf-slaves master host6379 127.0.0.1 6380【故障转移操作现在处于reconf-slaves状态-重新配置院主机的所有从机】 2820:X 19 Aug 02:21:32.906 * +slave-reconf-sent slave 127.0.0.1:6379 127.0.0.1 6379 @ host6379 127.0.0.1 6380 2820:X 19 Aug 02:21:33.863 * +slave-reconf-inprog slave 127.0.0.1:6379 127.0.0.1 6379 @ host6379 127.0.0.1 6380 2820:X 19 Aug 02:21:33.863 * +slave-reconf-done slave 127.0.0.1:6379 127.0.0.1 6379 @ host6379 127.0.0.1 6380 2820:X 19 Aug 02:21:33.939 # +failover-end master host6379 127.0.0.1 6380【故障转移操作顺利完成。所有从服务器都开始复制新的主服务器了】 2820:X 19 Aug 02:21:33.939 # +switch-master host6379 127.0.0.1 6380 127.0.0.1 6381【配置变更,主服务器的 IP 和地址已经改变。 这是绝大多数外部用户都关心的信息】 2820:X 19 Aug 02:21:33.940 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ host6379 127.0.0.1 6381 2820:X 19 Aug 02:21:33.940 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6381 2820:X 19 Aug 02:22:03.944 # +sdown slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6381
从日志可以看出,主机从6380切换到了6381。上面日志也可以看成一次故障转移的步骤。
此时,启动6380端口的服务,日志:
2820:X 19 Aug 02:30:57.570 # -sdown slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6381 2820:X 19 Aug 02:31:07.500 * +convert-to-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6381
从日志可以看出,原主机down机后,再恢复时候,哨兵会将其作为当前主机(6381)的从机使用
注:在故障切换的时候各个实例的配置文件和sentinel.conf配置文件会自动做出相应的修改
- 多哨兵模式(来源博客https://blog.csdn.net/enlyhua/article/details/80544135)
哨兵+主从复制保证了redis的高可用,通常为保证哨兵的健壮性,将哨兵部署为集群,至少3个节点
a、为什么redis哨兵集群不能为2个节点?
如果哨兵集群仅仅部署了个2个哨兵实例,Configuration: quorum = 1
+----+ +----+
| M1 |---------| R1 |
| S1 | | S2 |
+----+ +----+
master宕机,s1(哨兵1)和s2中只要有1个哨兵认为master宕机就可以执行切换,同时s1和s2中会选举出一个哨兵来执行故障转移。同时这个时候,也就是大多数(majority)哨兵都是运行的,但是如果整个M1和S1运行的机器宕机了,那么哨兵只有1个了,此时就没有majority来允许执行故障转移,虽然另外一台机器还有一个R1,但是故障转移不会执行。
b、经典的3节点哨兵模式:Configuration: quorum = 2,majority
+----+ +----+
| R2 | | R3 |
| S2 | | S3 |
+----+ +----+
如果M1所在机器宕机了,那么三个哨兵还剩下2个,S2和S3可以一致认为master宕机,然后选举出一个来执行故障转移。同时3个哨兵的majority是2,所以还剩下的2个哨兵运行着,就可以允许执行故障转移
c、小结:每次一个哨兵要做主备切换,首先需要quorum数量的哨兵认为odown,然后选举出一个哨兵来做切换,这个哨兵还得得到majority哨兵的授权,才能正式执行切换。如果quorum < majority,比如5个哨兵,majority就是3,quorum设置为2,那么就3个哨兵授权就可以执行切换。但是如果quorum >= majority,那么必须quorum数量的哨兵都授权,比如5个哨兵,quorum是5,那么必须5个哨兵都同意授权,才能执行切换
6、客户端API连接使用redis
- 直连redis
首先细读redis.conf文件以下说明,正确配置redis.conf的bind、protect-mode并确认防火墙,否则会导致连接不上redis。
直连redis代码:
package com; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.JedisSentinelPool; import java.util.HashSet; import java.util.Set; public class Main { public static void main(String[] args) { JedisPool jedisPool=new JedisPool("192.168.1.113", 6381); Jedis jedis=jedisPool.getResource(); Set<String> out=jedis.keys("*"); System.out.println(out); jedis.set("zhongguo","weida"); } }
- 通过哨兵连接redis
首先细读sentinel.conf文件以下说明,正确配置redis.conf的bind、protect-mode并确认防火墙,否则会导致连接不上redis。
通过哨兵连接redis代码:
package com; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; 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>(); sentinels.add("192.168.1.113:26379");//如果有多个哨兵,可继续添加 JedisSentinelPool jedisSentinelPool=new JedisSentinelPool("host6379", sentinels); Jedis jedis=jedisSentinelPool.getResource(); Set<String> out=jedis.keys("*"); System.out.println(out); jedis.set("shoudu","beijing"); } }