redis整理03——redis数据持久化、主从模式、主从模式+哨兵机制以及分片集群

2024-09-16

说明

linux(contos)、redis7.0+、.net core6.0+、docker

记录一下redis数据持久化、主从同步、哨兵机制、分片集群相关内容,使用docker部署

为了方便,本文是在同一台机器上演示的集群,正常情况下要在不同的机器部署节点,真别傻乎乎的在同一个机器部署集群,集群性能没有任何提升

单点redis的问题

单点redis实例可处理每秒数万到数十万的请求,一些小项目已经足够。但是如果业务比较复杂——并发更高、数据量更大,单点redis可能会出现以下问题:

  • 数据丢失——修改RDB、AOF的存储配置
  • 并发能力不足——搭建主从集群,实现读写分离。master主节点写数据、从节点读取数据
  • 故障恢复——添加哨兵机制,健康检测,故障恢复
  • 存储能力——搭建分片集群,每个分片存储不同的数据

redis持久化

redis持久化数据可分为RDBAOF两种方式,不指定任何配置时默认使用RDB持久化的方式,两者各有各的优势,也可以结合两者使用。

redis容器内部没有配置文件,需要自己映射或者把配置内容写道docker run命令,建议使用配置文件启动

redis配置文件

redis7.0的配置文件详解——redis-7.0.conf,默认的配置文件是开启RDB持久化方式,禁用AOF持久化方式

image-20240818210525734

redis重点关注配置

redis7.0配置文件详解,去掉注释了有需要请查看官方原版文件,更多配置文件请查看:Redis conf

RDB

RDB:Redis Database,定时对整个内存做快照,宕机时恢复数据快、迁移方便、自动压缩文件体积小但是丢失数据较多(两次备份期间未持久化的数据将全部丢失),做快照时占用大量CPU和内存资源

准备RDB配置文件

使用RDB持久化配置运行redis

image-20240817172426180

由以下的redis日志可以看出,启用rdb持久化时,正常退出会保存rdb文件(配置文件dbfilename),启动redis会加载rdb文件

image-20240817173155061

docker stop、docker restart 命令都会保存一次rdb文件、docker kill、docker pause 命令或突然关机、宕机时不会保存rdb文件,这就会导致数据丢失

image-20240817174323024

除此之外,我们还可以使用SAVE、BGSAVE这两个命令主动保存rdb文件、key的变更频率达到3600 1 300 100 60 10000也会触发保存文件

image-20240817181638252

总结:启用rdb持久化时,正常退出会保存rdb文件、启动时会加载rdb文件。所以有了rdb文件,迁移数据是很简单的。但是rdb文件的保存是不固定的,所以宕机时可能会丢失部分数据。我们可以修改save配置提高保存rdb文件的频率,但是不建议这样做。因为每次都是全量保存(先做快照再替换旧rdb文件),需要占用大量cpu和内存资源

AOF

AOF:Append Only File,记录每一次执行的redis命令,宕机恢复数据较慢(恢复数据时要重新执行这些命令)、文件体积较大但是数据相对完整可做到最多只会丢失一秒的数据(推荐使用everysec配置),主要消耗磁盘IO资源,不会大量占用cpu和内存资源(不过AOF重写还是会占用消耗cpu和内存资源)

准备AOF配置文件

使用AOF持久化配置运行redis

image-20240817192341758

AOF 日志文件同步频率参数取值有三个选项:everysec、always、no,推荐使用everysec。

  • always,每次执行写操作都将命令写入磁盘(1秒有十次写操作,则写入十次)。这是最安全的选项,性能最差(IO磁盘写入)

  • everysec,每秒将数据写入磁盘(1秒有十次写操作,则写入一次)。这是较为安全的选项,推荐使用,降低性能(IO磁盘写入)

  • no,不将数据写入磁盘,最不安全的选项,性能最高

    image-20240818204359382

不开启RDB,仍然可以通过SAVE、BGSAVE命令保存RDB文件,重启容器,我们在日志可以看到,退出容器前会再次(写入)同步AOF文件,启动容器时也会加载AOF文件

AOF重写

随着数据量越来越多,AOF文件不断增大,可以通过以下配置控制 AOF 重写,将 AOF 文件中的冗余数据和不必要的记录删除,从而减小 AOF 文件的大小,提高读写性能

  • no-appendfsync-on-rewrite 是否禁用在 AOF 重写过程中对磁盘的同步操作。为yes的话,重写时Redis 将不会将数据立即写入磁盘,而是将数据缓存在内存中,可以提高AOF重写的速度,但是也会有数据丢失的风险
  • auto-aof-rewrite-percentage 当 AOF 文件的大小增长到这个百分比时,Redis 会自动启动 AOF 重写过程。
  • auto-aof-rewrite-min-size: 当 AOF 文件的大小超过这个字节数时,Redis 会自动启动 AOF 重写过程

总结:启用AOF持久化时,会将我们执行的每一条命令按一定的频率写入文件中,推荐使用everysec配置。随着数据增大,AOF文件不断增大,大到一定程度会对AOF文件进行重写,缩小文件大小从而提高读写性能。虽然有重写,但AOF文件体积也要远远大于RDB文件。不过写入AOF文件仅占用IO磁盘资源,而且还可以控制数据丢失范围在一秒以内,相对于RDB持久化方式AOF持久化方式占用cpu内存资源小,数据更安全

主从模式—Replica

接下来我们来处理单点redis读并发能力不足的问题。举个例子,假如一台redis实例A能抗下每秒1万次读并发请求,那并发量提高一倍,我们可不可以再增加一台redis实例B,处理另外一万次读并发请求呢?答案肯定是可以的,不可以的话就不会问的那么刻意了...那应该怎么实现呢?

主从模式图解

主从模式,自动实现读写分离

image-20240824152839293

数据同步原理,参考:高级篇-分布式缓存-08-Redis主从-主从的全量同步原理哔哩哔哩bilibili

image-20240824151253438

配置文件redis.conf

公网服务器还需要设置安全组开放指定端口,谨慎操作

image-20240820232212867

7001——主节点

image-20240820233036608

7002——从节点

image-20240820234349981

7003——从节点

image-20240820234604950

主从演示

主节点可读写,从节点仅可读,所以我们可以把读的请求分给从节点处理

image-20240821000258602

c#代码演示

以下演示一下c#代码如何连接并使用主从模式,源码参考:redis 兼容主从复制 · logerlink/RedisTest@043a31e (github.com)

redis主从模式连接字符串

redis 帮助类改造——CommandFlags.PreferReplica优先从节点读取数据

具体使用

如下图,

所有节点存活时,读取缓存均成功——主写从读

仅关闭主节点后或者仅关闭主节点和从节点1后,读取缓存成功,写缓存失败——主写失败,从读成功

关闭所有节点(一主两从),读写缓存均失败

仅启动主节点时,读取缓存均成功——主写主读,又变成"单实例redis"了

image-20240824113440888

故障演示

从节点脱离主节点后,主节点Id会发生改变。若从节点再次链接主节点,则会进行全量同步主节点的数据,保证数据统一

image-20240821231601371

从节点异常断开连接,正常启动后,由于节点Id没有改变,所以会自动根据偏移量同步主节点的数据,保证数据统一

image-20240822001806958

超过半数的节点(此处两个从节点)断开连接后,主节点仍可用(可读写)。若此时继续处理高并发,全部节点断开连接,那肯定就不可用了

image-20240822234421566

主节点宕机断开连接,从节点不影响读取数据,但是无法设置缓存(因为他不是主节点),那我们得想办法让其中一台正常的机器升级为主节点.

image-20240823000625637

若原有集群含有大量数据,此时新增一个从节点,这个从节点大概多久能进行工作呢?从节点要完全复制主节点的数据才能开始工作

一键部署主从模式

测试环境下,单个机器部署多个redis节点,一键部署主从模式,方便测试。将以下命令写进replica-start.sh文件,并执行即可——replica-start.sh

哨兵机制—Sentinel

上面的主从模式虽然可以实现读写分离,但是仍有个隐患——主节点宕机之后,整个集群无法写数据。那有没有办法让主节点"自动复活"之类的操作呢?答案肯定也是有的。

我们可以安排一个小工头去监控整个集群工作情况,若主节点无法正常工作,则让另一个节点代替主节点工作,保证读写正常。当然了,一个小工头能力也有限,如果小工头也宕机罢工不干了,那就没人来监控了,所以我们也可以添加多个小工头一起来进行监控整个集群工作情况

哨兵机制图解

image-20240908193544925

哨兵三大功能

Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障转移,主要起到监控、自动故障转移、通知三个功能

监控:哨兵会不断检查主节点和从节点是否正常工作。

心跳机制,每秒发送ping命令,若节点不回复,则认为该实例主观下线(sdown)。若超过指定数量(quorum)的哨兵都认为该实例主观下线,则认为该实例客观下线(odown,已确认宕机)

自动故障转移:若主节点宕机,哨兵会自动选择一个从节点升级为主节点,前一个主节点恢复后会变成从节点

选择从节点的选举依据

  1. 首先判断从节点与主节点断开的时间长短,若超过指定值(down-after-milliseconds * 10),则直接排除该从节点
  2. 第二步判断节点的slave-priority值,越小优先级越高,0表示不参加选举
  3. 第三步判断从节点的offset,越大说明数据越新,优先级越高(最重要)
  4. 最后判断从节点的运行Id(启动时自动生成的),越小优先级越高

如何实现故障转移?

哨兵选中一个从节点,并发送slaveof NO ONE命令给该节点执行,让其成为主节点

哨兵会给剩下的从节点发送slaveof 从IP 从端口命令,让其成为新主节点的从节点,并开始同步master数据

最后,哨兵会将故障节点标记为从节点,加入slaveof 从IP 从端口命令到配置文件,等故障节点恢复后自动成为主节点的从节点

通知:集群发生故障时,Redis哨兵通过使用pub/sub机制向客户端发送切换通知,客户端通过订阅哨兵的消息来接收通知并重新连接到新的主节点,实现了高可用性和可靠性的Redis部署

配置文件sentinel.conf

我们只需要在原有的主从模式下添加哨兵集群即可,注意是使用redis-sentinel xx.conf启动哨兵而且哨兵集群无需主从复制,以下是通用配置文件

s1-7005

image-20240825151808267

s2-7006

image-20240825151756838

s3-7007

image-20240825152128588

哨兵及故障演示

分别成功启动主从模式和哨兵机制,模拟主节点宕机,我们查看哨兵leader的日志可以看到故障转移全过程

  1. +monitor ...哨兵启动,监控原主节点7001
  2. +slave slave ...哨兵自动连接上7001下的从节点。有个问题需要注意,docker环境下会自动连接6379端口作为从节点,忽略即可
  3. +sentinel sentinel ...识别两个哨兵,组成哨兵集群,用于一起监控主节点是否下线。
  4. +sdown ...主节点7001主观下线。
  5. +odown ...若超过quorum个哨兵都认为7001主观下线(sdown),则视为客观下线(odown)
  6. +try failover ...尝试对7001进行故障转移
  7. +vote-for-leader ...哨兵集群投票选举哨兵leader,来负责本次故障转移操作
  8. +elected-leader ...哨兵leader赢得选取
  9. +failover-state-select-slave ...哨兵leader开始寻找可升级的从节点
  10. +selected-slave ...leader选择7003作为新的主节点
  11. +failover-state-send-slaveof-noone slave ...哨兵leader向7003节点发送slaveof no one 脱离主从,成为单实例(master)
  12. +failover-state-wait-promotion ...等待其他哨兵确认7003新主节点
  13. +promoted-slave ...其他哨兵更新哨兵配置文件,确认新主节点成功
  14. +failover-state-reconf-slaves ...故障转移状态切换到了 reconf-slaves 状态
  15. +slave-reconf-sent ...哨兵leader向其他从节点发送slaveof命令让其跟随新的主节点
  16. +failover-end-for-timeout ...故障转移因超时而终止,超时时间可调整failover-timeout配置 。虽然超时对结果不影响,但是超时期间,从节点无法拿到新主节点的新数据,待超时后从节点才会从新主节点上同步数据
  17. +failover-end ...故障转移结束
  18. +slave-reconf-sent-be ...哨兵leader发送slaveof命令成功
  19. +switch-master ...哨兵选择新的主节点,并监控新主节点
  20. +slave slave哨兵自动连接新主节点下的所有从节点,包括故障节点
  21. -failover-abort-no-good-slave ... 若出现这个指令,说明当前没有从节点可升级为主节点,终止故障转移。这时我们需要看一下主节点是否正常,可以参考故障转移不成功的例子

image-20240904163625992

image-20240904183059724

查看redis7002、7003节点日志,从节点7003已经成功升级为主节点,从节点7002也跟着成为7003的从节点,并开始同步主节点数据

image-20240905161021438

此时我们再去看redis主从节点和哨兵的配置文件,发现已经被修改

image-20240904184828859

查看新主节点7003的info信息,7003已升级为主节点

image-20240905162808629

但是故障主节点7001的配置文件未被更改,如果故障节点7001修复后重启,还是主节点吗?

image-20240904185729042

重启后我们查看哨兵日志和7001节点日志,发现7001直接变成7003的从节点,而且7001的配置文件也被更改了

虽然故障转移成功后7001的配置文件未被更改,但是故障转移期间哨兵已经将故障节点标记为从节点,所以故障节点修复后重启会变成从节点并从新的主节点同步数据

image-20240904191416509

一主两从三哨兵的模式下,主节点宕机后,新主节点也宕机,此时仍旧支持故障转移并访问,若所有的redis节点都宕机,哨兵无法找到可用的节点故障转移失败,过段时间(failover-timeout)继续尝试故障转移,直到成功为止

image-20240905165042011

一主两从三哨兵的模式下,从节点宕机主节点正常的情况下 ,哨兵不会进行故障转移

image-20240905165933781

我们也可以手动触发故障转移,手动触发一般不会出现超时情况,因为此时7001仍可用

image-20240905175338366

更多哨兵常用命令
c#代码演示

以下演示一下c#代码如何连接并使用主从模式+哨兵机制,源码参考:使用哨兵 · logerlink/RedisTest@6d1589f (github.com)

redis主从模式+哨兵机制连接字符串,只需加上serviceName即可,注意需要redis主从的密码,而且连接的是哨兵集群地址

redis 帮助类改造——CommandFlags.PreferReplica优先从节点读取数据

具体使用

程序执行如下图:

  1. 主从模式和哨兵均正常,读写缓存成功
  2. 模拟主节点宕机的同时,立即执行程序,设置缓存失败,读取正常。原因:故障转移需要时间未完成,主节点无法工作,导致设置缓存失败
  3. 模拟主节点宕机,故障转移成功,7002升级为新的主节点,读写缓存成功。主从切换成功,会修改配置文件,此时会触发 IConnectionMultiplexer.ConfigurationChanged 事件
  4. 分别模拟三个哨兵宕机,对程序不影响,读写缓存成功。注意,若启动程序前,哨兵全部宕机,此时是无法连接redis节点的
  5. 模拟新主节点7002宕机,由于哨兵不可用,无法自动实现故障转移,主节点不可用,设置缓存失败,但从节点仍可读取

image-20240908192537750

常见问题

故障转移不一定会成功,如果主节点发生故障,哨兵没有找到合适的从节点可升级为主节点,此时会终止故障转移,并等待一段时间后(failover-timeout 默认6分钟)继续重试升级从节点为主节点的操作

image-20240825164100790

故障转移不成功,原因排查——主从模式是否设置密码?若有密码则加上sentinel auth-pass <password>并保证密码正确

故障转移不成功,原因排查——docker环境下,哨兵未找到主从模式的从节点,如下我们推演一下执行过程

  1. 使用docker,正常启动主从模式后添加两个哨兵
  2. 模拟主节点7001宕机,此时哨兵应该自动选取新的主节点,但是没有成功,哨兵选取新的主节点失败
  3. 我们查看哨兵日志,发现主节点7001主观下线且客观下线(满足quorum)
  4. 接着哨兵尝试投票选取主节点,但是又选中了主节点7001故障节点
  5. 紧接着哨兵终止故障转移,原因是没有找到可用的节点。那为什么没有找到可用节点呢?明明还有两个正常的从节点
  6. 继续往上查看更久远的日志,发现哨兵启动后,监控主节点(+monitor master...),并连接两个从节点(+slave slave ...)但是连接从节点失败(+sdown slave ...)
  7. 定位到原因:原来连接从节点失败且主节点故障,导致哨兵自动故障转移操作被终止,无法完成主从切换。

image-20240827184610125

那为什么哨兵连接从节点失败呢?我们先看一下监控主节点后的日志——哎,这两个从节点,为什么端口都是6379呢?我们设定的端口不是7001主节点,7002、7003为从节点吗?

image-20240829161219185

我们再看一下哨兵s7007的配置文件,查看哨兵自动生成的配置,我们发现自动生成连接从节点命令sentinel known-replica masterName 从ip 从端口,但是端口不正确,所以导致哨兵连接从节点失败

image-20240829171052072

出现这个问题的原因:哨兵监控主节点,并通过info命令获取到从节点信息(ip:port),从而自动识别从节点。但是在docker环境下执行info命令拿到的端口是docker内部的端口而不是外部映射的端口,如图

image-20240905171327204

我们只需要把两个从节点真正的地址追加到哨兵配置文件即可

image-20240829230142588

到此,哨兵可自动完成切换,哨兵切换的同时也会修改哨兵的配置文件,如下,监听主节点和连接从节点的地址都发生改变 image-20240829230635157

当然,为了方便,在使用docker搭建哨兵模式时,也可以直接把从节点写到我们的配置文件

哨兵故障转移后,redis节点日志出现权限不足无法修改配置文件的情况

# Could not create tmp config file (Permission denied)      # CONFIG REWRITE failed: Permission denied

image-20240827171927731

这种情况一般是docker挂载直接映射配置文件导致的,我们修改成映射配置文件所在的目录并给该目录添加写权限即可

image-20240904154920599

故障转移后更新配置文件成功如下

image-20240904155455918

一键启动哨兵

测试环境下,单个机器部署多个哨兵节点,一键部署,方便测试。将以下命令写进sentinel-start.sh文件,并执行即可——sentinel-start.sh

一键关闭哨兵

测试环境下,单个机器部署多个哨兵节点,一键关闭主从+哨兵,方便测试。将以下命令写进stop-all.sh文件,并执行即可——stop-all.sh

分片集群—Cluster

主从模式+哨兵机制可以很好的帮助我们提高redis并发能力和故障恢复,实现高并发高可用的效果。但还有一个问题,随着业务量增大,数据会越来越大。由于主从模式是通过复制来实现数据一致的,相当于三台机器保存同一份大文件(如总数据大小100G,使用主从复制,A、B、C三台机器均保存100G文件)这样才能在不同的机器读取到相同数据。显而易见,这种方式数据冗余比较多,而且三台机器都要使用更大的存储空间和内存来保存数据。而且主从模式+哨兵,很难扩容,节点越多越复杂

我们可不可以实现三台机器各自保存一部分数据呢?(如总数据大小100G,A机器保存40G,B、C机器各保存30G)而且还要支持在不同的机器读取到相同数据。当然可以,redis分片集群支持分片保存数据。

分片集群图解

redis分片集群可用来解决海量数据存储和高并发写问题。分片集群有以下特点

  • 一个分片集群有多个master节点,每个master节点存储不同的数据
  • 每个master都可以有多个slave节点,用于故障转移主从切换
  • 不再需要哨兵监控,每个master通过ping检测彼此健康状态

Redis默认使用虚拟槽分区,Redis会预先给每个主节点分配16384个插槽(slot),每个节点负责一定区间的slot(如 A:0-5460,B:5461-10922,C: 10923-16383)。存储和读取缓存时,自动对 key 使用 CRC16 算法计算再对16384取余,得到一个slot的值,并根据slot值寻找并路由到相应节点,然后在该节点上存储或读取数据。当有新的节点加入或者移除的时候,通过迁移槽以及其对应的数据,可以很方便的进行动态扩容或缩容

注意分片集群不是只能存储16384个key,slot值仅仅是用来确认操作节点。不同的key计算出相同的slot不会互相覆盖

image-20240909180122928

配置文件cluster.conf
一键部署关闭集群

测试环境下,单个机器部署多个redis节点,一键部署主从模式,方便测试。将以下命令写进replica-start.sh文件,并执行即可——cluster-start.sh

stop-cluster.sh

创建集群

创建集群之前,防火墙先开放端口:节点端口7001-7010,总线端口17001-17010。

公网服务器还需要设置安全组开放指定端口,谨慎操作

image-20240910160856066

部署并启动6个节点后,还需要手动执行相应命令创建集群,将所有节点关联起来

image-20240910171349651

注意,Redis5.0之后,集群管理操作已经集成到了redis-cli中,我们只需要redis-cli命令即可管理集群。Redis5.0之前,需安装redis-trib.rb才能管理集群

分片集群演示

演示之前我们先了解一下插槽,Redis会预先分配16384个插槽给每个主节点,每个主节点负责一定区间的slot。redis会根据key的有效部分自动计算插槽值,再找到对应的节点进行读写数据。意味着我们无需刻意知道数据与哪个插槽绑定,更不需要知道数据在哪个节点上

redis数据key不是和节点绑定,而是与插槽绑定,方便扩容缩容迁移数据,若数据key和节点绑定,节点宕机无法恢复,那意味着数据就永久丢失了。

image-20240910180734013

redis分片集群可以连接从节点写入数据,不过最终写入操作还是由主节点执行(从节点只计算插槽并路由)

image-20240915145827903

注意在redis集群中,keys * 、flushdb、flushall只能操作当前节点的数据,不能操作整个集群的数据。

image-20240910184336241

一条命令有时无法同时操作多个key(如DEL、SINTER...)判断依据是key的有效部分。若这些key有效部分相同则允许操作,若不一致则会报错:(error) CROSSSLOT Keys in request don't hash to the same slot

image-20240910185952502

redis分片集群默认只有1个数据库,无法执行select db选择数据库,否则报错:(error) ERR SELECT is not allowed in cluster mode

image-20240910184609164

故障演示

redis三主三从分片集群,如果某个主节点宕机后,该主节点的下的从节点会自动选举升级为新主节点,代替故障主节点工作

image-20240911124524993

我们可以查看一下新主节点7006的日志和当前集群状态

image-20240911125825630

redis三主三从分片集群,如果某个主节点宕机后,故障转移完成后,新主节点再次宕机,此时整个集群将不可用,报错:(error) CLUSTERDOWN The cluster is down。难道redis分片集群最少需要三个主节点才能正常工作吗?继续往下看

image-20240911142723096

image-20240911130655038

redis三主三从分片集群,升级为四主四从,如果某个主节点宕机后,故障转移完成后,新主节点再次宕机,此时集群仍不可用,报错:(error) CLUSTERDOWN The cluster is down。说明当某个主节点宕机且该主节点不存在可用从节点时会导致集群不可用

image-20240911151401599

redis三主三从分片集群,如果超过半数的主节点宕机,集群不可用,报错:(error) CLUSTERDOWN The cluster is down。说明当集群中超过半数的主节点宕机,会导致集群不可用,即使故障主节点下存在可用的从节点。

image-20240911152948673

redis三主三从分片集群,若某个主节点宕机后,完成故障转移后,另一个主节点才宕机,待完成故障转移,此时集群仍可用。

image-20240911154310912

redis三主三从分片集群,若某个主节点宕机后,完成故障转移后,故障主节点恢复后会直接变成从节点,并从新主节点同步数据

image-20240911160617808

c#代码演示

以下演示一下c#代码如何连接并使用分片集群,源码参考:分片集群 · logerlink/RedisTest@9c84c6d (github.com)

redis分片集群连接字符串

redis 帮助类改造——CommandFlags.PreferReplica优先从节点读取数据

redis集群常见命令——cluster

redis集群创建成功,我们可以使用cluster的相关命令查看和操作集群,需要进入redis环境中执行

image-20240911162022937

插槽相关

image-20240915170250691

image-20240915165622727

节点相关

image-20240915170137687

image-20240915150503689

CLUSTER RESET用于重置节点,遗忘该节点已知的其他所有节点,撤销指派给该节点的所有槽,并清空节点内部的槽-节点映射。

重置之前主节点不可存在任何key(从节点不受限制)否则报错:(error) ERR CLUSTER RESET can't be called with master nodes containing keys。

可选参数HARD、SOFT,默认SOFT配置。如果执行的是HARD重置,那么该节点会创建一个新节点ID,并将节点的纪元和配置纪元都设置为0

image-20240911184414484

重置主节点后,集群状态显示ok,表示可用,7003节点失联。但是集群有些插槽不可用,读写数据时,redis计算key的slot值刚好落到已重置主节点之前的槽位,会报错这两种错误:

  1. (error) CLUSTERDOWN Hash slot not served (指定SOFT参数会出这个错误)。解决方案:连接另一个可用节点7001进去集群执行CLUSTER MEET 119.45.100.200 7003 17003将重置节点7003重新加入集群,再连接重置节点7003进入集群执行CLUSTER ADDSLOTSRANGE 10923 16383添加全部插槽(不添加全部插槽仍然是失败的)
  2. Could not connect to Redis at :0: Name or service not known(指定HARD参数会出这个错误),未重置的其他主节点不受影响,正常读写。解决方案:连接另一个可用节点7001进去集群执行CLUSTER MEET 119.45.100.200 7003 17003将重置节点7003重新加入集群,并执行CLUSTER FORGET 原7003强重置前的节点Id让集群忘记旧节点Id,再连接重置节点7003进入集群执行CLUSTER ADDSLOTSRANGE 10923 16383添加全部插槽(不添加全部插槽仍然是失败的)

image-20240911185201501

重置从节点后,集群状态显示fail不可用,该节点7006会变成主节点,但不可直接使用也不影响整个集群,其他节点正常读写数据。

image-20240911231705392

重置从节点7006后,我们想让该节点分配插槽存储数据应该怎么处理呢?此时使用redis-cli的check、fix、reshard、rebalance命令都会出现一个错误:[ERR] Nodes don't agree about configuration!。我们只需要手动将节点7006移出集群,再将7006加入集群即可

我们可以先观察一下节点状态、集群状态是否正常,如果正常则观察所有节点的配置文件(集群自动生成和维护那个文件)看看是否正常,不正常则手动修改该配置文件。如果两个问题都正常则连接另一个可用节点7002,执行redis-cli -c -a 123456 -p 7002 --cluster del-node 119.45.100.200:7002 7006节点Id移除7006节点,再执行redis-cli -c -a 123456 -p 7002 --cluster add-node 119.45.100.200:7006 119.45.100.200:7002添加7006节点到集群中,再执行reshard或rebalance就可以分配插槽了。这个场景下不要用cluster meet加入节点7006,也不要使用cluster ADDSLOTS分配插槽,否则报错:(error) ERR Slot xxx is already busy

image-20240916163829996

故障转移

手动故障转移,在从节点上执行,将从节点升级为新的主节点,而原来的主节点降级为从节点。缺省参数FORCE:强制执行,TAKEOVER:更加强制执行

无法在主节点执行手动故障转移,否则报错:(ERR)You should send CLUSTER FAILOVER to a replica

image-20240911171122094

指定FORCE、TAKEOVER的故障转移流程,如下

image-20240911171945652

故障转移大致流程图如下。关于故障转移更多信息请参考:https://www.cnblogs.com/gqtcgq/p/7247041.htmlhttps://developer.aliyun.com/article/638627

image-20240916145250305

redis集群常见命令——redis-cli

使用redis-cli的相关命令查看和操作集群,不需要进入redis环境中执行

image-20240910173529733

image-20240915171439579

image-20240915223502590

节点相关

删除节点,有数据不允许删除节点,否则报错:[ERR] Node 119.45.100.200:7004 is not empty! Reshard data away and try again.

添加节点,默认添加为主节点,但不会自动为新节点分配插槽

image-20240915175516534

插槽相关

重新分配插槽,仅能重新分配插槽给主节点,否则(选择receiveId时)报错:The specified node (e65ee025151ea6c5f3a1049adfc25d1aa4cad77a) is not known or not a master, please retry.不管该节点有无数据,都可以进行重新分配插槽

image-20240915191708318

需要注意的是若将某个节点1的插槽全部迁移至另一个节点2,则节点1将会变成节点2的从节点

image-20240915211523203

那过段时间我又想给这个节点1分配插槽呢?我们可以手动将节点1删除,再以主节点的身份加入集群,这样就可以重新分配插槽了。这里不要使用forget和reset命令删除从节点,反正我没成功过

image-20240915212629807

image-20240915221508787

image-20240915222638000

其他

redis-cli的check, fix, reshard, del-node, set-timeout, info, rebalance, call, import, backup命令可以指定集群内的任意一个节点执行,可以的话建议使用不重要的节点执行这些命令

redis-cli可以和cluster的一些命令结合使用,如下

image-20240915231107520

常见问题

使用docker启动redis,容器一直处于重启状态?

daemonize yes后台运行改成daemonize no即可,亲测有效。修改docker--restart参数不管用

创建集群时,报错:[ERR] Node 119.45.100.200:7001 NOAUTH Authentication required?

redis-cli指定-a加上密码即可,redis-cli --cluster ... -a <password>

image-20240910171829864

创建集群后一直处于Waiting for the cluster to join状态?

大多两个原因:集群的节点端口没有开放;集群的节点总线端口没有开放。集群总线端口用于集群之间互相通信

还有一个原因:同一个机器创建多个节点创建集群并且使用docker运行redis时,各个节点的配置文件的节点端口和总线端口不要一致(如都是6379、16379)否则可能连不上。搞了半天原来是这个原因

image-20240910154631767

如何更安全的设置密码?使用 -a 提示:Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.

若集群设置密码,我们可以使用--no-auth-warning参数取消不安全警告;也可以连接集群后,在redis内部使用auth password指定密码。但是有些redis-cli --cluster一定要加上-a指定密码才可以执行

image-20240916164759615

如何让一批不同的key落到同一个节点上?

redis会根据key的有效部分自动计算插槽值,再找到对应的节点进行读写数据

  • key包含“{}”且“{}”包含一个字符,则“{}”中的内容视为有效部分;
  • key不包含“{}”,则整个key视为有效部分

这个有效部分有妙用,可以起到分组的作用,如我们想让一批key全部落到同一个节点上,我们只需要让这些key的有效部分相同,就可以让这些key有相同的插槽,从而达到让一批不同的key落到同一个节点上。如abc、{abc}name、{abc}info、{abc}age,这些key的有效部分相同——abc,所以都会落到同一个节点上

Redis集群是最多有16384个分片吗

不一定,但是多出来的分片可能分配不到槽位,因为默认情况下,最多只有16384个槽位。建议分片最多不超过1000个节点

为什么是16384个槽位

插槽不是越多越好,因为插槽位太多,会浪费带宽,压缩比高

插槽使用位图这样的数据结构表示,16384(16k,个数基本够用)个slots,需要的是2kb大小,如果给定的slots数量更多了,则需要消耗更多的空间,假如65536 那么他就是(8kb) 心跳包是周期性通信的(非常频繁,吃网络带宽)

redis数据分片算法

主流的数据分片算法有:

  1. 哈希取模——hash(key) % n。实现简单,但是对扩缩容不友好
  2. 一致性哈希算法——65535个操作。扩缩容时只影响该节点附近的节点,影响节点较少
  3. 哈希槽分区算法——crc16(key)%16384。16384固定值,数据与插槽绑定而非机器,方便扩缩容,数据分布均匀

redis使用的是哈希槽分区算法,相较于一致性哈希算法,哈希槽分区算法数据分布更均匀,可手动调整插槽的分配情况

更多请参考:

Redis第10讲——Redis数据分片的三种算法_redis分片规则-CSDN博客

Redis集群,集群的概念 三种主流分片方式-阿里云开发者社区 (aliyun.com)

总结

redis支持很多功能,单实例redis并发读能力不行,我们可以搭建主从集群(Replica),实现读写分离,缓解读压力。但是主从集群无法自动故障恢复,又在主从集群的基础上添加哨兵机制实现健康检测,故障恢复。然而主从+哨兵仍有不足,我们还可以搭建分片集群缓解高并发写和存储大量数据。但是我们会发现学习成本也越来越大,尤其是redis分片集群,经常会冷不丁的就报错:ERR...完全出乎意料,简单总结一下吧

redis优点缺点
单实例易,机器成本小,简单方便快捷读写并发不足,读写没有高可用
主从模式中,支持高并发读,读高可用写并发不足,写没有高可用,机器成本较大,多个节点数据冗余
主从模式+哨兵机制较难,支持高并发读,读写高可用写并发不足,多个节点数据冗余,机器成本大,在主从的基础上增加机器部署哨兵,然而哨兵又不能存储数据(不过哨兵机器的性能配置可以降低)
分片集群难,支持高并发读写,读写高可用,存储大量数据维护困难,机器成本大,扩缩容简单