网站定制开发流程和功能,公司门户官网,wordpress视频缩略图,wordpress 添加简码#x1f525;博客主页#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞#x1f44d;收藏⭐评论✍ 文章目录 1.0 分布式缓存概述 2.0 Redis 持久化 2.1 RDB 持久化 2.1.1 RDB 的 fork 原理 2.2 AOF 持久化 2.3 RDB 与 AOF 之间的区别 3.0 Redis 主从集群 3.1 搭建主从集群 3.2… 博客主页 【小扳_-CSDN博客】 ❤感谢大家点赞收藏⭐评论✍ 文章目录 1.0 分布式缓存概述 2.0 Redis 持久化 2.1 RDB 持久化 2.1.1 RDB 的 fork 原理 2.2 AOF 持久化 2.3 RDB 与 AOF 之间的区别 3.0 Redis 主从集群 3.1 搭建主从集群 3.2 主从的全量同步原理 3.3 主从的增量同步原理 4.0 Redis 哨兵集群 4.1 搭建哨兵集群 4.2 RedisTemplate 连接哨兵 5.0 Redis 分片集群 5.1 搭建分片集群 5.2 散列插槽 5.3 集群伸缩 5.4 故障转移 5.5 RedisTemp 访问分片集群 1.0 分布式缓存概述 分布式缓存是一种用于提高系统性能和可扩展性的技术它通过在多个节点上存储数据副本来减少数据访问延迟和负载。分布式的出现是为了解决在单点 Redis 的问题。 在单点 Redis 存在的问题 数据丢失问题通过实现 Redis 数据持久化来解决当前问题。 并发能力问题通过搭建主从集群实现读写分离来解决当前问题。 存储能力问题利用 Redis 哨兵实现健康检测和自动恢复。 故障恢复问题搭建分片集群利用插槽机制实现动态扩容。 2.0 Redis 持久化 因为 Redis 存储的数据都是在内存中的所以当 Redis 出现宕机或者断电等情况导致停止服务那么 Redis 中的数据就会直接丢失掉。 让 Redis 中的数据可以持久化也就是将 Redis 中的相关的数据保存在磁盘中。因此Redis 持久化常见有两种形式RDB 持久化、AOF 持久化。 这样就算 Redis 服务出现了宕机不需要担心数据丢失可以从磁盘中重新读取之前的数据再进行服务。 2.1 RDB 持久化 RDB 全称 Redis Database Backup fileRedis 数据备份文件也叫做 Redis 数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当 Redis 实例故障重启后从磁盘读取快照文件恢复数据。 该文件称为 RDB 文件默认是保存在当前运行目录。 1通过执行 Save 命令Redis 中的数据就会被重新创建一个 dump.rdb 文件记录来替换原先的文件。 通过时间可以看出来已经替换成功了。证明 save 可以用来将 Redis 内存数据进行拷贝到磁盘中来保证数据持久化。 但是使用 save 命令会有一个弊端save 命令由 Redis 主进程来执行 RDB因此会阻塞所有命令。通过 save 命令来进行数据持久化一般常用于将当前 Redis 服务器清除之前将数据保证起来通常 Redis 服务正在运行的时候是不会直接用 save 命令来保证数据持久化的因为会阻塞其他请求从而影响效率。 在主动将 Redis 服务进行停机之前会自动执行 save 命令将数据进行保存。重新启动该 Redis 服务之后会自动从 RDB 文件中获取数据重新将数据放到内存中。 2使用 bgsave 命令开启子进程执行 RDB避免主进程受到影响。
Redis 内部由触发 RDB 的机制可以在 Redis.conf 文件中找到格式如下 #3600 秒内如果至少由一个 key 被修改则执行 bgsave如果是 save 则表示禁用 RDB 符合条件就会自动触发每隔一段时间自动触发。这就保证了在 Redis 服务过程中也可以将数据进行持久化。
RDB 的其他配置也可以在 Redis.conf 文件中设置 设置是否压缩 RDB 文件建议不开启压缩也会消耗 cpu磁盘不值钱。 设置 RDB 文件名称。 设置 RDB 文件保存的路径目录默认是当前路径下。 2.1.1 RDB 的 fork 原理 bgsave 开始时会 fork 主进程得到子进程子进程共享主进程的内存数据。完成 fork 后子进程读取内存数据并写入到 RDB 文件。
fork 采用的是 copy-on-write 技术 当主进程执行读操作时访问共享内容。 当主进程执行写操作时则会拷贝一份数据执行写操作。 简单来说主进程在进行 fork 子进程的时候会阻塞其他请求因此需要加快 fork 子进程过程。主进程通过页表来操作内存因为通过拷贝页表子进程也就可以读取内存数据了。fork 过程结束之后主进程解除阻塞状态可以正常响应请求而子进程则将内存数据进行读操作写入磁盘中。在子进程写入磁盘过程中主进程有可能会进行写操作所有为了防止脏读的情况在写数据的时候将内存的数据进行拷贝再来进行写操作。 这也说明了当子进程写入数据的过程中主进程写操作的请求越多拷贝的数据也就越多可能会导致数据溢出。
RDB 的缺点 RDB 执行间隔时间长两次 RDB之间写入数据有丢失风险。 fork 子进程、压缩、写出 RDB 文件都比较耗时。 2.2 AOF 持久化 AOF 全称为 Append Only File追加文件。Redis 处理的每一个写命令都会记录在 AOF 文件可以看做是命令日志文件。 当 Redis 服务需要重新启动那么直接来读取 AOF 文件执行所有命令。这就保证了数据持久化。
AOF 持久化默认是关闭的 将 no 改为 yes则启动 AOF 持久化通过设置 appendfilename 来修改 AOP 文件名。 AOF 的命令记录的频率 AOF 也是异步的将命令写入到磁盘中。 # If unsure, use everysec.# 表示每执行一次命令立即记录到AOF文件
# appendfsync always
# 写命令执行完先放入AOF缓冲区然后表示每个1秒将缓冲区数据写到AOF文件是默认方案
appendfsync everysec
# 写命令执行完先放到AOF缓冲区由操作系统决定何时将缓冲区内容写回磁盘
# appendfsync no 因为是记录命令AOF 文件会比 RDB 文件大的多。而且 AOF 会记录对同一个 key 的多次写操作但只有最后一次写操作才意义。通过执行 bgrewriteaof 命令可以让 AOF 文件执行重写功能用最少的命令达到相同效果。
当然不需要我们手动去敲命令Redis 也会在触发阈值时自动去重写 AOF 文件 在配置文件中可以看到 # AOF文件比上次文件增长超过百分之百则触发重写
auto-aof-rewrite-percentage 100# AOF文件体积最小 64 mb以上才触发重写
auto-aof-rewrite-min-size 64mb 2.3 RDB 与 AOF 之间的区别 1持久化方式 RDB定时对整个内存做拷贝。 AOF记录每一次执行的命令。 2数据完整性 RDB不完整两次备份之间会丢失。 AOF相对完整取决于刷盘策略。 3文件大小 RDB会有压缩文件体积小。 AOF记录命令文件体积大。 4宕机恢复速度 RDB很快。 AOF慢。 5数据恢复优先级 RDB低因为数据完整性不如 AOF。 AOF高因为数据完整性更高。 6系统资源占用 RDB高大量CPU 和内存消耗。 AOF低但是 AOF 重写时会占用大量 CPU 和内存资源。 7使用场景 RDB可以容忍数分钟的数据丢失追求更快的启动速度。 AOF对数据安全性要求较高。 3.0 Redis 主从集群 在主从集群架构中主节点Master负责处理所有的写操作而从节点Slave则负责处理读操作。这样可以将读写请求分开从而提高系统的并发处理能力。 通过搭建主从集群实现读写分离来解决高并发能力问题。简单来说就是通过搭建多个从节点来提供只读的服务。 3.1 搭建主从集群 使用一台云服务器搭建一个主节点和两个从节点。 将 redis.conf 文件拷贝到三个目录。 rootiZ2ze0lv9jcc14x57qtu1dZ:~/temp# cp /opt/redis-6.2.5/redis.conf 7001
rootiZ2ze0lv9jcc14x57qtu1dZ:~/temp# cp /opt/redis-6.2.5/redis.conf 7002
rootiZ2ze0lv9jcc14x57qtu1dZ:~/temp# cp /opt/redis-6.2.5/redis.conf 7003接着分别将文件中 redis.conf 配置进行修改比如端口号、RDB 文件的存放路径。 再来修改每个实例的声明 IP 为了避免将来混乱需要在 redis.conf 文件中指定每一个实例的绑定 ip 信息 将 ip 换成自己云服务器的 ip 地址。 启动三个 redis 服务 成功启动 redis 服务。 最后来开启主从关系 将 7001 作为 master 主节点、而 7002、7003 作为 slave 从节点。 其中有临时和永久两种模式 1修改配置文件永久生效 在 redis.conf 中添加一行配置 REPLICAOF masterip masterport 2使用 redis-cli 客户端连接到 redis 服务执行 replicaof 命令重启后会失效 接下来使用临时模式来实现一下 此时已经将 7002 配置成 7001 的从节点。 因此不能进行写操作了读写已经完成分离主节点负责写操作而从节点负责读操作。 需要注意的是如果主节点已经配置密码了那么在连接主节点的时候在从节点的配置文件中设置主节点密码 配置完之后查看连接情况 现在已经 7002、7003 两个 slave 节点连接到 7001 master 节点上了。在 7001 中可以读和写而 7002、7003 只能进行读操作。 那么考虑一个问题当 7001 完成写操作之后7002 可以成功获取数据数据同步的原理是什么呢 因为 7002、7003 自动完成了主从的全量同步或者是增量同步详细讲解如下 3.2 主从的全量同步原理 先来了解两个概念 1Replication Id简称 replid是数据集的标记id 一致则说明是同一数据集。每一个 master 都有唯一的 replidslave 则会继承 master 节点的 replid 。 2offset偏移量随着记录在 rep_baklog 中的数据增多而逐渐增大。slave 完成同步也会记录当前同步的 offset 。如果 slave 的 offset 小于 master 的 offset说明 slave 数据落后于 master需要更新。 当 slave 从节点执行完 replicaof 命令建立连接时会发送 replid、offset 给 master 节点接着 master 主节点会判断发送过来的 replid 是否跟当前的 master 的 replid 是否一致 如果一致则说明已经同步过的 slave 从节点则只需要将在 offset 之后的 repl_baklog 命令发送给 slave 从节点执行接收到的命令即可 如果是不一致则说明有可能是第一次来建立连接的那么 master 先会返回主节点的 replid 和 offset slave 将版本信息进行保存master 会 fork 子进程来执行 bgsave生成 RDB 文件并且发送给 slave slave 先清空本地数据再来加载 RDB 文件。 在 slave 加载 RDB 文件的时候master 也会不断接收写操作的请求命令这些命令会先保存到 repl_baklog 文件中等待 slave 加载完成之后master 发送 repl_baklog 中的命令到 slave 节点中而 slave 节点接收到命令就会执行。 完成以上的步骤之后slave 就完成了数据同步了。 3.3 主从的增量同步原理 增量同步一般都是用于 slave 重启后同步。 slave 重启之后给 master 发送 replid、offset当 master 判断 replid 与当前 master 的 replid 一致那么就不会触发 RDB 发送了而是 master 去 repl_baklog 中获取 offset 后的命令并且发送到 slave 中slave 执行命令这就是增量同步原理。 需要注意repl_baklog 大小有上限写满后会覆盖最早的数据。如果 slave 断开时间过久导致数据被覆盖则无法实现增量同步只能再次全量同步。 需要知道的是执行全量同步消耗的资源是非常大的因此需要优化全量同步或者减少全量同步发生 1在 master 中配置 repl-diskless-sync yes 启用无磁盘复制避免全量同步的磁盘 IO 。 2Redis 单节点上的内存占用不要太大减少 RDB 导致的过多磁盘 IO 。 3适当提高 repl_baklog 的大小、发现 slave 宕机时尽快实现故障尽可能避免全量同步。 4限制 master 上的 slave 节点数量如果实现是太多 slave则可以采用主-从-从链式结构。减少 master 压力。 4.0 Redis 哨兵集群 思考slave 节点宕机恢复后可以找 master 节点同步数据那 master 节点宕机怎么办 这时候就需要用到哨兵了。 Redis 提供了哨兵机制来实现主从集群的自动故障恢复哨兵的主要功能 1监控哨兵会不断检查主从集群中的 master 和 slave 是否按预期工作。 Sentinel 基于心跳机制监测服务状态每隔 1 秒向集群的每个实例发送 ping 命令 主观下线如果某 sentinel 节点发现某实例未在规定时间响应则认为该实例主观下线。 客观下线若超过指定数量的 sentinel 都认为该实例主观下线则该实例客观下线。quorum 值最好超过 sentinel 实例数量的一半。 一旦发现 master 故障sentinel 需要在 slave 中选择一个作为新的 master选择依据 首先会判断 slave 与 master 节点断开时间长短如果超过指定值down-after-milliseconds*10则会排除该 slave 节点。 然后判断 slave 节点的 slave-priority 值越小优先级越高如果是 0 则永不参与选举。 如果 slave-prority 一样则判断 slave 节点的 offset 值越大说明数据越新优先级越高。 最后是判断 slave 节点的运行 id 大小越小优先级越高。 2自动故障恢复如果 master 故障哨兵会将一个 slave 提升为 master 。当故障实例恢复后也以新的 master 为主。 故障转移步骤 sentinel 给备选的 slave 节点发送 slaveof no one 命令让该节点成为 master。 sentinel 给所有其他 slave 发送 slaveof 新选举的 master 的 IP 和 Port 命令让这些 slave 成为新的 master 的从节点开始从新的 master 上进行全量同步。 最后sentinel 将故障节点标记为 slave 当故障节点恢复后会自动成为新的 master 的 slave 节点。 3通知哨兵充当 Redis 客户端的服务发现来源当集群发生故障转移时会将最新信息推送给 Redis 的客户端。 4.1 搭建哨兵集群 搭建三个哨兵节点哨兵的配置文件 解释 port 27003是当前 sentinel 实例的端口。 sentinel monitor mymaster 8.152.162.150 7001 2指定 master 节点信息 mymaster主节点名称自定义。 8.152.162.159 7001主节点的 IP 和 端口号。 2选举 master 时的 quorum 值。
需要注意 如果之前设置密码了那么在 sentinel.conf 配置文件中添加如下配置 ##如果配置了密码打开下面的注释写上密码 格式 sentinel auth-pass mymaster 12345678
# sentinel auth-pass master-name password 最后启动三个哨兵 redis-sentinel s1/sentinel.conf
redis-sentinel s2/sentinel.conf
redis-sentinel s3/sentinel.conf 现在三个 sentinel 已经开始运作了。
测试 现在手动将 7001 master 主节点停机测试哨兵是如何进行故障转移 现在 master 已经停机了。 哨兵也很快发现了 7001 停止服务了。 接着重新选举新的 master此时是 7002 被选中称为主节点。 再接着哨兵会广播给其他 slave 节点重新进行全量同步这时候的主节点为 7002 最后将 7001 标记为 slave当 7001 重启之后是一个 slave并且进行全量同步。 4.2 RedisTemplate 连接哨兵 在 sentinel 集群监管下的 redis 主从集群其节点会因为自动故障转移而发生变化Redis 的客户端必须感知这种变化即使更新连接信息。Spring 的 RedisTemplate 底层利用 lettuce 实现了节点的感知和自动切换。 1在 pom 文件中引入 redis 的 starter 依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency 2然后在配置文件 application.yml 中指定 sentinel 相关信息 spring:data:redis:password: ******host: 8.152.162.159lettuce:pool:max-active: 10max-idle: 10min-idle: 1time-between-eviction-runs: 10sdatabase: 0port: 6379sentinel:master: mymasternodes:- 8.152.162.159:27001- 8.152.162.159:27002- 8.152.162.159:27003
logging:level:io.lettuce.core: debugpattern:dateformat: MM-dd HH:mm:SSS 3配置主从读写分离 Beanpublic LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){return clientConfigurationBuilder - clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);} 这里的 ReadFrom 是配置 Redis 的读取策略是一个枚举包括下面选择 MASTER从主节点读取 MASTER_PREFERRED优先从 master 节点读取master 不可用才读取 replica REPLICA从 slave 节点读取 RREPLICA_PREFERRED优先从 slave 节点读取所有的 slave 都不可用才读取 master。 做一个测试 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;RestController
public class Text {AutowiredStringRedisTemplate stringRedisTemplate;GetMapping(get/{key})public String getKey(PathVariable String key){return stringRedisTemplate.opsForValue().get(key);}GetMapping(set/{key}/{value})public String setKey(PathVariable String key,PathVariable String value){stringRedisTemplate.opsForValue().set(key,value);return OK;}} 当发送请求之前会尝试与 sentinel 节点建立连接再由哨兵节点去订阅 redis 主从节点主要是从 slave 节点来获取数据。 5.0 Redis 分片集群 主从和哨兵可以解决高可用、高并发读的问题。但是依然会有两个问题没有解决 1海量数据存储问题。 2高并发写的问题。 使用分片集群可以解决上述问题分片集群特征 1集群中有多个 master每个 master 保存不同数据。 2每个 master 都可以有多个 salve 节点 3master 之间通过 ping 监测彼此健康状态。 4客户端请求可以访问集群任意节点最终都会被转发到正确节点。 5.1 搭建分片集群 redis.conf 配置文件如下 不同节点的端口号需要进行调整日志存放的位置也要进行调整。 接着创建每个节点之间在集群中的关系 命令说明 redis-cli --cluster 或者 ./redis-trib.rb代表集群操作命令。 create代表创建集群。 --replicas 1 或者 --cluster-replicas 1指定集群中每个 master 的副本个数为 1此时节点总数÷replicas 1得到的就是 master 的数量。因此节点列表中的前 n 个就是 master其他节点都是 slave 节点随机分配得到不同的 master 。 查看集群状态 redis-cli -p 7001 cluster nodes 5.2 散列插槽 Redis 的散列插槽Hash Slots是 Redis 集群模式下用于数据分布和路由的一种机制。Redis 集群将数据分散到多个节点上以实现高可用性和可扩展性。 Redis 集群总共有 16384 个散列槽。每个键在存储时会被映射到这 16384 个槽中的一个。Redis 使用 CRC16 散列算法来计算键的散列值并通过取模运算将其映射到 0 到 16383 的范围内。例如slot CRC16(key) % 16384。 每个 Redis 节点负责一部分散列槽。通过将键映射到散列槽Redis 可以确定该键应该存储在哪个节点上。在集群中客户端在执行命令时会根据键的散列槽来确定目标节点。 5.3 集群伸缩 Redis 集群支持动态伸缩允许用户根据需求增加或减少节点。
添加 7004 节点 给 7004 分配插槽 5.4 故障转移 利用 cluster failover 命令可以手动让集群中的某个 master 宕机切换到执行 cluster failover 命令的这个 slave 节点实现无感知的数据迁移。
流程如下 7002 从节点转换为 master 的命令 5.5 RedisTemp 访问分片集群 RedisTemplate 底层同样基于 lettuce 实现了分片集群的支持而使用的步骤与哨兵模式基本一致 1引入 redis 的 starter 依赖 2配置分片集群地址 3配置读写分离 与哨兵模式相比其中只有分片集群的配置方式只是有一点点差异如下