【高级篇】接下来继续学习Redis的高级篇内容包含分布式缓存、多级缓存、Redis最佳实践相关内容本篇主要是介绍Redis的分布式缓存为了解决单点Redis的问题具体有用RDB和AOF解决Redis的持久化问题Redis的主从集群Redis的哨兵以及分片集群欢迎大家一起学习共同进步文章目录【高级篇】1.分布式缓存1.1Redis持久化RDBAOF1.2Redis的主从1.2.1主从集群结构1.2.2搭建主从集群1.2.3主从的全量同步原理1.2.4主从的增量同步原理1.3Redis哨兵1.3.1哨兵的作用和工作原理1.3.2搭建哨兵集群1.3.3RedisTemplate连接哨兵1.4Redis分片集群1.4.1搭建分片集群1.4.2散列插槽1.4.3集群伸缩1.4.4故障转移1.4.5RedisTemplate访问分片集群1.分布式缓存单点Redis的问题数据丢失问题Redis是内存储存服务器重启可能会丢失数据解决方法实现Redis数据持久化并发能力问题解决方法搭建主从集群实现读写分离故障恢复问题如果Redis宕机则服务不可用需要一种自动的故障恢复手段解决方法利用Redis哨兵实现健康检测和自动恢复存储能力问题解决方法搭建分片集群利用插槽机制实现动态扩容1.1Redis持久化RDBRedis Database Backup fileRedis数据备份文件也叫Redis数据快照简单来说就是把内存中的所有数据都记录到磁盘中。当Redis故障重启后从磁盘读取快照文件恢复数据。快照文件称为RDB文件默认是存储在当前目录下Redis停机时会执行一次RDBRedis的fork原理bgsave开始时会fork主进程得到子进程子进程共享主进程的内存数据。完成fork后读取内存数据并写入RDB文件fork采用的是copy-on-write技术当主进程执行读操作时访问共享内存当主进程执行写操作是则会拷贝一份数据执行写操作总结RDB方式bgsave的基本流程fork主进程得到一个子进程共享内存空间子进程读取内存数据并写入新的RDB文件用新的RDB 文件替换旧的RDB文件RDB会在什么时候执行save 60 1000代表什么默认是服务停止时进行代表60秒至少执行1000次修改则触发RDBRDB的缺点RDB执行间隔时间长两次RDB之间写入数据有丢失的风险fork子进程、压缩、写出RDB文件都比较耗时AOFAOF全称Append Only File追加文件。Redis处理的每一个写命令都会记录在AOF文件可以看做是命令日志文件AOF默认是关闭的需要修改redis.conf配置文件来开启AOF# 是否开启AOF功能appendonlyyes# AOF文件的名称appendfilenameappendonly.aofAOF的命令记录的评率也可以通过redis.conf文件进行配置因为是记录命令AOF文件会比RDB文件大的多而且AOF会记录对同一个key的多次写操作但只有最后一次写操作才有意义。通过执行bgrewriteaof命令可以让文件执行重写功能用最少的命令达到相同的效果对比RDB和AOF各有自己的优缺点如果对数据安全性要求较高在实际开发中往往会结合两者来使用1.2Redis的主从1.2.1主从集群结构单节点Redis的并发能力有限要进一步提高Redis的并发能力急需要搭建主从集群实现读写分离在实际应用中对Redis的读操作比写操作要多所以对master进行读操作对其他slave进行写操作同时master要对slave进行数据同步1.2.2搭建主从集群在同一台虚拟机上开启三个Redis实例模拟主从集群开启主从关系修改配置文件永久生效在redis.conf文件中添加一行配置slaveof masterip masterport使用redis-cli客户端连接到redis服务执行slaveof命令重启后失效slaveof masterip masterport查看集群状态信息INFO replication1.2.3主从的全量同步原理主从第一次同步是全量同步master怎么知道是第一次同步Replication Id简称replid是数据集的标记Id一致则说明是用一个数据集每一个master都有唯一的replidslave则会继承master的replidoffset偏移量随着记录在repl_baklog这个数据增多而增大slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset说明slave数据落后于master需要更新因此slave做数据同步必须向master声明自己的replid和offsetmaster才可以判断需要同步哪些数据判断replid是否一样就能判断是否是第一次同步。简述全量同步的流程slave节点请求增量同步master节点判断replid发现不一致拒绝增量同步master将完整内存数据生成RDB发送RDB到slaveslave清空本地数据加载master的RDBmaster将RDB期间的命令记录在repl_baklog并持续将log中的命令发送给slaveslave执行接收到的命令保持与master之间的同步1.2.4主从的增量同步原理主从第一次同步时全量同步如果slave重启后同步则执行增量同步repl_backlogReplication Backlog在 Redis 的底层实现中本质上就是一个固定大小的循环队列注意repl_baklog大小有上限写满后会覆盖最早的数据。如果slave宕机时间太久导致尚未备份的数据被覆盖则无法基于log做增量同步只能再次全量同步优化主从集群优化全量同步在master中配置repl-diskless-sync yes启用无磁盘复制避免全量同步时的磁盘IORedis单节点上的内存占用不要太大减少RDB导致的过多磁盘IO尽量减少全量同步适当提高repl_baklog的大小发现slave宕机时尽快实现故障恢复尽可能避免全量同步限制一个master上的slave节点数量如果实在太多slave则可以采用主-从-从链式结构减少master压力总结1.3Redis哨兵1.3.1哨兵的作用和工作原理用来实现主从集群的自动故障恢复作用监控哨兵会不断检查master和slave是否按预期工作自动故障恢复如果master故障哨兵会将一个slave提升为master。当故障实例恢复后也以新的master为主通知哨兵充当Redis客户端的服务发现来源当集群发生故障转移时会将最新信息推送给Redis的客户端哨兵如何得知每个节点的状态服务状态监控哨兵基于心跳机制监测服务状态每1秒向集群的每个实例发送ping命令主观下线如果某哨兵节点发现某实例未在规定时间响应则认为该实例主观下线客观下线若超过指定数量quorum的哨兵都认为该实例下线则该实例客观下线quorum值最好超过哨兵实例数量的一半选举新的master一旦发现master故障sentinel需要在salve中选择一个作为新的master选择依据是这样的首先会判断slave节点与master节点断开时间长短如果超过指定值down-after-milliseconds * 10则会排除该slave节点然后判断slave节点的slave-priority值越小优先级越高如果是0则永不参与选举如果slave-prority一样则判断slave节点的offset值越大说明数据越新优先级越高最后是判断slave节点的运行id大小越小优先级越高。实现故障转移让被选中的slave变成master广播让其他slave跟从新的master让旧master变成slave监控对象1. 监控 Master主节点这是哨兵最核心的任务。哨兵会持续检查 Master 是否存活主要为了故障发现判断 Master 是否进入了“主观下线”或“客观下线”状态。自动故障转移一旦 Master 彻底不可用哨兵需要从 Slave 中选举一个新的 Master 来顶替它。2. 监控 Slave从节点哨兵同样会定期检查所有 Slave 节点的状态原因如下故障转移的候选池当 Master 挂掉时哨兵需要从健康的 Slave 中挑选一个数据最完整、优先级最高的节点升级为新的 Master。如果哨兵不监控 Slave就无法知道哪些节点是健康的也就无法完成自动切换。感知拓扑变化哨兵会自动发现新加入的 Slave 节点并将其纳入监控范围。3. 监控其他 Sentinel哨兵节点哨兵通常是以集群模式部署的一般至少 3 个节点它们之间也会互相监控互相发现哨兵通过 Master 节点上的__sentinel__:hello频道进行通信发现彼此的存在。达成共识单个哨兵认为 Master 下线只是“主观下线”必须通过哨兵间的通信和投票达到法定人数Quorum确认才会判定为“客观下线”并触发故障转移。1.3.2搭建哨兵集群创建三个哨兵实例在配置文件指定监控信息1.3.3RedisTemplate连接哨兵在哨兵Sentinel集群监管下的Redis主从集群其节点会因为自动故障转移而发生变化Redis的客户端必须感知这一变化及时更新连接信息。Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动切换在pom文件中引入Redis的starter依赖dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependency然后再配置文件application.yml中指定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配置主从读写分离BeanpublicLettuceClientConfigurationBuilderCustomizerconfigurationBuilderCustomizer(){returnconfigBuilder-configBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);}这里的ReadFrom是配置Redis的读取策略是一个枚举包括下面选择MASTER从主节点读取MASTER_PREFERRED优先从master节点读取master不可用才读取replicaREPLICA从slavereplica节点读取REPLICA_PREFERRED优先从slavereplica节点读取所有的slave都不可用才读取master1.4Redis分片集群分片集群结构主从哨兵可以解决高可用、高并发读的问题但仍然有两个问题海量数据存储问题高并发写问题使用分片集群可以解决分片集群的特征集群中有多个master每个master保存不同数据每个master都可以有多个slave节点master之间通过ping监测彼此健康状态客户端请求可以访问集群任意节点最终都会被转发到正确节点1.4.1搭建分片集群核心步骤具体过程准备环境 → 配置节点 → 启动实例 → 组装集群准备目录和配置文件创建目录编写配置文件以一个节点为例# 端口号 (7001~7006 分别对应修改)port7001# 允许远程访问bind0.0.0.0# 开启集群模式 (必须)cluster-enabledyes# 集群配置文件 (自动生成无需手动修改)cluster-config-file nodes-7001.conf# 节点超时时间 (毫秒)cluster-node-timeout15000# 开启持久化 (推荐 AOF)appendonlyyes# 设置密码 (生产环境强烈建议开启所有节点密码需一致)# requirepass yourpassword# masterauth yourpassword# 后台运行daemonizeyes启动所有Redis节点redis-server /opt/redis-cluster/7001/redis.conf redis-server /opt/redis-cluster/7002/redis.conf redis-server /opt/redis-cluster/7003/redis.conf redis-server /opt/redis-cluster/7004/redis.conf redis-server /opt/redis-cluster/7005/redis.conf redis-server /opt/redis-cluster/7006/redis.conf验证启动ps -ef | grep redis创建并组装集群使用redis-cli工具自动完成主从关系的分配和槽位Slot的分配redis-cli--clustercreate\127.0.0.1:7001127.0.0.1:7002127.0.0.1:7003\127.0.0.1:7004127.0.0.1:7005127.0.0.1:7006\--cluster-replicas1验证集群状态连接集群redis-cli-c-p7001websourcesource_group_web_9/websource查看集群信息cluster info查看节点拓扑cluster nodes1.4.2散列插槽插槽Slot是 Redis 集群用来管理数据和定位数据的“逻辑地图”。虽然数据最终确实是存在 Master 节点里但引入“插槽”这一层中间抽象是为了解决“如何高效地找到数据”以及“如何灵活地调整数据分布”这两个难题Redis如何判断某个key应该在哪个实例将16384个插槽分配到不同的实例根据key的有效部分计算哈希值对16384取余余数作为插槽寻找插槽对应的实例如何将同一类数据固定的保存在同一个Redis实例让这一类数据使用相同的有效部分例如key都以{typeId}为前缀1.4.3集群伸缩集群伸缩可以动态添加或减少节点添加节点redis-cli--clusteradd-node新节点IP:端口现有集群中任意节点IP:端口重新分配插槽启动重新分片对集群中任意节点执行以下命令redis-cli--clusterreshard现有集群中任意节点IP:端口按提示交互操作输入要迁移的插槽数量系统会询问How many slots do you want to move...?。你需要计算并输入一个数值例如如果希望数据均匀分布可以计划将总插槽数16384除以新的主节点总数。输入接收插槽的目标节点 ID系统会询问What is the receiving node ID?。输入你在步骤二末尾记下的新节点的 ID。指定插槽来源系统会询问Please enter all the source node IDs...输入all表示从所有现有的主节点中均衡地迁移插槽推荐。也可以输入特定源节点的 ID然后输入done来指定从哪些节点迁移。确认并执行系统会显示迁移计划询问Do you want to proceed with the proposed reshard plan?。输入yes确认Redis 就会开始在后台异步迁移插槽和对应的数据。1.4.4故障转移当集群中一个master宕机时故障检测主观下线集群中每个节点都会通过心跳ping/pong机制互相检测客观下线当超过半数的主节点都将该节点标记为 PFAIL 后这个状态就会升级为“确认下线”FAIL - Confirmed Failure并通过 Gossip 协议广播给整个集群从节点选举一旦主节点被标记为 FAIL其所有从节点Replica会开始竞争成为新的主节点发起选举每个从节点会延迟一个随机的短暂时间后向集群中所有主节点发送FAILOVER_AUTH_REQUEST投票请求投票胜出条件为了赢得选举一个从节点必须获得超过半数主节点的选票故障转移与晋升选出来的新master会晋升为主执行SLAVEOF NO ONE命令将自己提升为新的主节点。接管插槽接管原主节点负责的所有哈希槽hash slots。广播身份向整个集群广播 PONG 消息宣告自己已成为新的主节点并告知其负责的插槽信息。其他节点收到后会更新自己的集群拓扑信息。客户端重定向请求失败在故障转移期间发往已宕机主节点的请求可能会收到CLUSTERDOWN错误或连接超时的响应。自动重定向具备集群感知能力的客户端如 Jedis Cluster, Lettuce在捕获到异常后会主动查询集群最新的拓扑结构通过CLUSTER SLOTS命令获取新主节点的地址并将后续请求自动重定向过去。Redis 分片集群Redis Cluster没有哨兵Sentinel它依靠的是集群内部每个节点互相监测的机制来保证高可用数据迁移1.4.5RedisTemplate访问分片集群RedisTemplate底层同样基于lettuce实现了分片集群的支持使用的步骤与哨兵模式基本一致引入Redis的starter依赖配置分片集群地址配置读写分离分片集群的配置与哨兵模式不同spring:redis:cluster:nodes:# 指定分片集群的每一个节点信息-192.168.150.101.7001-192.168.150.101.7002-192.168.150.101.7003-192.168.150.101.7004