当前位置: 首页 > article >正文

【从零开始学Redis】高级篇--超全总结笔记

Redis从入门到精通,超全笔记专栏

Hello,大家好,我是姜来可期(全网同名),一名在读985转码大学生,一起学习,共同交流!!!

分布式缓存

分布式缓存是指将缓存数据分布存储在多个服务器上,解觉单台缓存服务器的弊端问题

假设你有一个大型网站,用户访问的速度非常快,你希望能够快速响应每个请求,避免每次都去查数据库。为了加速这个过程,你把一些经常访问的数据保存在内存中(就是缓存),这样下次请求的时候直接从缓存中获取,速度会非常快。

但是,随着数据量和用户量的增加,单个服务器的内存容量有限,无法存储所有数据,同时也容易出现单点故障。分布式缓存就像是将多个单点服务器连接在一起,每个服务器负责存储一部分数据,大家协同工作,确保可以存储更多的数据,避免一个服务器坏了就无法取到数据。

分布式缓存的特点
1. 数据分布:数据不是存储在单个缓存服务器上,而是分布在多个服务器上。

2. 高可用性:避免单台服务器宕机导致缓存不可用,分布式缓存通常设置多个副本(主从复制)。如果某个服务器宕机了,可以从其他副本中取数据,保证系统的高可用性。

3. 扩展性:随着数据量增加,可以不断地向集群中添加新的缓存服务器,系统的容量和性能都能随之扩展。

4. 负载均衡:通过负载均衡策略,分布式缓存能够分散访问压力,使得每个服务器处理的请求量合理且相对均匀,提高系统的整体性能。

主要的分布式缓存工具:

Redis集群:Redis支持通过集群模式,将数据分布到多个节点上,每个节点负责一部分数据,同时保证数据的一致性和高可用性。

应用场景:
1. 网站加速:通过缓存热点数据,减少数据库的访问频率,加速网站响应的速度。

举个例子:

假设你在一个电商网站上购买商品,商品的库存信息会存储在数据库中。每次查询库存时,直接查询数据库会导致响应慢,尤其是在高峰时段。于是你将库存数据缓存到 Redis 中,这样查询库存时首先会访问 Redis,如果没有数据,再访问数据库。随着访问量增大,Redis 集群会将缓存数据分布到多个服务器上,确保整个系统的高效和稳定。

总结,分布式缓存,就是将缓存分布到多个服务器上面,解决了单一服务器的容量、性能、故障等问题,使得系统可以高效地处理大量数据和请求。


Redis集群

基于Redis集群解决单机Redis存在的一些问题

1. 数据丢失问题——实现数据持久化

Redis是内存存储,服务重启可能会丢失数据

2. 并发能力问题——搭建主从集群,实现读写分离

单节点Redis并发能力虽然不错,但也无法满足像618这样的高并发场景

3. 故障恢复问题——利用Redis哨兵,实现健康检测和自动恢复

如果Redis宕机,那么服务就不可用,需要一种自动的故障恢复手段

4. 存储能力问题——搭建分片集群,利用插槽机制实现动态扩容

Redis存储是基于内存的,单节点能够存储的数据量很那满足海量数据的要求

1.Redis持久化

是为了解决数据丢失问题(Redis存储数据在内存,服务重启后先前的数据会丢失)

1.1.RDB持久化

RDB全称为——Redis Database Backup File (Redis数据备份文件),也会叫做  Redis数据快照。简单来说,就是把内存中存储的所有数据都记录到磁盘中。当Redis实例重启后,从磁盘中读取快照文件,恢复之前存储的数据。

快照文件称为RDB文件,默认是保存在当前的运行目录(在哪运行Redis,就保存到哪里)


1.1.1.执行时机

RDB持久化会在下面四种情况发生:

  • 执行 save 命令
  • 执行 bgsave 命令
  • Redis 停机时
  • 触发 RDB 条件时

1)save 命令

当执行下面的命令的时候,会将 Redis 中的所有数据持久化磁盘上(即创建一个RDB快照文件) 。但是在执行这个命令的时候,他会导致 Redis 阻塞其他所有的命令,也就是说,在执行 save 命令的时候,Redis 不会响应任何其他的请求,知道持久化的过程结束。

为什么save会阻塞?

1.持久化操作:save 命令会将 Redis 内存中的数据写入磁盘中(以 RDB 格式保存),这个过程会需要一些时间,尤其是当数据量非常大的时候。而且,写入磁盘的操作,本身就很耗时。

2. 阻塞:在这个过程中,Redis 不会处理其他客户端的请求,包括读写命令,所有的操作都会被“暂停”直到 SAVE 完成。因为 Redis 进程在执行 SAVE 时会占用大部分 CPU 和磁盘 I/O 资源,所以其他请求会被阻塞。

为什么不推荐使用 save 命令?

1. 性能问题:save 在执行的过程中 会导致Redis停止处理其他请求,这会导致显著的性能下降,造成延迟。

2. 同步阻塞:save 是一个 同步阻塞 操作,意味着它会等到持久化完成后才返回,这个过程会阻塞一段时间

何时使用 save 命令?

SAVE 命令在进行 数据迁移故障恢复 时可能有用。比如你想要将当前 Redis 的数据保存到磁盘上以便之后恢复,或者你希望确保数据在某些特定情况下已经被写入磁盘。然而,通常情况下,Redis 推荐使用 异步的持久化方式(例如 BGSAVE 命令或者 AOF 持久化),它们可以在后台执行持久化操作,不会阻塞其他命令的执行。


2)bgsave 命令

下面的命令可以异步执行RDB:

是 Redis 中用于持久化数据的一种异步方式,和 SAVE 命令不同,BGSAVE 不会阻塞 Redis 主进程。它是通过开启一个 子进程 来执行 RDB 快照的生成过程,而 Redis 主进程则可以继续处理用户的请求。

bgsave命令的工作原理

1. 异步执行:当执行 BGSAVE 命令时,Redis 主进程会启动一个 子进程 来生成数据的 RDB 快照。这个子进程会负责将 Redis 内存中的数据写入磁盘,并且这个过程是独立于主进程的,不会影响主进程继续接收和响应客户端请求。

2. 主进程不好受影响:由于子进程负责持久化任务,所以主进程仍然可以处理用户的命令和请求。不会因持久化操作的阻塞而影响到系统的整体性能。

与 save 命令的对比:

  • SAVE:同步执行,执行时会阻塞 Redis,直到数据写入磁盘完成,无法处理其他命令。
  • BGSAVE:异步执行,Redis 会创建一个子进程来执行 RDB 持久化操作,主进程可以继续处理请求,性能更高,适合生产环境。

3)停机时

redis停机会自动执行一次 save 命令,实现一次 RDB持久化。


4)触发 RDB 条件

Redis内部有触发RDB的机制,允许你根据特定条件自动创建数据的快照。你可以在 redis.conf 文件中配置这些条件,以决定何时执行 RDB 持久化操作。

redis.conf 文件中,save 配置项定义了触发 RDB 快照生成的条件。格式如下:

save <seconds> <changes>
# 900秒内,如果至少有1个key被修改,则执行bgsave
# 如果是save "" 则表示禁用RDB
save 900 1  
# 300秒内,如果至少有10个key被修改,则执行bgsave
save 300 10  
save 60 10000 

除了触发条件以外,Redis还提供了一些其他的配置选项 来控制 RDB的行为:

# 是否压缩 ,建议不开启,虽然压缩体积会变小,减少磁盘占用空间,但是压缩也会消耗cpu,磁盘的话不值钱
rdbcompression yes

# RDB文件名称
dbfilename dump.rdb  

# 文件保存的路径目录
dir ./ 

虽然启用压缩可以减小 RDB 文件的体积,节省磁盘空间,但压缩和解压过程会消耗一定的 CPU 资源。因为磁盘相对便宜,通常建议关闭压缩(rdbcompression no)。

RDB方案的默认配置是已经开启了的,但是他存在一些缺陷:如果 Redis崩溃或断电,可能会丢失最后一次快照之后的数据。因此,在高可用要求较高的场景中,会结合使用 AOF(Append-Only  File)持久化机制来弥补 RDB 的不足。


1.1.2. RDB原理

执行 basave 命令,开始时,会 fork 主进程得到子进程,这个子进程共享主进程的内存数据。完成fork后,子进程会读取内存数据,并写入磁盘的RDB文件中。

fork 是一种在操作系统中用于创建子进程的系统调用。简单来说,就是主进程(父进程)调用 fork 后,操作系统会创建一个与主进程几乎完全相同的新进程,这个新进程叫做 子进程。

在fork调用之后,父进程和子进程各自独立执行。
通常,父进程在创建子进程后继续执行,而子进程可以执行一些不同的操作。

在实际应用中,fork 通常用于进程并发、创建后台任务等场景。

fork采用的是 copy-on-write (写时复制)操作

1.当主进程访问读操作时,访问共享内存;

2. 当主进程执行写操作时,则会拷贝一份数据,执行写操作。

3. 每一次只要有写,都会拷贝,避免这个读和写同时发生导致的一些问题

copy-on-write 技术

  1. 内存共享机制:在 Linux 系统中,进程不能直接操作物理内存,而是操作虚拟内存,物理内存和虚拟内存之间通过页表存在一个映射关系。当 fork 操作发生时,会复制一份页表,由于映射关系也被复制,所以子进程和主进程实际上共享同一块物理内存 ,这使得 fork 操作的开销相对较小。
  2. 读操作情况:当主进程执行读操作时,直接访问共享内存。从图中可以看到,主进程和子进程的读操作箭头都指向同一块物理内存中的数据(如数据 A 和数据 B),因为此时数据是只读状态,共享内存不会产生冲突。
  3. 写操作情况:当主进程执行写操作时,就会触发 copy - on - write 技术。系统会拷贝一份需要写入的数据(如图片 2 中的数据 B 副本),然后在这份拷贝的数据上执行写操作。这样做可以避免读和写同时发生导致的数据不一致等问题,保证了数据的一致性和操作的正确性。

展示了 bgsave 开始 fork 主进程得到子进程,子进程共享主进程内存数据的整体流程,以及 fork 时复制页表共享内存的原理,还说明了 Linux 系统中进程与内存的操作关系。

  • 重点展示了主进程执行写操作时 copy - on - write 技术的具体过程,即拷贝数据副本并在副本上执行写操作,而子进程仍访问原来的共享数据。

1.1.3. 小结

RDB方式bgsave的基本流程?

  • fork主进程得到一个子进程,共享内存空间

  • 子进程读取内存数据并写入新的RDB文件

  • 用新RDB文件替换旧的RDB文件

RDB会在什么时候执行?save 60 1000代表什么含义?

  • 默认是服务停止时

  • 代表60秒内至少执行1000次修改则触发RDB

RDB的缺点?

  • RDB执行间隔时间长,两次RDB之间写入数据有丢失的风险

  • fork子进程、压缩、写出RDB文件都比较耗时(因为这些操作比较耗时,所以你也不能把RDB执行间隔设置的太短)

1.2.AOF持久化

1.2.1.AOF原理

AOF全称为 Append Only File (追加文件)。Redis 处理的每一个命令都会被记录到 AOF文件中,可以看做是——命令记录文件。


1.2.2.AOF配置

AOF默认是关闭的,需要修改 reids.conf 配置文件 来开启 AOF:

# 是否开启AOF功能,默认是no
appendonly yes
# AOF文件的名称
appendfilename "appendonly.aof"

AOF的命令记录的频率也可以通过redis.conf文件来配

# 表示每执行一次写命令,立即记录到AOF文件
appendfsync always 
# 写命令执行完先放入AOF缓冲区,然后表示每隔1秒将缓冲区数据写到AOF文件,是默认方案
appendfsync everysec 
# 写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓冲区内容写回磁盘
appendfsync no

三种策略的对比

我们可以查看一下 AOF 文件中记录的内容


1.2.3.AOF文件重写

因为AOF文件是记录命令,AOF 文件会比 RDB 文件大得多。而且 AOF 文件会记录对同一个 key 的多次写操作,但只有最后一次写操作才会有意义。

我们可以通过执行 barewriteaof 命令,让AOF文件执行一次重写,用最少的命令达到相同的效果,减少 AOF 文件的体积。

如图中所示,AOF 原本记录着三个 set 命令,但是set num 123 和 set num 666都是对num的操作,第二次会覆盖第一次的值,因此第一个命令记录下来没有意义。

所以重写命令后,AOF文件内容就是:mset name jack num 666 ,用最少的命令实现相同效果!

Redis也会在触发阈值时自动去重写AOF文件。阈值也可以在redis.conf中配置:

# AOF文件比上次文件 增长超过多少百分比则触发重写
auto-aof-rewrite-percentage 100
# AOF文件体积最小多大以上才触发重写 
auto-aof-rewrite-min-size 64mb 

1.3.RDB与AOF对比

只能说各有优缺点。在实际开发中,针对不同的场景,往往会结合两者来使用。

2.Redis主从

搭建Redis主从集群,是为了解决 Redis 并发能力问题。

2.1.搭建主从架构

单节点的Redis的并发能力是有上限的,要进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离。

1. 一个主节点,多个从节点。

2. 大部分应用场景式——读多写少,读写分离

共包含三个节点,一个主节点,两个从节点。

我们可以选择在同一台虚拟机中的不同端口上运行3个Redis实例,模拟主从集群。

具体的搭建流程可以参考其他资料,网上非常全面。

1. 实现主从节点的读写分离

2. 实现主从节点之间的数据同步机制。

假设有A、B两个Redis实例,如何让 B 作为 A 的slave节点?

在B节点执行命令:slaveof   A的IP  A的port


2.2.主从数据同步原理

2.2.1.全量同步

当主从节点之间第一次建立连接的时候,会执行全量同步,将master节点中的全部数据都拷贝给slave节点,流程如图所示。


数据同步原理

这里有一个问题,master如何得知salve是第一次来连接呢??

这里就会有几个概念,需要了解一下,可以作为判断依据:

  • Replication ID : 简称 replid,是数据集的标记,如果 replid 一致则说明是同一个数据集。每个 master 都有一个唯一的 replid,master 的从节点 slave 都会继承 master 节点的 replid。
  • offser :偏移量,随着记录在 repl_baklog 中的数据增多而逐渐增大。slave 完成同步的时候也会记录当前同步的 offset。如果 slave 的offset 小于 master 的offset,说明 slave 数据落后于 master, 需要更新。

因此,slave 做数据同步的时候,必须向 master 声明自己的 replication id 和 offset, master 才可以进行判断。

因为slave原本也是一个master,有自己的replid和offset,当第一次变成slave,与master建立连接时,发送的replid和offset是自己的replid和offset。

master判断发现slave发送来的replid与自己的不一致,说明这是一个全新的slave,这是主从节点之间第一次建立连接,就知道要做全量同步了。

master会将自己的replid和offset都发送给这个slave,slave保存这些信息。以后slave的replid就与master一致了。

因此,master 判断一个节点是否是第一次建立连接进行同步,就是判断 replid 是否一致。

如图所示


完整的流程描述

  • slave 节点请求增量同步
  • master 节点判断 replid ,发现不一致,说明是第一次建立连接,拒绝增量同步,执行全量同步。
  • master 将所有的内存数据生成 RDB, 然后发送 RDB 到 slave
  • slave 清空本地数据,然后加载 master 的 RDB
  • master 将 RDB 期间的命令都记录到 repl_baklog中,并且持续将 log 中的命令发送给 slave
  • slave 执行接收到的命令,进一步保持与 master 之间的同步

2.2.2.增量同步

通过上面的学习,我们知道要完成全量同步,需要先做 RDB, 然后将整个 RDB 文件通过网络传输给 slave,这样子成本很高的。

因此,除了第一次建立连接数据同步的时候做的是全量同步,其他大多数时候 slave 与 master 都是做的增量同步。

那么,什么是增量同步??就是只更新 slave 与 master 存在差异的部分数据。如图所示,更加清晰:

从图中我们可以知道,master 会判断 replid 与自己的是否一致,如果一致,说明不是第一次,进行增量同步。然后,master 会到 repl_baklog 文件中去获取 slave 传过来的 offset 之后的命令,将 offset 之后的命令发送给 slave,然后 slave 执行这些命令,完成增量同步。
 

2.2.3.repl_backlog 原理

但是,master 是如何知道 slave 与自己的数据差异在哪里呢?
这个就要说到做全量同步的时候,有一个 repl_baklog 文件了。

这个文件是一个固定大小的数组,只不过这个数据是环形的,也就是说当角标到达数组末尾之后,就会再次从0开始读写,这样数组头部的数据就会被覆盖。

repl_baklog中会记录 Redis 处理过的命令日志以及 offset,包括当前这个状态 master 的 offset,以及 slave 已经拷贝了的 offset。如图所示,非常形象。

slave与master的offset之间的差异,就是salve需要增量拷贝的数据了。

随着不断有数据写入,master的offset逐渐变大,slave也不断的拷贝,追赶master的offset

直到数组被填满。

此时此刻,如果有新的数据写入,就会覆盖数组中的旧数据。不过,旧的数据只要是绿色的,说明是 slave 已经同步了的数据,被覆盖了并没有什么影响。因为未同步的数据仅仅是红色部分。

但是,如果slave出现网络阻塞,导致master的offset远远超过了slave的offset:

如果master继续写入新数据,其offset就会覆盖旧的数据,直到将slave现在的offset也覆盖:

棕色框中的红色部分,就是尚未同步、但是已经被覆盖了的数据。此时如果slave恢复,需要同步,却发现自己的offset都没有了,无法完成增量同步了。只能做全量同步。


2.3.主从同步优化

主从同步可以保证主从数据的一致性,这对与读写分离非常重要。

我们可以从以下几个方面来优化Redis主从集群:

  • 在master中配置repl-diskless-sync yes启用无磁盘复制,避免全量同步时的磁盘IO。 写RDB文件时,不往磁盘中写,直接通过网络发送给从节点,提高全量同步的性能

  • Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO 提高全量同步的性能

  • 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步

  • 限制一个master上的slave节点数量(减少master的压力),如果实在是太多slave,则可以采用主-从-从链式结构,减少master压力

主从从架构图:

2.4.小结

简述全量同步和增量同步之间的区别???

  • 全量同步:master 将完整的内存数据生成 RDB 文件,然后发送 RDB 文件到 slave。后续的命令则记录到 repl_baklog 中,逐个发送给 slave。
  • 增量同步:slave 提交自己的 offset 到 master,master 从 repl_baklog 中获取 offset 之后的命令发送给 slave 。

什么时候执行全量同步?

  • slave 节点第一次连接 master 节点的时候
  • slave 节点断开时间太久,repl_baklog 中的 offset 已经被覆盖了

什么时候执行增量同步?

  • 能够在 repl_baklog 中找到 offset。

3.Redis哨兵

3.1.哨兵原理

3.1.1.集群结构和作用

哨兵的结构如图:

哨兵的作用如下:

  • 监控:Sentinel 会不断检查您的 master 和 slave 是否按预期工作。
  • 自动故障恢复:如果 master 故障,Sentinel 会将一个 slave 提升为 master (主从切换)。当故障实例恢复以后也以新的 master 为主
  • 通知:Sentinel 充当 Redis 客户端的服务发现来源,当集群发生故障转移时,会将最新信息(主从状态切换以后的主从地址)推送给 Redis 的客户端。

3.1.2.集群监控原理

Sentinel 基于心跳机制检测服务状态,每隔 1s 向集群的每个实例发送 ping 命令:

  • 主观下线:如果某 sentinel 节点发现某实例未在规定的时间内响应,则认为该实例主观下线
  • 客观下线:若超过指定的数量(quorum)(在Redis配置文件中可以修改)的 sentinel 都认为该实例主观下线,则该实例客观下线。quorum 值最好超过 Sentinel 实例数量的一半。

3.1.3.集群故障恢复原理

一旦发现 master 故障,sentinel 需要在 slave 中选择一个作为新的 master,选择依据如下:

  • 首先会判断 slave 节点与 master 节点断开时间长短,如果超过指定值(down-after-milliseconds * 10)则会排除该slave节点
  • 然后判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举

  • 如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高

  • 最后是判断slave节点的运行id大小,越小优先级越高

当选出一个新的master后,该如何实现切换呢?

流程如下:

  • sentinel给备选的slave1节点发送 slaveof no one ,让该节点成为master

  • sentinel给所有其它slave发送slaveof 192.168.150.101 7002 命令,让这些slave成为新master的从节点,开始从新的master上同步数据。

  • 最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点

    3.1.4.小结

    Sentinel的三个作用是什么?

    • 监控

    • 故障转移

    • 通知

    Sentinel如何判断一个redis实例是否健康?

    • 每隔1秒发送一次ping命令,如果超过一定时间没有相向则认为是主观下线

    • 如果大多数sentinel都认为实例主观下线,则判定服务下线

    故障转移步骤有哪些?

    • 首先选定一个slave作为新的master,执行slaveof no one

    • 然后让所有节点都执行slaveof 新master

    • 修改故障节点配置,添加slaveof 新master

    3.2.搭建哨兵集群

    具体的搭建流程参考其他资料。

    3.3.RedisTemplate的哨兵模式

    在 Sentinel 集群监督下的 Redis 主从集群,其节点会因为自动故障转移而发生变化,Redis的客户端必须能够感知到这种变化,及时更新连接信息。Spring 的 RedisTemplate 底层利用 lettuce 实现节点的感知和自动切换——可以自动实现主从节点的切换

    下面,我们可以通过一个 demo 来实现 RedisTemplate 集成哨兵机制

    导入Demo工程

    引入依赖

    在项目的pom文件中引入 Redis的 starter 依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    配置 Redis 地址

    然后在项目的配置文件 application.yml 中指定的 redis 的 sentinel 相关信息

    spring:
      redis:
        sentinel:
          master: mymaster   # 指定master名称
          nodes: # 指定redis-sentinel集群地址
            - 192.168.150.101:27001
            - 192.168.150.101:27002
            - 192.168.150.101:27003

    配置读写分离

    在项目的启动类中,添加一个新的bean:

    @Bean
    public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
        return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
    }

    LettuceClientConfigurationBuilderCustomizer:

    • LettuceClientConfigurationBuilderCustomizer 是一个接口,通常用于定制和修改 Lettuce 客户端的配置。
    • Lettuce 是一个流行的 Redis 客户端库,在 Spring 中用来与 Redis 交互。LettuceClientConfigurationBuilderCustomizer 允许你定制 Lettuce 客户端的配置,例如设置连接池、超时、读写策略等。

    clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED)

    • 这段代码传递给 LettuceClientConfigurationBuilderCustomizer 接口的实现是一个 Lambda 表达式。
    • clientConfigurationBuilder 是一个配置构建器对象,允许你通过它来设置 Lettuce 客户端的各种配置选项。
    • readFrom(ReadFrom.REPLICA_PREFERRED) 是配置客户端的读策略。ReadFrom 是一个枚举,表示读取 Redis 数据的策略:
      • REPLICA_PREFERRED 表示客户端会优先从 Redis 的从节点(Replica)读取数据,而不是直接从主节点(Master)读取。这样可以减少主节点的负载,但仍然保证读取到的数据是可用的(主节点和从节点的数据是同步的,尽管可能会有轻微延迟)。

    整体作用:

    • 这段代码的作用是定义一个 @Bean 方法,返回一个 LettuceClientConfigurationBuilderCustomizer 实例,该实例通过 Lambda 配置了 Lettuce 客户端的读取策略,使其优先从 Redis 从节点读取数据。这种配置可以在 Redis 主从结构下分担主节点的负载,提升系统性能。

    总结:

    • 这段代码配置了 Lettuce 客户端的读策略,指定优先从 Redis 从节点读取数据。通过将这个定制器注册为 Spring Bean,可以在应用中轻松配置 Lettuce 客户端行为。

    这个bean中配置的就是读写策略,ReadFrom是配置Redis的读取策略,是一个枚举,包括四种:

    • MASTER:从主节点读取

    • MASTER_PREFERRED:优先从master节点读取,master不可用才读取replica

    • REPLICA:从slave(replica)节点读取

    • REPLICA _PREFERRED:优先从slave(replica)节点读取,所有的slave都不可用才读取master

    4.Redis分片集群

    4.1.搭建分片集群

    主从和哨兵可以解决高并发读、高可用的问题。但是目前依然存在两个问题没有解决:

    • 海量数据的存储问题
    • 高并发写的问题

    使用分片集群可以解决上述的两个问题,分片集群的特征如下:

    • 集群中有多个 master 每个 master 保存不同的数据。

    使用分片集群可以解决上述的问题,如图所示:

    分片集群的特征:

    • 集群中有多个 master , 每个 master 保存不同的数据
    • 每个 master 都可以有多个 slave 节点
    • master 之间通过 ping 检测彼此健康状态
    • 客户端请求可以访问集群中的任意节点,但是最终都会被转发到正确节点(最终都会被路由到正确的结点上)

    4.2.散列插槽

    4.2.1.插槽原理

    Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到:

    1. 7001——分配的是 0 —5460这些插槽

    2. 7002——分配的是5461—10922这些插槽

    3. 7003——分配的是10923—16383这些插槽


    set key value时,数据存储到哪个master??其实是与插槽值有关的。。

    数据key不是与节点绑定,而是与插槽绑定。redis会根据key的有效部分计算插槽值,分两种情况:

    • key中包含"{}",且“{}”中至少包含1个字符,“{}”中的部分是有效部分

    • key中不包含“{}”,整个key都是有效部分

    例如:key是num,那么就根据num计算,如果是{itcast}num,则根据itcast计算。计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值。

    如图,在7001这个节点执行set a 1时,对a做hash运算,对16384取余,得到的结果是15495,因此要存储到7003节点。

    到了7003后,执行get num时,对num做hash运算,对16384取余,得到的结果是2765,因此需要切换到7001节点

    • 将数据与插槽绑定,如果节点宕机,那么将插槽再与一个活着的节点绑定即可,插槽上的数据又不会丢失

    • 数据跟着插槽走

    4.2.2.小结

    Redis如何判断某个key应该在哪个实例?

    • 将16384个插槽分配到不同的实例

    • 根据key的有效部分计算哈希值,对16384取余

    • 余数作为插槽,寻找插槽所在实例即可

    如何将同一类数据固定的保存在同一个Redis实例?

    • 这一类数据使用相同的有效部分,例如key都以{typeId}为前缀

    4.3.集群伸缩

    • 集群伸缩 —— 集群能够动态地 增加节点或者是移除节点

    redis-cli --cluster提供了很多操作集群的命令,可以通过下面方式查看:

    比如,添加节点的命令:

    4.3.1.需求分析

    需求:向集群中添加一个新的master节点,并向其中存储 num = 10

    • 启动一个新的redis实例,端口为7004

    • 添加7004到之前的集群,并作为一个master节点

    • 给7004节点分配插槽,使得num这个key可以存储到7004实例

    这里需要两个新的功能:

    • 添加一个节点到集群中

    • 将部分插槽分配到新节点

    4.3.2.创建新的 Redis 实例

    4.3.3.添加新节点到redis中

    4.3.4.转移插槽

    我们要将num存储到7004节点,因此需要先看看num的插槽是多少:

    如上图所示,num的插槽为2765.

    我们可以将0~3000的插槽从7001转移到7004,命令格式如下:

    具体命令如下:

    建立连接:

    得到下面的反馈:

    询问要移动多少个插槽,我们计划是3000个:

    新的问题来了:

    哪个node来接收这些插槽??

    显然是7004,那么7004节点的id是多少呢?

    复制这个id,然后拷贝到刚才的控制台后:

    这里询问,你的插槽是从哪里移动过来的?

    • all:代表全部,也就是三个节点各转移一部分

    • 具体的id:目标节点的id

    • done:没有了

    这里我们要从7001获取,因此填写7001的id:

    填完后,点击done,这样插槽转移就准备好了:

    确认要转移吗?输入yes:

    然后,通过命令查看结果:

    可以看到:

    目的达成。

    4.4.故障转移

    集群初始状态是这样的:

    其中7001、7002、7003都是master,我们计划让7002宕机。

    4.4.1.自动故障转移

    当集群中有一个master宕机会发生什么呢?

    直接停止一个redis实例,例如7002:

    redis-cli -p 7002 shutdown

    1)首先是该实例与其它实例失去连接

    2)然后是疑似宕机:

    3)最后是确定下线,自动提升一个slave为新的master:

    4)当7002再次启动,就会变为一个slave节点了:

    4.4.2.手动故障转移

    利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。其流程如下:

    这种failover命令可以指定三种模式:

    • 缺省:默认的流程,如图1~6歩

    • force:省略了对offset的一致性校验

    • takeover:直接执行第5歩,忽略数据一致性、忽略master状态和其它master的意见

    案例需求:在7002这个slave节点执行手动故障转移,重新夺回master地位

    步骤如下:

    1)利用redis-cli连接7002这个节点

    2)执行cluster failover命令

    如图:

     

    效果:

    4.5.RedisTemplate访问分片集群

    RedisTemplate底层同样基于lettuce实现了分片集群的支持,而使用的步骤与哨兵模式基本一致:

    1)引入redis的starter依赖

    2)配置分片集群地址

    3)配置读写分离

    与哨兵模式相比,其中只有分片集群的配置方式略有差异,如下

    spring:
      redis:
        cluster:
          nodes:
            - 192.168.150.101:7001
            - 192.168.150.101:7002
            - 192.168.150.101:7003
            - 192.168.150.101:8001
            - 192.168.150.101:8002
            - 192.168.150.101:8003


    http://www.kler.cn/a/561222.html

    相关文章:

  • Qt基础之四十九:Qt属性系统(Property System)
  • 洛谷 最长公共子序列
  • 《论面向对象的建模及应用》审题技巧 - 系统架构设计师
  • Docker:Docker从入门到精通(一)- Docker简介
  • 飞天侠:用 aioredis 加速你的 Redis 操作
  • 鸿蒙5.0实战案例:基于原生能力的横竖屏旋转适配
  • Linux设备驱动开发-GPIO子系统使用详解
  • 为人工智能驱动的交通研究增强路面传感器数据采集
  • 设计模式-结构型-代理模式
  • 国产编辑器EverEdit - 如何在EverEdit中创建工程?
  • Pytorch实现之GIEGAN(生成器信息增强GAN)训练自己的数据集
  • 附录1:网络英文简写大全
  • ESP32S3:参考官方提供的led_strip组件使用 SPI + DMA 方式驱动WS2812 RGB灯的实现思路 (实现各个平台移植使用该方式)
  • shell 脚本中的 sh 和 bash 是有区别的
  • 什么是向量化?ElasticSearch如何存储向量?
  • 2025-02-23 学习记录--C/C++-PTA 7-28 猴子选大王
  • 机器学习数学基础:37.偏相关分析
  • 基于C#+SQL Server设计与实现的教学管理信息系统
  • 机器学习数学基础:32.斯皮尔曼等级相关
  • 火语言RPA--Excel添加Sheet页