Redis 怎么才能做到高可用
对于 Redis 主从架构, slave 可以对应多个本身可以保障高可用, 但是对于一个 master 节点, 如果宕机, 整个缓存系统就无法进行写的操作, 显然整个系统会无法做到高可用
sentinel 哨兵可以监测 master 节点是否正常运行(会自动识别出所有的 slave 信息), 如果出现宕机, 则会在对应的 slave 节点中通过投票的方式来选取一个 slave 节点作为新的 master 节点,
旧的 master 节点恢复之后会被接管成为新的 master 节点的 slave 节点. 同时 sentinel 哨兵节点本身也是集群的方式部署来保障自身的高可用, 并且一个 sentinel 是可以同时监听多个 master 节点
对于 sentinel 哨兵节点的一些核心概念:
1,sdown 和 odown 转换机制
sdown 和 odown 两种失败状态
sdown 是主观宕机, 就一个哨兵如果自己觉得一个 master 宕机了, 那么就是主观宕机
odown 是客观宕机, 如果 quorum 数量的哨兵都觉得一个 master 宕机了, 那么就是客观宕机
sdown 达成的条件很简单, 如果一个哨兵 ping 一个 master, 超过了 is-master-down-after-milliseconds 指定的毫秒数之后, 就主观认为 master 宕机
sdown 到 odown 转换的条件很简单, 如果一个哨兵在指定时间内, 收到了 quorum 指定数量的其他哨兵也认为那个 master 是 sdown 了, 那么就认为是 odown 了, 客观认为 master 宕机
2, 哨兵集群的自动发现机制
哨兵互相之间的发现, 是通过 Redis 的 pub/sub 系统实现的, 每个哨兵都会往__sentinel__:hello 这个 channel 里发送一个消息, 这时候所有其他哨兵都可以消费到这个消息, 并感知到其他的哨兵的存在
每隔两秒钟, 每个哨兵都会往自己监控的某个 master+slaves 对应的__sentinel__:hello channel 里发送一个消息, 内容是自己的 host,ip 和 runid 还有对这个 master 的监控配置
每个哨兵也会去监听自己监控的每个 master+slaves 对应的__sentinel__:hello channel, 然后去感知到同样在监听这个 master+slaves 的其他哨兵的存在
每个哨兵还会跟其他哨兵交换对 master 的监控配置, 互相进行监控配置的同步
3,slave 配置的自动纠正
哨兵会负责自动纠正 slave 的一些配置, 比如 slave 如果要成为潜在的 master 候选人, 哨兵会确保 slave 在复制现有 master 的数据; 如果 slave 连接到了一个错误的 master 上, 比如故障转移之后, 那么哨兵会确保它们连接到正确的 master 上
4,slave->master 选举算法
如果一个 master 被认为 odown 了, 而且 majority 哨兵都允许了主备切换, 那么某个哨兵就会执行主备切换操作, 此时首先要选举一个 slave 来
会考虑 slave 的一些信息
(1)跟 master 断开连接的时长
(2)slave 优先级
(3)复制 offset
(4)run id
如果一个 slave 跟 master 断开连接已经超过了 down-after-milliseconds 的 10 倍, 外加 master 宕机的时长, 那么 slave 就被认为不适合选举为 master
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
接下来会对 slave 进行排序
(1)按照 slave 优先级进行排序, slave priority 越低, 优先级就越高
(2)如果 slave priority 相同, 那么看 replica offset, 哪个 slave 复制了越多的数据, offset 越靠后, 优先级就越高
(3)如果上面两个条件都相同, 那么选择一个 run id 比较小的那个 slave
5,quorum 和 majority
每次一个哨兵要做主备切换, 首先需要 quorum 数量的哨兵认为 odown, 然后选举出一个哨兵来做切换, 这个哨兵还得得到 majority 哨兵的授权, 才能正式执行切换
如果 quorum <majority, 比如 5 个哨兵, majority 就是 3,quorum 设置为 2, 那么就 3 个哨兵授权就可以执行切换
但是如果 quorum>= majority, 那么必须 quorum 数量的哨兵都授权, 比如 5 个哨兵, quorum 是 5, 那么必须 5 个哨兵都同意授权, 才能执行切换
6,configuration epoch
哨兵会对一套 Redis master+slave 进行监控, 有相应的监控的配置
执行切换的那个哨兵, 会从要切换到的新 master(salve->master)那里得到一个 configuration epoch, 这就是一个 version 号, 每次切换的 version 号都必须是唯一的
如果第一个选举出的哨兵切换失败了, 那么其他哨兵, 会等待 failover-timeout 时间, 然后接替继续执行切换, 此时会重新获取一个新的 configuration epoch, 作为新的 version 号
7,configuraiton 传播
哨兵完成切换之后, 会在自己本地更新生成最新的 master 配置, 然后同步给其他的哨兵, 就是通过之前说的 pub/sub 消息机制
这里之前的 version 号就很重要了, 因为各种消息都是通过一个 channel 去发布和监听的, 所以一个哨兵完成一次新的切换之后, 新的 master 配置是跟着新的 version 号的
其他的哨兵都是根据版本号的大小来更新自己的 master 配置的
1redis 安装
解压完成后可以看到 INSTALL 和 README.md 文件, 查看以获取更多有用信息.
在 README 文件中可以获取到软件的安装步骤. 以下安装步骤基于此.
- #step1 进入文件夹, 执行编译命令
- [root@ Redis-3.2.8]# make
- #step2 为了后面开发测试的方便, 把启动脚本, 配置文件, 日志文件统一放到 Redis 目录下
- [root@ Redis-3.2.8]# mkdir /usr/local/Redis
- [root@ Redis-3.2.8]# mkdir /usr/local/Redis/logs
- [root@ Redis-3.2.8]# mkdir /usr/local/Redis/bin
- [root@ Redis-3.2.8]# mkdir /usr/local/Redis/conf
- [root@ Redis-3.2.8]# mkdir /etc/sentinel
- [root@ Redis-3.2.8]# mkdir -p /var/sentinel/26377
- [root@ Redis-3.2.8]# mkdir -p /var/sentinel/26378
- [root@ Redis-3.2.8]# mkdir -p /var/sentinel/26379
- [root@ Redis-3.2.8]# cp Redis.conf sentinel.conf /usr/local/Redis/conf/
- [root@ src]# cp Redis-server Redis-sentinel Redis-cli /usr/local/Redis/bin/
- #step3 开启 Redis 服务, 检测其可用性
- [root@ bin]# Redis-server ../conf/Redis.conf
可以看到日志信息
其中有 3 个警告
第一个警告: The TCP backlog setting of 511 cannot be enforced because /proc/sys.NET/core/somaxconn is set to the lower value of 128.
意思是: TCP backlog 设置值, 511 没有成功, 因为 /proc/sys.NET/core/somaxconn 这个设置的是更小的 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.
意思是: overcommit_memory 参数设置为 0! 在内存不足的情况下, 后台程序 save 可能失败. 建议在文件 /etc/sysctl.conf 中将 overcommit_memory 修改为 1.
第三个警告: 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 延迟和内存使用问题. 执行 echo never> /sys/kernel/mm/transparent_hugepage/enabled 修复该问题.
临时解决方法:
- echo 511> /proc/sys.NET/core/somaxconn
- echo "vm.overcommit_memory=1"> /etc/sysctl.conf
- echo never> /sys/kernel/mm/transparent_hugepage/enabled.
永久解决方法:
- VIM /etc/sysctl.conf
- net.core.somaxconn = 512
- vm.overcommit_memory = 1
- kernel.mm.transparent_hugepage.enabled = never
Redis server 使用默认端口 6379 启动成功.
#step4 修改配置文件, 使其以后台服务方式运行.
- #what? 局域网内本机 IP.
- # 我是部署到同一机器上所以 IP 都一样, 端口不同
- bind 127.0.0.1
- # 修改默认端口, 避免被恶意脚本扫描.
- port 9999
- loglevel debug
- logfile /usr/local/Redis/logs/Redis.log.9999
- # 为服务设置安全密码
- requirepass redispass
- # 以守护进程方式运行
- daemonize yes
- #step5 重新启动 Redis.
- [root@ bin]# Redis-cli -p 9999 -a redispass shutdown
我使用脚本启动方式
启动脚本 redis_init_script 位于位于 Redis 的 /utils/ 目录下, redis_init_script 脚本代码如下:
- #!/bin/sh
- #
- # Simple Redis init.d script conceived to work on Linux systems
- # as it does use of the /proc filesystem.
- #Redis 服务器监听的端口
- REDISPORT=9999
- # 服务端所处位置
- EXEC=/usr/local/bin/Redis-server
- # 客户端位置
- CLIEXEC=/usr/local/bin/Redis-cli
- #Redis 的 PID 文件位置, 需要修改
- PIDFILE=/var/run/redis_${REDISPORT}.pid
- #Redis 的配置文件位置, 需将 ${REDISPORT}修改为文件名
- CONF="/etc/redis/${REDISPORT}.conf"
- case "$1" in
- start)
- if [ -f $PIDFILE ]
- then
- echo "$PIDFILE exists, process is already running or crashed"
- else
- echo "Starting Redis server..."
- $EXEC $CONF
- fi
- ;;
- stop)
- if [ ! -f $PIDFILE ]
- then
- echo "$PIDFILE does not exist, process is not running"
- else
- PID=$(cat $PIDFILE)
- echo "Stopping ..."
- $CLIEXEC -p $REDISPORT shutdown
- while [ -x /proc/${PID} ]
- do
- echo "Waiting for Redis to shutdown ..."
- sleep 1
- done
- echo "Redis stopped"
- fi
- ;;
- *)
- echo "Please use start or stop as first argument"
- ;;
- esac
可以根据你自己端口进行修改
- mkdir /etc/Redis
- cp Redis.conf /etc/Redis/9999.conf
将启动脚本复制到 / etc/init.d 目录下, 本例将启动脚本命名为 redisd(通常都以 d 结尾表示是后台自启动服务).
- cp redis_init_script /etc/init.d/redis_9999
- ./redis_9999 start
2 开启主从复制(master-slave)
主从模式的两个重要目的, 提升系统可靠性和读写分离提升性能.
这里通过修改端口的方式, 再启动端口为 9997 和 9998 的服务作为备 (从) 机.
备机启动需要修改配置文件部分属性(在 9999 配置的基础上).
- port 9997
- logfile /usr/local/Redis/logs/Redis.log.9997
- # 指定 master ip port
- slaveof 127.0.0.1 9999
- # 认证 master 时需要的密码. 必须和 master 配置的 requirepass 保持一致
- masterauth redispass
- requirepass redispass
- protected-mode no
从机 9998 配置同理
- port 9998
- logfile /usr/local/Redis/logs/Redis.log.9998
- slaveof 127.0.0.1 9999
- masterauth redispass
- requirepass redispass
- protected-mode no
开启从机服务
- [root@ bin]# ./redis_9997 start
- [root@ bin]# ./redis_9998 start
查看 slave 9998 日志(省略部分信息), 可以看出, slave 在启动时成功连接 master, 并接收到了 104 字节的同步数据.
- 6472:S 30 Mar 11:18:17.206 * Connecting to MASTER 127.0.0.1:9999
- 6472:S 30 Mar 11:18:17.206 * MASTER <-> SLAVE sync started
- 6472:S 30 Mar 11:18:17.223 * MASTER <-> SLAVE sync: receiving 104 bytes from master
- 6472:S 30 Mar 11:18:17.223 * MASTER <-> SLAVE sync: Finished with success
Redis 做一个基准的压测
你如果要对自己刚刚搭建好的 Redis 做一个基准的压测, 测一下你的 Redis 的性能和 QPS(query per second)
Redis 自己提供的 Redis-benchmark 压测工具, 是最快捷最方便的, 当然啦, 这个工具比较简单, 用一些简单的操作和场景去压测
1, 对 Redis 读写分离架构进行压测, 单实例写 QPS + 单实例读 QPS
- Redis-3.2.8/src
- ./Redis-benchmark -h 127.0.0.1
- -c <clients> Number of parallel connections (default 50)
- -n <requests> Total number of requests (default 100000)
- -d <size> Data size of SET/GET value in bytes (default 2)
根据你自己的高峰期的访问量, 在高峰期, 瞬时最大用户量会达到 10 万 +,-c 100000,-n 10000000,-d 500
1,QPS, 自己不同公司, 不同服务器, 自己去测试, 跟生产环境还有区别
生产环境, 大量的网络请求的调用, 网络本身就有开销, 你的 Redis 的吞吐量就不一定那么高了
QPS 的两个杀手: 一个是复杂操作, lrange, 挺多的; value 很大, 2 byte, 我之前用 Redis 做大规模的缓存
做商品详情页的 cache, 可能是需要把大串数据, 拼接在一起, 作为一个 JSON 串, 大小可能都几 k, 几个 byte
2, 水平扩容 Redis 读节点, 提升度吞吐量
就按照上一节课讲解的, 再在其他服务器上搭建 Redis 从节点, 单个从节点读请 QPS 在 5 万左右, 两个 Redis 从节点, 所有的读请求打到两台机器上去, 承载整个集群读 QPS 在 10 万 +
3 sentinel 模式故障自动迁移
Master-slave 主从复制避免了数据丢失带来的灾难性后果.
但是单点故障仍然存在, 在运行期间 master 宕机需要停机手动切换.
Sentinel 很好的解决了这个问题, 当 Master-slave 模式中的 Master 宕机后, 能够自主切换, 选择另一个可靠的 Redis-server 充当 master 角色, 使系统仍正常运行.
一般来说 sentinel server 需要大于等于 3 个.
这里通过修改端口的方式开启 3 个 sentinel server. 修改配置文件 sentinel.conf 部分属性
- # 服务运行端口号
- port 26379
- sentinel monitor mumaster 1270.0.0.1 9999 2
- #mymaster 为指定的 master 服务器起一个别名
- #master IP 和端口号
- #2 的含义: 当开启的 sentinel server 认为当前 master 主观下线的 (+sdown) 数量达到 2 时, 则 sentinel server 认为当前 master 客观下线 (+odown) 系统开始自动迁移. 2 的计算(建议):
- #sentinel server 数量的大多数, 至少为 count(sentinel server)/2 向上取整. 2>3/2(主观下线与客观下线?)
- #master 别名和认证密码. 这就提醒了用户, 在 master-slave 系统中, 各服务的认证密码应该保持一致.
- sentinel auth-pass mymaster redispass
- # 以守护进程方式运行
- daemonize yes
- logfile /usr/local/Redis/logs/sentinel.log.26379
- protected-mode no
- sentinel down-after-milliseconds mymaster 6000
- sentinel failover-timeout mymaster 18000
(多开服务只需要在以上配置基础上修改端口号, 其它保持不变 port 26378/port 26377)
开启 Sentinel 服务
- Redis-sentinel /etc/sentinel/26377.conf
- Redis-sentinel /etc/sentinel/26378.conf
- Redis-sentinel /etc/sentinel/26379.conf
启动之后可以看到日志信息, 每个哨兵都能去监控到对应的 Redis master, 并能够自动发现对应的 slave, 哨兵之间, 互相会自动进行发现, 用的就是之前说的 pub/sub, 消息发布和订阅 channel 消息系统和机制
检查哨兵状态
- Redis-cli -h 127.0.0.1 -p 26377 -a "redispass"
- sentinel master mymaster
- SENTINEL slaves mymaster
- SENTINEL sentinels mymaster
- SENTINEL get-master-addr-by-name mymaster
哨兵节点相关配置
1, 哨兵节点的增加和删除
增加 sentinal, 会自动发现
删除 sentinal 的步骤
(1)停止 sentinal 进程
(2)SENTINEL RESET *, 在所有 sentinal 上执行, 清理所有的 master 状态
(3)SENTINEL MASTER mastername, 在所有 sentinal 上执行, 查看所有 sentinal 对数量是否达成了一致
2,slave 的永久下线
让 master 摘除某个已经下线的 slave:SENTINEL RESET mastername, 在所有的哨兵上面执行
3,slave 切换为 Master 的优先级
slave->master 选举优先级: slave-priority, 值越小优先级越高
4, 基于哨兵集群架构下的安全认证
每个 slave 都有可能切换成 master, 所以每个实例都要配置两个指令
master 上启用安全认证, requirepass
master 连接口令, masterauth
sentinal,sentinel auth-pass <master-group-name> <pass>
5, 容灾演练
通过哨兵看一下当前的 master:SENTINEL get-master-addr-by-name mymaster
把 master 节点 kill -9 掉, pid 文件也删除掉
查看 sentinal 的日志, 是否出现 + sdown 字样, 识别出了 master 的宕机问题; 然后出现 + odown 字样, 就是指定的 quorum 哨兵数量, 都认为 master 宕机了
(1)三个哨兵进程都认为 master 是 sdown 了
(2)超过 quorum 指定的哨兵进程都认为 sdown 之后, 就变为 odown
(3)哨兵 1 是被选举为要执行后续的主备切换的那个哨兵
(4)哨兵 1 去新的 master(slave)获取了一个新的 config version
(5)尝试执行 failover
(6)投票选举出一个 slave 区切换成 master, 每隔哨兵都会执行一次投票
(7)让 salve,slaveof noone, 不让它去做任何节点的 slave 了; 把 slave 提拔成 master; 旧的 master 认为不再是 master 了
(8)哨兵就自动认为之前的 master 变成 slave, 将投票出的 slave 变成 master
(9)哨兵去探查了一下之前的 master(变成来 salve)的状态, 认为它 sdown 了
故障恢复, 再将旧的 master 重新启动, 查看是否被哨兵自动切换成 slave 节点
查看到结果将 9999 切换为 slave 节点
容灾演练日志
- Sentinel ID is ea82430cf7f6d452eb22bbf29b92fcf001c734c8
- 3010:X 21 Nov 22:37:31.405 # +monitor master mymaster 127.0.0.1 9999 quorum 2
- 3010:X 21 Nov 23:01:12.589 # +sdown master mymaster 127.0.0.1 9999
- 3010:X 21 Nov 23:01:12.642 # +odown master mymaster 127.0.0.1 9999 #quorum 3/2 -- 进入 ODOWN 状态时. 有三个哨兵认为 master 当机了
- 3010:X 21 Nov 23:01:12.642 # +new-epoch 7 -- 当前配置版本被更新时.
- 3010:X 21 Nov 23:01:12.642 # +try-failover master mymaster 127.0.0.1 9999 -- 尝试故障转移, 正等待其他 sentinel 的选举.
- 3010:X 21 Nov 23:01:12.658 # +vote-for-leader ea82430cf7f6d452eb22bbf29b92fcf001c734c8 7 -- 投票给领导
- 3010:X 21 Nov 23:01:12.668 # edace82644513417b676ee6eced3184771d6361d voted for ea82430cf7f6d452eb22bbf29b92fcf001c734c8 7
- 3010:X 21 Nov 23:01:12.668 # 7e68a54266703cbf429f5c6512a50a39ab94b943 voted for ea82430cf7f6d452eb22bbf29b92fcf001c734c8 7
- 3010:X 21 Nov 23:01:12.716 # +elected-leader master mymaster 127.0.0.1 9999 -- 被选举为去执行 failover 的时候.
- 3010:X 21 Nov 23:01:12.716 # +failover-state-select-slave master mymaster 127.0.0.1 9999 -- 开始要选择一个 slave 当选新 master 时.
- 3010:X 21 Nov 23:01:12.792 # +selected-slave slave 127.0.0.1:9998 127.0.0.1 9998 @ mymaster 127.0.0.1 9999 -- 找到了 port:9998 一个适合的 slave 来担当新 master
-- 当把选择为新 master 的 slave 的身份进行切换的时候.
3010:X 21 Nov 23:01:12.792 * +failover-state-send-slaveof-noone slave 127.0.0.1:9998 127.0.0.1 9998 @ mymaster 127.0.0.1 9999
-- 等待提升 9998 为新的 master
- 3010:X 21 Nov 23:01:12.851 * +failover-state-wait-promotion slave 127.0.0.1:9998 127.0.0.1 9998 @ mymaster 127.0.0.1 9999
- 3010:X 21 Nov 23:01:13.699 # +promoted-slave slave 127.0.0.1:9998 127.0.0.1 9998 @ mymaster 127.0.0.1 9999 -- 提升 9998 master
- 3010:X 21 Nov 23:01:13.699 # +failover-state-reconf-slaves master mymaster 127.0.0.1 9999 -- Failover 状态变为 reconf-slaves 状态时
3010:X 21 Nov 23:01:13.749 * +slave-reconf-sent slave 127.0.0.1:9997 127.0.0.1 9997 @ mymaster 127.0.0.1 9999 -- 重新配置 9997 为 slave
3010:X 21 Nov 23:01:14.770 # -odown master mymaster 127.0.0.1 9999 -- 离开 ODOWN 状态时.
-- inprog 9997 slave 被重新配置为 9998 的 master 的 slave, 但数据复制还未发生时.
3010:X 21 Nov 23:01:14.770 * +slave-reconf-inprog slave 127.0.0.1:9997 127.0.0.1 9997 @ mymaster 127.0.0.1 9999
-- done 9997 slave 被重新配置为 9998 的 master 的 slave, 并且数据复制已经与 master 同步时.
- 3010:X 21 Nov 23:01:14.770 * +slave-reconf-done slave 127.0.0.1:9997 127.0.0.1 9997 @ mymaster 127.0.0.1 9999
- 3010:X 21 Nov 23:01:14.841 # +failover-end master mymaster 127.0.0.1 9999 -- 故障转移结束
- 3010:X 21 Nov 23:01:14.841 # +switch-master mymaster 127.0.0.1 9999 127.0.0.1 9998 -- master 由 9999 替换为 9998
3010:X 21 Nov 23:01:14.841 * +slave slave 127.0.0.1:9997 127.0.0.1 9997 @ mymaster 127.0.0.1 9998 -- 检测到 9997slave 并添加进 slave 列表时
3010:X 21 Nov 23:01:14.842 * +slave slave 127.0.0.1:9999 127.0.0.1 9999 @ mymaster 127.0.0.1 9998 -- 检测到 9999slave
- 3010:X 21 Nov 23:01:44.849 # +sdown slave 127.0.0.1:9999 127.0.0.1 9999 @ mymaster 127.0.0.1 9998 -- 进入 SDOWN 状态时;
- 3010:X 21 Nov 23:04:19.457 # -sdown slave 127.0.0.1:9999 127.0.0.1 9999 @ mymaster 127.0.0.1 9998 -- 离开 SDOWN 状态时
来源: https://www.cnblogs.com/lihonglin2016/p/10037808.html