基本概念

一个主机 Master,多个从机 Slave。应用连接主机进行写操作,连接从机进行读操作,主机的数据复制到从机中。

解决的问题

  1. 读写分离,性能扩展。
  2. 容灾快速恢复。其中一个从 slaver 崩了,可以切换到其他 slaver 上。

本地尝试主从复制

通过命令行的形式来尝试主从复制,但是当重启时就没有主从关系了。

一直保持主从关系要在配置文件中配置。

# 1. 创建 demo 文件夹
mkdir ./redisdemo
cd ./redisdemo
 
# 2. 创建 `redis.conf` 文件副本
cp /etc/redis/redis.conf redis.conf # 或者改一下,这样会导致文件无法访问
 
# 3. 创建主从配置文件
vim redis6380.conf
vim redis6381.conf
vim redis6382.conf
 
# 4. 配置文件,以6380端口为例
include /home/wangsc/redisdemo/redis.conf
pidfile redis6380.pid
port 6380
dbfilename dump6380.rdb
daemonize yes
appendonly no # 或者更改AOF文件名
 
# 5. 启动服务
redis-server redis6380.conf
redis-server redis6381.conf
redis-server redis6382.conf
 
# 6. 查看主机运行情况
(base) wangsc@Server:~/redisdemo$ redis-cli -p 6380
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:0
master_replid:603ad33e987cc14e732d2d8fd06727cb76e7c46c
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
 
# 7. 对从机进行配置,配从不配主
(base) wangsc@Server:~/redisdemo$ redis-cli -p 6380
127.0.0.1:6380> slaveof 127.0.0.1 6382
OK
127.0.0.1:6380> exit
(base) wangsc@Server:~/redisdemo$ redis-cli -p 6381
127.0.0.1:6381> slaveof 127.0.0.1 6382
OK
127.0.0.1:6381> exit
 
# 8. 查看主机状态
(base) wangsc@Server:~/redisdemo$ redis-cli -p 6382
127.0.0.1:6382> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=98,lag=0
slave1:ip=127.0.0.1,port=6381,state=online,offset=98,lag=1
master_replid:79dbf0d2f2df07ad0208264ad00d4b473b3eb21f
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:98
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:98
 
# 9. 尝试在从机上写数据,出错
(base) wangsc@Server:/$ redis-cli -p 6380
127.0.0.1:6380> set k1 v1
(error) READONLY You can't write against a read only slave.

常见的主从关系

一主二从

从机不可以写操作,从机从头开始复制

从机 shutdown 后重启

  1. 从机 shutdown 后,主机 info replication 少了一个从机;
  2. 从机重启后,失去主从关系,info replication 看到自己是主机;
  3. 重新使用 slaveof 连接上主机,主机数据覆盖从机数据。

主机 shutdown 后重启

  1. 主机 shutdown 后,所有从机仍然是从机,仍然记录着主机的 IP 和 Port。
  2. 主机重启后,info replication 可以看到之前的从机记录。

反客为主

当一个 master 宕机后,后面的 slave 可以手动升为 master,其后面的 slave 不用做任何修改。

slaveof  no one   将从机变为主机。

哨兵模式

反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

Sentinel(哨兵)是用于监控 redis 集群中 Master 状态的工具,是 Redis 的高可用性解决方案,sentinel 哨兵模式已经被集成在 redis2.4 之后的版本中。sentinel 是 redis 高可用的解决方案,sentinel 系统可以监视一个或者多个 redis master 服务,以及这些 master 服务的所有从服务;当某个 master 服务下线时,自动将该 master 下的某个从服务升级为 master 服务替代已下线的 master 服务继续处理请求。

Sentinel 可以让 redis 实现主从复制,当一个集群中的 master 失效之后,Sentinel 可以选举出一个新的 master 用于自动接替 master 的工作,集群中的其他 redis 服务器自动指向新的 master 同步数据。一般建议 Sentinel 采取奇数台,防止某一台 Sentinel 无法连接到 master 导致误切换。

本地哨兵模式

  1. 调整为一主二仆模式,6379 带着 6380、6381。
  2. 自定义目录下新建 sentinel.conf 文件,名字绝不能错
  3. 配置哨兵, 填写内容 Sentinel monitor mymaster 127.0.0.1 6379 1. 其中 mymaster 为监控对象起的服务器名称, 1 为至少有多少个哨兵同意迁移的数量。

自定义的**/myredis**目录下新建**sentinel.conf**文件,名字绝不能错

配置哨兵**,**填写内容

sentinel monitor mymaster 127.0.0.1 6379 1

其中 mymaster 为监控对象起的服务器名称, 1 为至少有多少个哨兵同意迁移的数量。

启动哨兵

/usr/local/bin

redis 做压测可以用自带的 redis-benchmark 工具

执行 redis-sentinel  /myredis/sentinel. conf

哪个从机会被选举为主机呢?根据优先级别:slave-priority

原主机重启后会变为从机。

复制延时

由于所有的写操作都是先在 Master 上操作,然后同步更新到 Slave 上,所以从 Master 同步到 Slave 机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave 机器数量的增加也会使这个问题更加严重。

故障恢复

  1. 新 master
    从下线的主服务的所有从服务中挑选一个从服务,将其转换为主服务。条件:
    优先级靠前,偏移量最大,runid 最小
  2. 从服务更新 master
    Sentinel 向从服务发送 slaveof 新 master 命令,复制新 master
  3. 旧 master 成从
    旧 master 恢复后,sentinel 发送 slaveof 新 master 命令,变成新 master 的 slave。

优先级在 redis.conf 中默认:slave-priority 100,值越小优先级越高
偏移量是指获得原主机数据最全的
每个 redis 实例启动后都会随机生成一个 40 位的 runid

private static JedisSentinelPool jedisSentinelPool=null;
 
public static  Jedis getJedisFromSentinel(){
	if(jedisSentinelPool==null){
		Set<String> sentinelSet=new HashSet<>();
		sentinelSet.add("192.168.11.103:26379");
 
		JedisPoolConfig jedisPoolConfig =new JedisPoolConfig();
		jedisPoolConfig.setMaxTotal(10); //最大可用连接数
		jedisPoolConfig.setMaxIdle(5); //最大闲置连接数
		jedisPoolConfig.setMinIdle(5); //最小闲置连接数
		jedisPoolConfig.setBlockWhenExhausted(true); //连接耗尽是否等待
		jedisPoolConfig.setMaxWaitMillis(2000); //等待时间
		jedisPoolConfig.setTestOnBorrow(true); //取连接的时候进行一下测试 ping pong
 
		jedisSentinelPool=new JedisSentinelPool("mymaster",sentinelSet,jedisPoolConfig);
		return jedisSentinelPool.getResource();
	}else{
		return jedisSentinelPool.getResource();
	}
}