Redis集群的三种方式
Posted 蚂蚁小哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis集群的三种方式相关的知识,希望对你有一定的参考价值。
一:主从模式Windows下搭建
1:基本介绍
主从复制中一个 主节点(master)可以拥有多个 从节点(slave),但是一个slave只能对应一个master。这样,当某个slave宕机是不会影响其它slave的读取和master的读取和写入;但是之前宕机的slave重新启动服务后,数据会从自动master上同步过来(前提是在配置文件上配置的主从复制,若是通过命令的方式设置的主从复制,不管哪一台宕机重启后会都会变为一个普通的单机的Redis,默认都是master)。在主从模式下,只有一个主节点可以写和读,而从节点只能读,所以通常主节点负责写,从节点负责读(因为读取的操作最频繁)。但是,当唯一的master挂了以后,虽然不影响slave的读操作,但影响当前主从复制Redis服务的写操作,需要将master重启后,redis才重新对外提供写服务(前提是配置文件配置的主从复制)
2:主从复制优缺点
优点:
①:支持主从复制,主机(Master)会自动将数据同步到从机(Slave),可以进行读写分离
②:为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成
③:Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。
④:Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求。
⑤:Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据
缺点:
①:Redis不具备自动容错和恢复功能,主机和从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
②:主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
③:Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
3:配置主从模式
为了方便在 windows 下操作主从复制(Linux和这里Windows操作基本一致后,都是解压后复制三个出来)
注:要是不会Redis下如何搭建单据Redis请参考 Redis基本操作
环境:
Redis:解压三份Redis应用(这里我以6.2.5为例)
解压后的文件夹:
redis6379 用来配置主机Master
redis6380 用来配置从机Slave①
redis6381 用来配置从机Slave②
修改redis6379、redis6380、redis6381下的Redis配置
注:Redis配置文件在Windows下一般为 "redis.windows.conf"
注:Redis配置文件在Windows下一般为 "redis.conf"
①:redis6379主节点(Master)配置文件修改
Redis中的各种配置不明白的请参考 ”“
进入redis6379主节点Master文件夹下修改基本配置
①:指定Redis主节点端口(端口不可冲突)
port:6379
②:指定新的PID文件路径 (windows下没有)
pidfile /var/run/redis_6379.pid
③:日志文件位置,当指定为 "空字符串" 为标准输出。如 redis 以守护进程运行,日志将会输出到 "/dev/null"
logfile "redisLog_6379.log"
④:RDB持久化快照的的文件名
dbfilename dump6379.rdb
⑤:设置redis连接密码
requirepass 1234
⑥:保护模式,默认开启,拒绝外部访问,建议关闭 -- 设置为:no
protected-mode yes
②:redis6380从节点(Slave①)配置文件修改
进入redis6380从节点Slave①文件夹下修改基本配置
①:指定Redis主节点端口(端口不可冲突)
port:6380
②:指定新的PID文件路径 (windows下没有)
pidfile /var/run/redis_6380.pid
③:日志文件位置,当指定为 "空字符串" 为标准输出。如 redis 以守护进程运行,日志将会输出到 "/dev/null"
logfile "redisLog_6380.log"
④:RDB持久化快照的的文件名
dbfilename dump6380.rdb
⑤:设置redis连接密码
requirepass 1234
⑥:保护模式,默认开启,拒绝外部访问,建议关闭 -- 设置为:no
protected-mode yes
配置主Master地址
①:主从复制,使用replicaof来指明出Master主节点地址 ,默认关闭
replicaof 127.0.0.1 6379
②:如果Master需要密码认证,则在这里设置,默认不设置
masterauth 1234
③:redis6381从节点(Slave②)配置文件修改
进入redis6381从节点Slave②文件夹下修改基本配置
①:指定Redis主节点端口(端口不可冲突)
port:6381
②:指定新的PID文件路径 (windows下没有)
pidfile /var/run/redis_6381.pid
③:日志文件位置,当指定为 "空字符串" 为标准输出。如 redis 以守护进程运行,日志将会输出到 "/dev/null"
logfile "redisLog_6381.log"
④:RDB持久化快照的的文件名
dbfilename dump6381.rdb
⑤:设置redis连接密码
requirepass 1234
⑥:保护模式,默认开启,拒绝外部访问,建议关闭 -- 设置为:no
protected-mode yes
配置主Master地址
①:主从复制,使用replicaof来指明出Master主节点地址 ,默认关闭
replicaof 127.0.0.1 6379
②:如果Master需要密码认证,则在这里设置,默认不设置
masterauth 1234
4:启动主从模式
开始三个Redis服务端
注:配置文件和服务放在一个目录下
创建bat脚本文件:redis服务启动.bat
@echo off
rem 设置我们redis目录位置 开启Redis服务端
set redis_home=C:\\Users\\xiaof\\Desktop\\redis
start cmd /k "cd /d %redis_home%\\redis6379 && redis-server.exe redis.windows.conf"
start cmd /k "cd /d %redis_home%\\redis6380 && redis-server.exe redis.windows.conf"
start cmd /k "cd /d %redis_home%\\redis6381 && redis-server.exe redis.windows.conf"
exit
创建bat脚本文件:redis客户端启动.bat
@echo off
rem 设置我们redis目录位置 开启Redis客户端
set redis_home=C:\\Users\\xiaof\\Desktop\\redis
start cmd /k "cd /d %redis_home%\\redis6379 && redis-cli.exe -h 127.0.0.1 -p 6379 -a 1234"
start cmd /k "cd /d %redis_home%\\redis6380 && redis-cli.exe -h 127.0.0.1 -p 6380 -a 1234"
start cmd /k "cd /d %redis_home%\\redis6381 && redis-cli.exe -h 127.0.0.1 -p 6381 -a 1234"
exit
创建bat脚本文件:redis服务端客户端关闭.bat
@echo off
start cmd /k "taskkill /f /t /im redis-server.exe && taskkill /f /t /im cmd.exe"
exit
5:测试主从复制
到这我们就算成功搭建了主从复制了,在主机上使用set命令也会同步到其它从机,但是从机上不会有写命令
info replication属性说明:
role
当前Redis服务器角色(会有Master和slave)
connected_slaves
当前Redis服务器下连接的从机数
slave(N)
从机的连接状态信息 state=online 代表状态在线
master_failover_state
主节点的故障转移状态,可选值如下:
no-failover:当前没有正在协调中的故障转移。
waiting-for-sync:主节点正在等待副本来获取它的副本数据偏移值。
failover-in-progress:主节点已经降级了,并试图将所有权移交给目标副本。
master_replid
每次重启redis产生一个40位的ID,用于主从复制.识别增量的一个标识
master_host、master_port
连接主节点的IP+端口号
master_link_status
连接主节点的状态 up代表连接 down代表未连接
Master主机配置查看: 127.0.0.1:6379> info replication # Replication role:master connected_slaves:2 slave0:ip=127.0.0.1,port=6381,state=online,offset=438,lag=0 slave1:ip=127.0.0.1,port=6380,state=online,offset=438,lag=0 master_failover_state:no-failover master_replid:e96c06dc1ffde9d13cb7791bdfba36dd26b89794 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:438 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:438 Slave从机配置查看: 127.0.0.1:6380> info replication # Replication role:slave master_host:127.0.0.1 master_port:6379 master_link_status:up master_last_io_seconds_ago:11 master_sync_in_progress:0 slave_repl_offset:410 slave_priority:100 slave_read_only:1 replica_announced:1 connected_slaves:0 master_failover_state:no-failover master_replid:e96c06dc1ffde9d13cb7791bdfba36dd26b89794 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:410 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:410
6:使用命令方式配置主从复制
不推荐使用此方式配置主从,只可用于测试,因为这种方式的配置主从不会持久化,每次关闭再启动就会变为一个单机
用上边配置文件搭建案例来说,关闭Redis服务后注释主机和从机的配置文件配置的 replicaof、masterauth、requirepass
这时候利用脚本启动的Redis服务都是单机的Master主机
这时候我们可以看出Master原来的主节点里没有任何从机,下面我将6380、6381设置为从机
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:a0b02f9a1a0e6a2b24c90a19a7d9c2e5e1f2c96c
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
把Redis6380设置为从节点Slave①: 127.0.0.1:6380> slaveof 127.0.0.1 6379 把Redis6381设置为从节点Slave②: 127.0.0.1:6381> slaveof 127.0.0.1 6379 这时候就设置好主从了 6379为Master主节点;6380、6381为Slave从节点 若其中一台从机想断开和主机连接则执行:slaveof no one 若主节点宕机则不影响从机的读取,但是缺少主节点就代表没有写功能 若从机宕机后重启则之前配置为从机的配置被初始化,启动后默认为单个主机
7:简单介绍主从复制原理
全量同步:一般发生在Slave连接主机后初始化阶段,这时Slave需要将Master上的所有数据都复制一份
①:从服务器连接到主服务器,首先发送一个SYNC命令;
②:主服务器接收到SYNC命名后,主服务器开始执行 bgsave 命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
③:主服务器 bgsave 命令执行完后,向所有从服务器发送快照文件,并在发送期间继续记录主服务器被执行的写命令;
④:从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
⑤:主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
⑥:从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;
完成上面几个步骤后就完成了从服务器数据初始化的所有操作,从服务器此时可以接收来自用户的读请求。
增量同步:Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。
二:主从复制Linux下搭建
通过上面的在Windows下的搭建操作,这里我就不多啰嗦了,直接上手操作搭建
若连在Linux如何搭建单机Redis不怎么熟悉的请参考:Redis入门及环境搭建
具体搭建完主从复制是这个样子的:
如果想按照我这个目录快速搭建主从复制可以直接复制下面代码:
具体步骤:
①:把 redis-6.2.6.tar.gz 压缩包放到home目录下解压
rz -y
tar -zxvf redis-6.2.6.tar.gz
②:解压后进入redis目录里进行编译安装,
cd redis-6.2.6/
make && make PREFIX=/home/redis6379 install
③:复制成三份(主从复制 6379主,6380、6381从)
cp -r redis6379 redis6380
cp -r redis6379 redis6381
④:为每个redis创建配置文件目录和拷贝redis配置文件
mkdir /home/redis6379/conf
mkdir /home/redis6380/conf
mkdir /home/redis6381/conf
cp /home/redis-6.2.6/redis.conf /home/redis6379/conf/redis6379.conf
cp /home/redis-6.2.6/redis.conf /home/redis6380/conf/redis6380.conf
cp /home/redis-6.2.6/redis.conf /home/redis6381/conf/redis6381.conf
⑤:去编辑每个Redis目录下的 redis63*.conf 配置文件
具体参考上面Windows下搭建主从复制的第3小节文件配置
⑥:编写启动脚本:startRedis.sh和stopRedis.sh
cd /home/
cat > startRedis.sh <<EOF
#!/bin/sh
/home/redis6379/bin/redis-server /home/redis6379/conf/redis6379.conf 2>&1 &
/home/redis6380/bin/redis-server /home/redis6380/conf/redis6380.conf 2>&1 &
/home/redis6381/bin/redis-server /home/redis6381/conf/redis6381.conf 2>&1 &
echo "redis is start"
EOF
cat > stopRedis.sh <<EOF
#!/bin/sh
#stop redis
ps -ef|grep redis |grep -v grep|awk \'print \'|xargs kill
echo "redis is stop"
EOF
chmod 744 startRedis.sh stopRedis.sh
接下来就是启动测试了,测试就按照上面测试即可,启动则启动下那个命令即可!
Redis客户端启动: /home/redis6379/bin/redis-cli -h 127.0.0.1 -p 6379 -a 1234 /home/redis6380/bin/redis-cli -h 127.0.0.1 -p 6380 -a 1234 /home/redis6381/bin/redis-cli -h 127.0.0.1 -p 6381 -a 1234
然后通过:info replication 命令来查看具体主从服务
通过 tail -f xxx.conf 来实时刷新日志查看
三:哨兵模式Linux下搭建
1:基本介绍
介绍了主从复制后,我们知道slave成为master的从节点后,一旦master主节点宕机,我们可以选择一个正常的slave成为新的主节点,其它从节点再去连接这个新的主节点,实现手动的故障恢复。但是,人工干预效率低、易出错,并且故障感知滞后,不具备生产实用性。一个能够自动感知系统故障、自动故障转移的可靠组件,肯定是生产环境中最需要的。为此,Redis官方提供一个高可用的解决方案----哨兵(Sentinel),使用它可以搭建一个即使无人干预也能抵抗某些类型失败的高可用的Redis分布式系统。
哨兵是Redis的一种运行模式,它专注于对Redis实例(主节点、从节点)运行状态的监控,并能够在主节点发生故障时通过一系列的机制实现选主及主从切换,实现故障转移,确保整个Redis系统的可用性。
Redis Sentinel官方文档
Redis官方中说明了Redis哨兵具备如下几个功能:
监控(Monitoring):
持续监控Redis主节点、从节点是否处于预期的工作状态。
通知(Notification):
哨兵可以把Redis实例的运行故障信息通过API通知监控系统或者其他应用程序。
自动故障恢复(Automatic failover):
当主节点运行故障时,哨兵会启动自动故障恢复流程:某个从节点会升级为主节点,其它从节点会使用新的主节点进行主从复制,
通知客户端使用新的主节点进行。
配置中心(Configuration provider):
哨兵可以作为客户端服务发现的授权源,客户端连接到哨兵请求给定服务的Redis主节点地址。如果发生故障转移,哨兵会通知新的地址。
这里要注意:哨兵并不是Redis代理,只是为客户端提供了Redis主从节点的地址信息。
Sentinel的分布式特性:
Redis Sentinel是一个分布式系统,它被设计为基于一套配置,并在多个哨兵实例的配合下工作。多实例共同协作有以下优势:
①:主节点的系统故障是在多个实例共同认可的情况下完成的,大大降低了误报的概率。
②:即使不是所有的哨兵实例都正常运行哨兵集群也能正常工作(除非哨兵实例全宕机),这大大增加了系统的稳定性和健壮性。
2:哨兵模式的作用
哨兵服务(sentinel)通过发送命令让Redis服务器(主服务器和从服务器)返回当前运行状态;当哨兵监测到master宕机后,哨兵服务会自动将Slave切换成Master ,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机;然而一个哨兵进程服务对Redis服务器进行监控,也可能会出现宕机问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式,就算一个哨兵进程宕机,后面的哨兵还能继续工作
优点:
哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有
主从可以自动切换,系统更健壮,可用性更高
缺点:
Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂
3:故障切换过程
假设Master服务器宕机,哨兵A先检测到这个结果,并不会马上进行failover(故障转移)过程,仅仅是哨兵A主观上认为Master服务器不可用,这个现象成为主观下线(不能肯定是Master宕机了)。当后面的哨兵B,哨兵C,哨兵D....也检测到Master服务器不可用,并且随着多个哨兵服务主观认为Master宕机且主观认为的数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover(故障转移)操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线(Master确实是宕机了)。这样对于客户端而言,一切都是透明的。
4:哨兵模式的工作方式
①:每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的主服务器(Master)、从服务器(Slave)、哨兵(Sentinel)进程
发送一个PING命令。 ②:如果一个实例(instance)距离最后一次有效回复PING命令的时间超过 down-after-milliseconds 选项所指定的值(默认30秒),
这个实例会被Sentinel进程标记为主观下线(SDOWN) ③:如果一个Master被标记为主观下线(SDOWN),则正在监视这个Master的所有 Sentinel进程要以每秒一次的频率确认Master
是否的确进入了主观下线状态 ④:当有足够数量的Sentinel进程(大于等于配置文件指定的值)在指定的时间范围内确认Master进入了主观下线状态(SDOWN),
则主Master会被标记为客观下线(ODOWN) ⑤:在一般情况下, 每个Sentinel进程会以每10秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送INFO命令,
目的是发现slave结点,确定主从关系。 ⑥:当Master被Sentinel进程标记为客观下线(ODOWN)时,Sentinel进程向下线的Master的所有Slave发送INFO命令的频率会从
10秒一次改为每秒一次。 ⑦:若没有足够数量的Sentinel进程同意Master下线,Master的客观下线状态就会被移除。若之前被标记的Master重新又向Sentinel进程
发送PING命令返回有效回复,Master主服务器的主观下线状态就会被移除。
5:搭建哨兵模式
哨兵模式是基于主从复制模式之上的,所以自己得准备一个基本的一主二从的主从模式环境 注:这里主从搭建完全使用第二小节的:主从复制Linux下搭建里的目录配置 这里哨兵模式我使用多哨兵模式(都在127.0.0.1机器里): ./redisSentinel26379 此文件夹下存放哨兵A 端口:26379 ./redisSentinel26380 此文件夹下存放哨兵B 端口:26380 ./redisSentinel26381 此文件夹下存放哨兵C 端口:26381 ①:创建文件夹专门存放哨兵模式 mkdir /home/sentinel ②:拷贝3份Redis程序当哨兵程序并复制哨兵配置文件(这里我直接拷贝6379原主从复制主机) cp -r /home/redis6379 /home/sentinel/redisSentinel26379 cp -r /home/redis6379 /home/sentinel/redisSentinel26380 cp -r /home/redis6379 /home/sentinel/redisSentinel26381 rm -rf /home/sentinel/redisSentinel26379/conf/* rm -rf /home/sentinel/redisSentinel26380/conf/* rm -rf /home/sentinel/redisSentinel26381/conf/* cp /home/redis-6.2.6/sentinel.conf /home/sentinel/redisSentinel26379/conf/sentinel26379.conf cp /home/redis-6.2.6/sentinel.conf /home/sentinel/redisSentinel26380/conf/sentinel26380.conf cp /home/redis-6.2.6/sentinel.conf /home/sentinel/redisSentinel26381/conf/sentinel26381.conf ③:编写哨兵启动脚本 cd /home/sentinel cat > startRedisSentinel.sh << EOF #!/bin/sh /home/sentinel/redisSentinel26379/bin/redis-sentinel /home/sentinel/redisSentinel26379/conf/sentinel26379.conf 2>&1 & /home/sentinel/redisSentinel26380/bin/redis-sentinel /home/sentinel/redisSentinel26380/conf/sentinel26380.conf 2>&1 & /home/sentinel/redisSentinel26381/bin/redis-sentinel /home/sentinel/redisSentinel26381/conf/sentinel26381.conf 2>&1 & echo "redis sentinel is start" EOF chmod 744 startRedisSentinel.sh
6:更改配置文件
注:每个哨兵的配置文件都必须一致,处理端口和日志文件存在差别,其它一样都监控某一台主机
①:保护模式,默认开启,拒绝外部访问,建议关闭 -- 设置为:no protected-mode no ②:指定新的PID文件路径 (windows下没有) pidfile /var/run/redis-sentinel26379.pid ③:日志文件位置 logfile "./redisSentinelLog26379.log" ④:工作目录(存储运行时的文件) dir /home/sentinel ⑤:配置监听的主服务器,这里 sentinel monitor 代表监控 sentinel monitor mymaster 127.0.0.1 6379 2 ⑥:定义连接Master服务的密码(搭建的主从服务密码必须一致,要不然有问题) sentinel auth-pass mymaster 1234
7:启动哨兵测试
注:必须先启动主从复制机器,确认没问题后再次启动多哨兵机器
执行之前脚本启动:./startRedisSentinel.sh
启动哨兵日志打印:
启动26379、26380、26381三台哨兵服务(以26379日志来说,三个日志一样)
~# oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
~# Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=9099, just started
~# Configuration loaded
~* Increased maximum number of open files to 10032 (it was originally set to 1024).
~* monotonic clock: POSIX clock_gettime
~* Running mode=sentinel, port=26379.
~# WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
~# Sentinel ID is ae12e9902494e32aa77172ad7900dafb83b29ea4
~# +monitor master mymaster 127.0.0.1 6379 quorum 2
~* +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
~* +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
~* +sentinel sentinel dbeba218078a3400c04c0fb2247861be0167e2b8 127.0.0.1 26381 @ mymaster 127.0.0.1 6379
~* +sentinel sentinel 335d08176477f79734a60420f0b1890303c9aec1 127.0.0.1 26380 @ mymaster 127.0.0.1 6379
使用客户端连接哨兵服务:
/home/sentinel/redisSentinel26379/bin/redis-cli -h 127.0.0.1 -p 26379
哨兵基本命令:
ping
回复PONG.
sentinel masters
显示被监控的所有master以及它们的状态
sentinel master <master name>
显示指定被监控的master的信息和状态;如我们设置的名称为mymaster
sentinel slaves <master name>
显示指定master的所有slave以及它们的状态
sentinel get-master-addr-by-name <master name>
返回指定master的ip和端口,如果正在进行failover或者failover已经完成,
将会显示被提升为master的slave的ip和端口。
sentinel reset <pattern>
重置名字匹配该正则表达式的所有的master的状态信息,清楚其之前的状态信息,以及slaves信息
sentinel failover <master name>
强制sentinel执行failover,并且不需要得到其他sentinel的同意。
但是failover后会将最新的配置发送给其他sentinel。
主机宕机模拟:
强行杀死主节点6379服务器后的30秒,哨兵就会进行主节点选举
哨兵26379日志打印:
~# +sdown master mymaster 127.0.0.1 6379
~# +odown master mymaster 127.0.0.1 6379 #quorum 2/2
~# +new-epoch 1
~# +try-failover master mymaster 127.0.0.1 6379
~# +vote-for-leader ae12e9902494e32aa77172ad7900dafb83b29ea4 1
~# dbeba218078a3400c04c0fb2247861be0167e2b8 voted for ae12e9902494e32aa77172ad7900dafb83b29ea4 1
~# 335d08176477f79734a60420f0b1890303c9aec1 voted for ae12e9902494e32aa77172ad7900dafb83b29ea4 1
~# +elected-leader master mymaster 127.0.0.1 6379
~# +failover-state-select-slave master mymaster 127.0.0.1 6379
~# +selected-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
~* +failover-state-send-slaveof-noone slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
~* +failover-state-wait-promotion slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
~# +promoted-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
~# +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
~* +slave-reconf-sent slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
~* +slave-reconf-inprog slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
~* +slave-reconf-done slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
~# -odown master mymaster 127.0.0.1 6379
~# +failover-end master mymaster 127.0.0.1 6379
~# +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380
~* +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
~* +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
~# +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
哨兵26380、26381日志打印(两个一样)
~# +sdown master mymaster 127.0.0.1 6379
~# +new-epoch 1
~# +vote-for-leader ae12e9902494e32aa77172ad7900dafb83b29ea4 1
~# +odown master mymaster 127.0.0.1 6379 #quorum 3/2
~# Next failover delay: I will not start a failover before Fri Jan 14 01:16:10 2022
~# +config-update-from sentinel ae12e9902494e32aa77172ad7900dafb83b29ea4 127.0.0.1 26379 @ mymaster 127.0.0.1 6379
~# +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380
~* +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
~* +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
~# +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
通过上面的日志可以看出6379宕机了,但是通过哨兵选举6380切换为主机,并把它从机设置连接为6380为主机
原主机宕机后上线模拟:
命令切换到home目录:/home/redis6379/bin/redis-server /home/redis6379/conf/redis6379.conf 2>&1 &
但是启动6379后日志拼命打印如下日志,并发现连不上哨兵选举的主机(在Redis服务器有密码的情况下) ~* Retrying with SYNC... ~# MASTER aborted replication with an error: NOAUTH Authentication required. ~* Reconnecting to MASTER 127.0.0.1:6381 after failure ~* MASTER <-> REPLICA sync started ~* Non blocking connect for SYNC fired the event. ~* Master replied to PING, replication can continue... ~* (Non critical) Master does not understand REPLCONF listening-port: -NOAUTH Authentication required. ~* (Non critical) Master does not understand REPLCONF capa: -NOAUTH Authentication required. ~* Partial resynchronization not possible (no cached master) ~# Unexpected reply to PSYNC from master: -NOAUTH Authentication required. 这时候就得杀死6379 Redis服务后进行简单配置,在redis6379.conf内找到 masterauth配置主机密码 masterauth 1234 这个配置在主从复制时就可以配置密码
哨兵26380日志打印:
~# -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
~* +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
~# +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
~* +reboot slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
~# -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
哨兵26379、26381日志打印(两个一样)
~# -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
~# +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
~* +reboot slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
~# -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
这时6379的Redis服务就会被当为从机来指向6380哨兵选举的Redis主机上
8:哨兵日志输出状态说明
@
字符之后的内容用于指定主服务器,这些内容是可选的,它们仅在@字符之前的内容指定的
实例不是主服务器时使用
+slave
一个新的从服务器已经被Sentinel识别并关联。
+sdown
给定的实例现在处于主观下线状态。
-sdown
给定的实例已经不再处于主观下线状态。
+odown
给定的实例现在处于客观下线状态。
-odown
给定的实例已经不再处于客观下线状态。
+new-epoch
当前的纪元(epoch)已经被更新。
+try-failover
一个新的故障迁移操作正在执行中,等待被大多数 Sentinel 选中(waiting to be
elected by the majority)
+reset-master
主服务器已被重置
+monitor
主节点已被监视器监控
+failover-state-reconf-slaves
故障转移状态切换到了 reconf-slaves 状态。
+failover-detected
另一个 Sentinel 开始了一次故障转移操作,或者一个从服务器转换成了主服务器。
+slave-reconf-sent
领头(leader)的 Sentinel 向实例发送了 [SLAVEOF](/commands/slaveof.html) 命令,
为实例设置新的主服务器。
+slave-reconf-inprog
实例正在将自己设置为指定主服务器的从服务器,但相应的同步过程仍未完成。
+slave-reconf-done
从服务器已经成功完成对新主服务器的同步。
-dup-sentinel
对给定主服务器进行监视的一个或多个 Sentinel 已经因为重复出现而被移除 —— 当Sentinel实
例重启的时候,就会出现这种情况。
+sentinel
一个监视给定主服务器的新 Sentinel 已经被识别并添加。
+elected-leader
赢得指定纪元的选举,可以进行故障迁移操作了。
+failover-state-select-slave
故障转移操作现在处于 select-slave 状态 —— Sentinel 正在寻找可以升级为主服务器的从服务器。
no-good-slave
Sentinel 操作未能找到适合进行升级的从服务器。Sentinel 会在一段时间之后再次尝试寻找合适的
从服务器来进行升级,又或者直接放弃执行故障转移操作。
selected-slave
Sentinel 顺利找到适合进行升级的从服务器。
failover-state-send-slaveof-noone
Sentinel 正在将指定的从服务器升级为主服务器,等待升级功能完成。
failover-end-for-timeout
故障转移因为超时而中止,不过最终所有从服务器都会开始复制新的主服务器
(slaves will eventually be configured to replicate with the new master anyway)
failover-end
故障转移操作顺利完成。所有从服务器都开始复制新的主服务器了。
+switch-master
配置变更,主服务器的 IP 和地址已经改变。 这是绝大多数外部用户都关心的信息。
+tilt
进入 tilt 模式。
-tilt
退出 tilt 模式。
四:集群模式Linux下搭建(重点)
Redis 是我们目前大规模使用的缓存中间件,由于它强大高效而又便捷的功能,得到了广泛的使用。单节点的Redis已经就达到了很高的性能,为了提高可用性我们用主从复制到哨兵再到更强大的Redis集群。
Redis集群(Redis Cluster)是Redis 3.0开始引入的一个分布式存储系统;Redis集群是一个可以在多个Redis节点之间进行数据共享的设施(installation),不过Redis集群不支持那些需要同时处理多个键的Redis命令(如:mset,hmset...), 因为执行这些命令需要在多个Redis节点之间移动数据,并且在高负载的情况下,这些命令将降低Redis集群的性能,并导致不可预测的错误。
下面我将详细介绍Redis集群,主从和哨兵的搭建只是便于理解集群的演变,本节适用3.0~6.0的Redis版本
1:Redis集群介绍
Redis集群通过分区(partition)来提供一定程度的可用性(availability);
即使集群中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。
Redis集群提供了以下两个好处:
将数据自动切分(split)到多个节点的能力,这就是下面说的数据分片。
当集群中的一部分节点失效或者无法进行通讯时,仍然可以继续处理命令请求的能力。
2:数据分片
Redis集群没有使用一致性hash(consistency hashing), 而是引入了哈希槽的概念,使用哈希槽来实现数据分片(sharding);一个Redis集群包含16384个哈希槽(hash slot),数据库中的每个键计算后都会在这16384个哈希槽的其中一个槽位上;集群中槽位计算使用公式CRC16(key) % 16384来计算键key属于哪个槽里, 其中 CRC16(key) 语句用于计算键key的CRC16校验和。
集群中的每个节点负责处理一部分哈希槽。举个例子,一个集群中存在三个Master主节点,那么它最多可以有三个哈希槽, 其中:
节点 A 负责处理 0 号至 5460 号哈希槽。 [5461个槽点]
节点 B 负责处理 5461 号至 10922 号哈希槽。 [5462个槽点]
节点 C 负责处理 10923 号至 16383 号哈希槽。 [5461个槽点]
这种将哈希槽分布到不同节点的做法使得用户可以很容易地向集群中添加或者删除节点。
因为添加一个集群Master节点我只需要把前三个Master节点上的槽位里的槽点提出来部分分给新添加上来的Master节点上的槽位里;删除也差不多,只需要提前把待删除的主节点Master上的槽点全部提取出来分给其它Master节点上的槽位里,再删除主节点Master
这时我要添加一个key键比如说:设置一个key,叫MyName的key == > set MyName antLaddie
按照Redis Cluster的哈希槽算法,CRC16(\'MyName\') = 45121;45121 % 16384 = 12353 那么这个key就被分配到了节点C上;同样的,当我连接(A,B,C)的任意一个节点想获取MyName这个key,都会转到节点C上;如果用户将新节点 D 添加到集群中,那么集群只需要将节点 A 、B 、C 中的部分槽移动到节点 D 就可以了。增加一个D节点的结果可能如下(把ABC槽点分部分给D):
节点A覆盖1365-5460
节点B覆盖6827-10922
节点C覆盖12288-16383
节点D覆盖0-1364,5461-6826,10923-1228
与此类似, 如果用户要从集群中移除节点 A , 那么集群只需要将节点 A 中的所有哈希槽移动到节点 B 和节点 C ,然后再移除空白(不包含任何哈希槽)的节点 A 就可以了,因为将一个哈希槽从一个节点移动到另一个节点不会造成节点阻塞, 所以无论是添加新节点还是移除已存在节点,又或者改变某个节点包含的哈希槽数量, 都不会造成集群下线。
3:集群主从复制模型
为了使集群在一部分节点下线或者无法与集群的大多数(majority)节点进行通讯的情况下, 仍然可以正常运作, Redis集群对节点使用了主从复制功能;集群中的每个节点都有 1 个至 N 个复制品(replica), 其中一个复制品为主节点(master), 而其余的 N-1 个复制品为从节点(slave)。在之前列举的节点A 、B 、C的例子中,如果节点 B 下线了(没有从节点), 那么集群将无法正常运行, 因为集群找不到节点来处理 5461 号至 10922号的哈希槽位。另一方面, 假如在创建集群的时候(或者至少在节点 B 下线之前), 我们为主节点 B 添加了从节点 B1 , 那么当主节点 B 下线的时候,集群就会将 B1 设置为新的主节点, 并让它代替下线的主节点 B , 继续处理 5461 号至 10922 号的哈希槽, 这样集群就不会因为主节点 B 的下线而无法正常运作了。不过如果节点 B 和 B1 都下线的话, Redis 集群还是会停止运作。
总的来说,当某个负责指定插槽槽位的主节点(Master)下线了,并且负责这个槽位内的主从模型范围内里找不到替代主节点的从机后,整个集群将会置为不可用的状态;但是可以通过 cluster-require-full-coverage no 来关闭这限制,表示除下线的槽位不可以,其余槽位可正常写入和读取
4:Redis一致性保证
Redis并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操作
第一个原因是因为集群是用了异步复制. 写操作过程:
①:客户端向主节点B写入一条命令.
②:主节点B向客户端回复命令状态(成功还是失败).
③:主节点将写操作复制给当前主从复制下的从节点 B1、B2、B3...
主节点对命令的复制工作发生在返回命令状态回复之后,因为如果每次处理命令请求都需要等待复制操作完成的话,那么主节点处理命令请求的速度
将极大地降低;我们必须在性能和一致性之间做出权衡。注意:Redis集群可能会在将来提供同步写的方法。
第二原因是Redis集群可能会丢失命令的情况是集群出现了网络分区并且一个客户端与至少包括一个主节点在内的少数实例被孤立:
假设集群包含A、B、C、A1、B1、C1六个节点,其中 A、B、C 为主节点, A1、B1、C1 为A,B,C的从节点,还有一个客户端 Z1 假设
集群中发生网络分区,那么集群可能会分为两方,大部分的一方包含节点 A、C、A1、B1、C1 ,小部分的一方则包含节点 B 和客户端 Z1。
Z1仍然能够向主节点B中写入, 如果网络分区发生时间较短,那么集群将会继续正常运作,如果分区的时间足够让大部分的一方将B1选举为新的
master,那么Z1写入B中得数据便丢失了.
注意,在网络分裂出现期间,客户端 Z1 可以向主节点 B 发送写命令的最大时间是有限制的,
这一时间限制称为节点超时时间(node timeout),是 Redis 集群的一个重要的配置选项
5:Redis集群搭建准备
搭建Redis集群至少需要准备3个主Master节点和3个从Slave节点,总共6个节点才能搭建一套Redis集群,因为6台机器成本太大,所以换成伪集群在一台机器上部署;这里我将介绍 redis3.0~6.0 两种方式搭建集群
注:若不是工作需求直接使用5.0+版本,直接使用官方自带的redis-cli -c 来操作集群,不需要在下载Ruby脚本啥的
①:节点说明 127.0.0.1:8001[Master主节点] 127.0.0.1:8004 [Slave从节点] 127.0.0.1:8002[Master主节点] 127.0.0.1:8005 [Slave从节点] 127.0.0.1:8003[Master主节点] 127.0.0.1:8006 [Slave从节点] 127.0.0.1:8007 [后面用来测试扩容] 127.0.0.1:8008 [后面用来测试扩容] 127.0.0.1:8009 [后面用来测试扩容] 127.0.0.1:8010 [后面用来测试扩容] ②:搭建基本环境及BaseRedis(就是一个单机版Redis) cd /home yum -y install cpp binutils glibc glibc-kernheaders glibc-common glibc-devel gcc make gcc-c++ libstdc++-devel tcl # 通过 rz -y 上传Redis压缩包 redis-3.2.9.tar.gz 或 redis-6.2.6.tar.gz tar -zxvf redis-3.2.9.tar.gz cd redis-3.2.9/ -p /usr/local/redisCluster/redisBase/bin make && make PREFIX=/usr/local/redisCluster/redisBase install # 到这我们就有一个基本的Redis程序了,可以用这个redisBase里的命令来搭配不同配置文件批量启动集群 # 批量集群节点文件夹创建 -p /usr/local/redisCluster/redis8001 /usr/local/redisCluster/redis8001/etc -p /usr/local/redisCluster/redis8002 /usr/local/redisCluster/redis8002/etc -p /usr/local/redisCluster/redis8003 /usr/local/redisCluster/redis8003/etc -p /usr/local/redisCluster/redis8004 /usr/local/redisCluster/redis8004/etc -p /usr/local/redisCluster/redis8005 /usr/local/redisCluster/redis8005/etc -p /usr/local/redisCluster/redis8006 /usr/local/redisCluster/redis8006/etc -p /usr/local/redisCluster/redis8007 /usr/local/redisCluster/redis8007/etc -p /usr/local/redisCluster/redis8008 /usr/local/redisCluster/redis8008/etc -p /usr/local/redisCluster/redis8009 /usr/local/redisCluster/redis8009/etc -p /usr/local/redisCluster/redis8010 /usr/local/redisCluster/redis8010/etc # 为每个节点文件夹里面放一个Redis配置文件 cp /home/redis-3.2.9/redis.conf /usr/local/redisCluster/redis8001/etc/redisNodes_8001.conf cp /home/redis-3.2.9/redis.conf /usr/local/redisCluster/redis8002/etc/redisNodes_8002.conf cp /home/redis-3.2.9/redis.conf /usr/local/redisCluster/redis8003/etc/redisNodes_8003.conf cp /home/redis-3.2.9/redis.conf /usr/local/redisCluster/redis8004/etc/redisNodes_8004.conf cp /home/redis-3.2.9/redis.conf /usr/local/redisCluster/redis8005/etc/redisNodes_8005.conf cp /home/redis-3.2.9/redis.conf /usr/local/redisCluster/redis8006/etc/redisNodes_8006.conf cp /home/redis-3.2.9/redis.conf /usr/local/redisCluster/redis8007/etc/redisNodes_8007.conf cp /home/redis-3.2.9/redis.conf /usr/local/redisCluster/redis8008/etc/redisNodes_8008.conf cp /home/redis-3.2.9/redis.conf /usr/local/redisCluster/redis8009/etc/redisNodes_8009.conf cp /home/redis-3.2.9/redis.conf /usr/local/redisCluster/redis8010/etc/redisNodes_8010.conf
基本的文件创建好,我们就要修改每个节点的配置文件了,然后和一键启动脚本即可
①:修改每个节点下的配置文件,如下示例,其它文件只需要套用此配置,把8001批量替换800* 具体替换的vim命令 :%s/源字符串/目标字符串/g # 端口号 port 8001
# 设置外部任何IP都可以连接,或者设置指定IP连接 用 "-" 分割
bind 0.0.0.0 或 * 新版本设置:bind * -::* # 后台启动 daemonize yes # 指定数据文件存放发位置,必须指定不同的目录位置,不然会丢失运行的一些数据,比如日志,持久化文件等 /usr/local/redisCluster/redis8001 # 指定新的PID进程文件路径 (windows下没有)当 daemonize yes为守护进程时必须设置 pidfile /var/run/redisNodes_8001.pid # 日志文件名称 logfile "/usr/local/redisCluster/redis8001/redisNodes_8001.log" # RDB持久化快照的的文件名 dbfilename dumpNodes_8001.rdb # 开启aof 注同时开启AOF和RDB时当Redis重启时,会优先使用AOF文件来还原数据集,若考虑性能则可以关闭aof appendonly yes # aof文件路径 appendfilename "appendonly_8001.aof" # 是否开启集群(必须开启) cluster-enabled yes #集群节点配置文件 cluster-config- nodes-8001.conf # 集群连接超时时间 cluster-node-timeout 5000 ②:创建Redis集群启动脚本 RedisClusterStart.sh 这里的 8007 8008 8009 8010虽然启动,但是不使用,后面扩容使用 cat > RedisClusterStart.sh << EOF #!/bin/sh /usr/local/redisCluster/redisBase/bin/redis-server /usr/local/redisCluster/redis8001/etc/redisNodes_8001.conf /usr/local/redisCluster/redisBase/bin/redis-server /usr/local/redisCluster/redis8002/etc/redisNodes_8002.conf /usr/local/redisCluster/redisBase/bin/redis-server /usr/local/redisCluster/redis8003/etc/redisNodes_8003.conf /usr/local/redisCluster/redisBase/bin/redis-server /usr/local/redisCluster/redis8004/etc/redisNodes_8004.conf /usr/local/redisCluster/redisBase/bin/redis-server /usr/local/redisCluster/redis8005/etc/redisNodes_8005.conf /usr/local/redisCluster/redisBase/bin/redis-server /usr/local/redisCluster/redis8006/etc/redisNodes_8006.conf /usr/local/redisCluster/redisBase/bin/redis-server /usr/local/redisCluster/redis8007/etc/redisNodes_8007.conf /usr/local/redisCluster/redisBase/bin/redis-server /usr/local/redisCluster/redis8008/etc/redisNodes_8008.conf /usr/local/redisCluster/redisBase/bin/redis-server /usr/local/redisCluster/redis8009/etc/redisNodes_8009.conf /usr/local/redisCluster/redisBase/bin/redis-server /usr/local/redisCluster/redis8010/etc/redisNodes_8010.conf echo "redis is start" EOF chmod 744 RedisClusterStart.sh
这时我们就把10个节点的Redis启动了,此时的Redis都是以集群模式启动了,只是现在没有让它们之间集群关联
6:Redis集群关联(原生)
集群的搭建可以分为四步:
①:启动节点:将节点以集群模式启动,此时节点是独立的,并没有建立联系;[上一小节已启动]
②:节点握手:让独立的节点连成一个网络;
③:分配槽:将16384个槽分配给主节点;
④:指定主从关系:为从节点指定主节点。
注:这里我只使用 8001 ~ 8006 其中 8001 ~ 8003为Master主节点,要分配槽的,8004 ~ 8006 为Slave从节点
1:节点握手
我们上面的10个节点启动以后是相互独立的,并不知道其它节点的存在;需要进行节点握手,将独立的节点组成一个网络。节点握手使用 cluster meet ip port 命令实现,例如在8001节点中执行 cluster meet 10.200.157.126 8002,可以完成8001节点和8002节点的握手;注意ip使用的是局域网ip而不是 localhost 或127.0.0.1,是为了其他机器上的节点或客户端也可以访问。可使用cluster nodes查看
以集群模式连接8001节点:(这里一定要加 -c 以集群模式启动)
/usr/local/redisCluster/redisBase/bin/redis-cli -c -h 10.200.157.126 -p 8001
查看当前节点:(此时8001就单独一个节点集群)
10.200.157.126:8001> cluster nodes
3a00331cad40d52e5755c22699668574e090fa7e :8001 myself,master - 0 0 0 connected
在 8001 节点下使用Cluster meet 命令把所有节点加入到集群,完成节点握手:
cluster meet 10.200.157.126 8002
cluster meet 10.200.157.126 8003
cluster meet 10.200.157.126 8004
cluster meet 10.200.157.126 8005
cluster meet 10.200.157.126 8006
2:分配槽
在Redis集群中,借助槽实现数据分区。集群有16384个槽,槽是数据管理和迁移的基本单位。当数据库中的16384个槽都分配了节点时,集群处于上线状态(ok);如果有任意一个槽没有分配节点,则集群处于下线状态(fail)。cluster info命令可以查看集群状态,分配槽之前状态为fail;
未分配完(或未分配)槽点:
10.200.157.126:8001> cluster info
cluster_state:fail 【集群状态fail失败 ok 成功】
cluster_slots_assigned:0 【当前整个集群分配的插槽点数,未到16384是无法启动的】
通过redis_cli客户端命令为 8001 ~ 8003 分配插槽 【每个节上的插槽随便分配点数,这里我就平均分配】
/usr/local/redisCluster/redisBase/bin/redis-cli -p 8001 cluster addslots 0..5460
/usr/local/redisCluster/redisBase/bin/redis-cli -p 8002 cluster addslots 5461..10922
/usr/local/redisCluster/redisBase/bin/redis-cli -p 8003 cluster addslots 10923..16383
分配完槽点显示正常:
10.200.157.126:8001> cluster info
cluster_state:ok
cluster_slots_assigned:16384
3:指定主从关系
集群中指定主从关系就不再使用slaveof命令了,而是使用 cluster replicate 命令;参数使用节点ID
从分配插槽时就已经确定了主节点Master了,因为只有主节点才可以分配插槽,下面我将从机连接到主机上 Master 8001 --> Slave 8004 Master 8002 --> Slave 8005以上是关于Redis集群的三种方式的主要内容,如果未能解决你的问题,请参考以下文章