点击上方蓝色字体,选择“设为星标”
回复”资源“获取更多资源 大数据技术与架构 点击右侧关注,大数据开发领域最强公众号! 暴走大数据 点击右侧关注,暴走大数据!由于单机Redis存储能力受单机限制,以及无法实现读写操作的负载均衡和读写分离,无法保证高可用。本篇就来介绍 Redis 集群搭建方案及实现原理,实现Redis对数据的冗余备份,从而保证数据和服务的高可用。主从复制是哨兵和集群的基石,因此我们循序渐进,由浅入深一层层的将Redis高可用方案抽丝剥茧展示在大家面前。
主从复制
介绍
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器,主从是哨兵和集群模式能够实施的基础。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有零个或多个从节点(0+个从节点),但一个从节点只能有一个主节点。一般主节点负责接收写请求,从节点负责接收读请求,从而实现读写分离。
主从一般部署在不同机器上,复制时存在网络延时问题,使用参数repl-disable-tcp-nodelay选择是否关闭TCP_NODELAY,默认为关闭:
关闭:无论数据大小都会及时同步到从节点,占带宽,适用于主从网络好的场景;
开启:主节点每隔指定时间合并数据为TCP包节省带宽,默认为40毫秒同步一次,适用于网络环境复杂或带宽紧张,如跨机房;
数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
读写分离:主库写、从库读,读写分离不仅可以提高服务器的负载能力,同时可根据需求的变化,改变从库的数量;
高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础。
#在从服务器执行下面的命令成为或取消成为某节点的从节点
#slaveof 主服务器的IP 端口号
slaveof host port
#取消成为任何服务器的从服务器
slaveof no one
#从服务器只读(推荐配置)
config set slave-read-only yes
#查看主从信息
info replication
#配置主节点ACL账号密码(Redis6开启ACL的情况)
config set masteruser username
config set masterauth password
slaveof
命令是异步的,不会阻塞。 同时,从服务器现有的数据会先被清空,然后才会同步主服务器的数据。
#在从节点配置文件中新增下面两个配置即可指定成为某个主节点的从节点
#slaveof 主节点地址 主节点端口
slaveof host port
#从服务器只读(推荐配置)
slave-read-only yes
#命令行模式
#在从节点配置主节点ACL账号密码(Redis6开启ACL的情况)
config set masteruser default
config set masterauth wyk123456
#在从节点查看主节点的ACL用户密码
config get master*
#配置文件模式 redis.conf
#在从节点配置主节点ACL账号密码(Redis6开启ACL的情况)
masteruser default
masterauth wyk123456
保存主节点信息:配置slaveof之后会在从节点保存主节点的信息。
主从建立socket连接:定时发现主节点以及尝试建立连接。
发送ping命令:从节点定时发送ping给主节点,主节点返回PONG。若主节点没有返回PONG或因阻塞无法响应导致超时,则主从断开,在下次定时任务时会从新ping主节点。
权限验证:若主节点开启了ACL或配置了requirepass参数,则从节点需要配置masteruser和masterauth参数才能保证主从正常连接。
同步数据集:首次连接,全量同步。
命令持续复制:全量同步完成后,保持增量同步。
监控:监控主从节点运行情况。
通知:当监控节点出现故障,哨兵之间进行通讯。
自动故障转移:当监控到主节点宕机后,断开与宕机主节点连接的所有从节点,然后在从节点中选取一个作为主节点,将其他的从节点连接到这个最新的主节点。最后通知客户端最新的服务器地址。
#端口
port 26379
#后台启动
daemonize yes
#运行时PID文件
pidfile /var/run/redis-sentinel.pid
#日志文件(绝对路径)
logfile "/opt/app/redis6/sentinel.log"
#数据目录
dir /tmp/sentinel_26379
#监控的节点名字可以自定义,后边的2代表的:如果有俩个哨兵判断这个主节点挂了那这个主节点就挂了,通常设置为哨兵个数一半加一
sentinel monitor mymaster 127.0.0.1 6379 2
#哨兵连接主节点多长时间没有响应就代表主节点挂了,单位毫秒。默认30000毫秒,30秒。
sentinel down-after-milliseconds mymaster 30000
#在故障转移时,最多有多少从节点对新的主节点进行同步。这个值越小完成故障转移的时间就越长,这个值越大就意味着越多的从节点因为同步数据而暂时阻塞不可用
sentinel parallel-syncs mymaster 1
#在进行同步的过程中,多长时间完成算有效,单位是毫秒,默认值是180000毫秒,3分钟。
sentinel failover-timeout mymaster 180000
#禁止使用SENTINEL SET设置notification-script和client-reconfig-script
sentinel deny-scripts-reconfig yes
Redis服务:localhost:6381,localhost:6382,localhost:6383
sentinel服务:localhost:26381,localhost:26382,localhost:26383
6381为Redis初始主节点,6382,6383分别为6381的从节点。
26381,26382,26383作为三个哨兵服务监控上面的Redis主从架构。
配置启动三个Redis服务以及Sentinel 服务:
1.首先复制Redis目录出三个:
cp -r /opt/app/redis6 /opt/app/redis6A
cp -r /opt/app/redis6 /opt/app/redis6B
cp -r /opt/app/redis6 /opt/app/redis6C
2.分别修改A,B,C三个目录中的redis.conf和sentinel.conf文件,主要修改端口和文件路径,下面以A为演示,B,C略过:
vim redis.conf
--------------------------------------------
port 6381
daemonize yes
pidfile "/var/run/redisA_6381.pid"
logfile "/opt/app/redis6A/redis_6381.log" #需要手动touch文件
dir "/opt/app/redis6A/data" #需要手动先mkdir文件夹
--------------------------------------------
vim sentinel.conf
--------------------------------------------
port 26381
daemonize yes
pidfile /var/run/redis-sentinel_26381.pid
logfile "/opt/app/redis6A/sentinel_26381.log" #需要手动先touch文件
dir /tmp/sentinel_26381 #需要手动先mkdir文件夹
sentinel monitor mymaster 127.0.0.1 6381 2 #此参数在ABC三个服务中保持一致,都监听6381端口
--------------------------------------------
创建log文件和目录:
mkdir /opt/app/redis6A/data
mkdir /opt/app/redis6B/data
mkdir /opt/app/redis6C/data
touch /opt/app/redis6A/redis_6381.log
touch /opt/app/redis6B/redis_6382.log
touch /opt/app/redis6C/redis_6383.log
mkdir /tmp/sentinel_26381
mkdir /tmp/sentinel_26382
mkdir /tmp/sentinel_26383
touch /opt/app/redis6A/sentinel_26381.log
touch /opt/app/redis6B/sentinel_26382.log
touch /opt/app/redis6C/sentinel_26383.log
3.配置完成后,分别启动Redis三个服务以及Sentinel三个服务:
#启动Redis
/opt/app/redis6A/bin/redis-server /opt/app/redis6A/bin/redis.conf
/opt/app/redis6B/bin/redis-server /opt/app/redis6B/bin/redis.conf
/opt/app/redis6C/bin/redis-server /opt/app/redis6C/bin/redis.conf
#配置Redis主从,6381为主,6382和6383为从节点
#最后启动Sentinel
/opt/app/redis6A/bin/redis-sentinel /opt/app/redis6A/bin/sentinel.conf
/opt/app/redis6B/bin/redis-sentinel /opt/app/redis6B/bin/sentinel.conf
/opt/app/redis6C/bin/redis-sentinel /opt/app/redis6C/bin/sentinel.conf
使用redis-cli客户端命令行进入6381,6382,6383的Redis服务,然后配置6382和6383作为6381的从节点: 启动哨兵服务: 此时我们在redis客户端中使用debug命令模拟主节点崩溃的情况,然后看是否会选举6382和6383提升为主节点,以及6381恢复启动后是什么角色:
#命令执行一个非法的内存访问从而让 Redis 崩溃,仅在开发时用于 BUG 调试,执行后需要重启服务
debug segfault
然后我们查看哨兵的日志: vim /opt/app/redis6A/sentinel_26381.log 重启6381的redis服务后查看,哨兵已经自动将6381节点作为6382新主节点的从节点: 原理 哨兵之间会有通讯,哨兵和主从节点之间也有监控,基于这些信息同步和状态监控实现Redis的故障转移:
哨兵和哨兵之间以及哨兵和Redis主从节点之间每隔一秒发送ping监控它们的健康状态;
哨兵向Redis主从节点每隔10秒发送一次info保存节点信息;
哨兵向Redis主节点每隔2秒发送一次hello,直到哨兵报出sdown,代表主节点失联,然后通知其余哨兵尝试连接该主节点;
哨兵A发现Redis主节点失联;
哨兵A报出sdown,并通知其他哨兵,发送指令sentinel is-master-down-by-address-port给其余哨兵节点;
其余哨兵接收到哨兵A的指令后尝试连接Redis主节点,发现主节点确实失联;
哨兵返回信息给哨兵A,当超过半数的哨兵认为主节点下线后,状态会变成odown;
最先发现主节点下线的哨兵A会成为哨兵领导者负责这次的主从节点的切换工作;
哨兵Leader 根据一定规则从各个从节点中选择出一个节点升级为主节点;
其余从节点修改对应的主节点为新的主节点;
当原主节点恢复启动的时候,变为新的主节点的从节点
#Java
JedisSentinelPool
#Python
from redis.sentinel import SentinelConnectionPool
集群 介绍 Redis集群(Redis Cluster)是从 Redis 3.0 开始引入的分布式存储方案。集群由多个节点(Node)组成,Redis 的数据分布在这些节点中。 集群中的节点分为主节点和从节点,只有主节点负责读写请求和集群信息的维护,从节点只进行主节点数据和状态信息的复制。 作用 Redis集群的作用有下面几点:
数据分区:突破单机的存储限制,将数据分散到多个不同的节点存储;
负载均衡:每个主节点都可以处理读写请求,提高了并发能力;
高可用:集群有着和哨兵模式类似的故障转移能力,提升集群的稳定性;
普通端口:即客户端访问端口,如默认的6379;
集群端口:普通端口号加10000,如6379的集群端口为16379,用于集群节点之间的通讯;
MEET:在节点握手阶段,对新加入的节点发送meet消息,请求新节点加入当前集群,新节点收到消息会回复PONG消息;
PING:节点之间互相发送ping消息,收到消息的会回复pong消息。ping消息内容包含本节点和其他节点的状态信息,以此达到状态同步;
PONG:pong消息包含自身的状态数据,在接收到ping或meet消息时会回复pong消息,也会主动向集群广播pong消息;
FAIL:当一个主节点判断另一个主节点进入fail状态时,会向集群广播这个消息,接收到的节点会保存该消息并对该fail节点做状态判断;
PUBLISH:当节点收到publish命令时,会先执行命令,然后向集群广播publish消息,接收到消息的节点也会执行publish命令;
#使用-c进入集群命令行模式
redis-cli -c -p 6381
#使用命令查看key所在的槽
cluster keyslot key1
Smart客户端相比于dummy客户端,smart客户端在初始化连接集群时就缓存了槽slot和节点node的对应关系, 也就是在连接任意节点后执行cluster slots,我们使用的JedisCluster就是smart客户端:
cluster slots
集群代理:Redis6版本中新增的特性,客户端不需要知道集群中的具体节点个数和主从身份,可以直接通过代理访问集群。与Redis在不同的分支,将在后面的文章中具体介绍。
1.首先复制Redis目录出三个:
cp -r /opt/app/redis6A /opt/app/redis6AA
cp -r /opt/app/redis6B /opt/app/redis6BB
cp -r /opt/app/redis6C /opt/app/redis6CC
2.分别修改6个目录中的redis.conf文件,主要开启集群以及修改端口和文件路径,下面以A为演示,其余略过:
vim /opt/app/redis6A/bin/redis.conf
--------------------------------------------
port 6381
daemonize yes
pidfile "/var/run/redisA_6381.pid"
logfile "/opt/app/redis6A/redis_6381.log" #需要手动touch文件
dir "/opt/app/redis6A/data" #需要手动先mkdir文件夹
cluster-enabled yes # 启用集群模式
cluster-node-timeout 15000 # 设置当前节点连接超时毫秒数
cluster-config-file node_6381.conf #设置当前节点集群配置文件路径
--------------------------------------------
3.在6个目录下分别创建log文件和目录:
mkdir /opt/app/redis6A/data
touch /opt/app/redis6A/redis_6381.log
cluster-config-file:每个节点在运行过程中,会维护一份集群配置文件。 当集群信息发生变化时(如增减节点),集群内所有节点会将最新信息更新到该配置文件。 节点重启后,会重新读取该配置文件,获取集群信息,可以方便的重新加入到集群中。 也就是说,当 Redis 节点以集群模式启动时,会首先寻找是否有集群配置文件。 如果有则使用文件中的配置启动;如果没有,则初始化配置并将配置保存到文件中。 集群配置文件由 Redis 节点维护,不需要人工修改。 启动部署:部署集群需要 先启动各个节点的服务,此时这些节点都没加到集群中,使用 redis-cli --cluster create xxx命令创建集群:
bin/redis-cli --cluster create 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6391 127.0.0.1:6392 127.0.0.1:6393 --cluster-replicas 1
#这里的--cluster-replicas表示每个主节点有几个副本节点
redis-cli --cluster 代替了之前的 redis-trib.rb ,我们无需安装ruby环境即可直接使用它附带的所有功能:创建集群、增删节点、槽迁移、完整性检查、数据重平衡等等。 集群限制 由于Redis集群中数据分布在不同的节点上,因此有些功能会受限: db库:单机的Redis默认有16个db数据库,但在集群模式下只有一个db0; 复制结构:上面的复制结构有树状结构,但在集群模式下只允许单层复制结构; 事务/lua脚本:仅允许操作的key在同一个节点上才可以在集群下使用事务或lua脚本;(使用Hash Tag可以解决) key的批量操作:如mget,mset操作,只有当操作的key都在同一个节点上才可以执行;(使用Hash Tag可以解决) keys/flushall:只会在该节点之上进行操作,不会对集群的其他节点进行操作; Hash Tag: 上面介绍集群限制的时候,由于key被分布在不同的节点之上,因此无法跨节点做事务或lua脚本操作,但我们可以使用hash tag方式解决。 hash tag:当key包含{}的时候,不会对整个key做hash,只会对{}包含的部分做hash然后分配槽slot;因此我们可以让不同的key在同一个槽内,这样就可以解决key的批量操作和事务及lua脚本的限制了; 但由于hash tag会将不同的key分配在相同的slot中,如果使用不当,会造成数据分布不均的情况,需要注意。 集群参数优化 cluster_node_timeout:默认值为15s。 影响ping消息接收节点的选择,值越大对延迟容忍度越高,选择的接收节点就越少,可以降低带宽,但会影响收敛速度。应该根据带宽情况和实际要求具体调整。 影响故障转移的判定,值越大越不容易误判,但完成转移所消耗的时间就越长。应根据网络情况和实际要求具体调整。 cluster-require-full-coverage 为了保证集群的完整性,只有当16384个槽slot全部分配完毕,集群才可以上线,但同时,若主节点发生故障且故障转移还未完成时,原主节点的槽不在任何节点中,集群会处于下线状态,影响客户端的使用。 该参数可以改变此设定: no: 表示当槽没有完全分配时,集群仍然可以上线; yes: 默认配置,只有槽完全分配,集群才可以上线。 欢迎点赞+收藏+转发朋友圈素质三连
文章不错?点个【在看】吧! ?