怎么开始做网站,天元建设集团有限公司财务报表,医院网站建设投标书,安卓下载文章目录 1. 单点Redis的问题数据丢失问题并发能力问题故障恢复问题存储能力问题 2. Redis持久化 - 数据丢失问题RDB持久化linux单机安装Redis步骤RDB持久化与恢复示例#xff08;详细#xff09;RDB机制RDB配置示例RDB的fork原理总结 AOF持久化AOF配置示例#xff08;详… 文章目录 1. 单点Redis的问题数据丢失问题并发能力问题故障恢复问题存储能力问题 2. Redis持久化 - 数据丢失问题RDB持久化linux单机安装Redis步骤RDB持久化与恢复示例详细RDB机制RDB配置示例RDB的fork原理总结 AOF持久化AOF配置示例详细AOF文件重写RDB与AOF对比 3. Redis主从 - 并发能力问题主从架构搭建主从架构示例详细集群架构准备实例和配置启动开启主从关系测试 主从数据同步原理主从的全量同步原理简述全量同步的流程 主从的增量同步原理主从数据同步优化点 总结 4. Redis哨兵 - 故障恢复问题哨兵的作用和原理服务状态监控选举新的master如何实现故障转移总结 搭建哨兵集群详细集群结构准备实例和配置准备sentinel-cluster文件夹启动哨兵模拟主节点宕机测试哨兵是否生效重启原来的主节点 RedisTemplate的哨兵模式配置步骤哨兵配置示例详细引入依赖配置文件HelloControllerRedisDemoApplication测试 5. Redis分片集群 - 存储能力问题分片集群结构搭建分片集群示例详细集群结构准备实例和配置启动各节点创建集群 散列插槽总结 集群伸缩添加集群节点分片插槽示例详细 故障转移自动故障转移自动故障转移示例详细 手动故障转移手动故障转移示例详细 RedisTemplate访问分片集群配置步骤配置示例引入依赖配置文件HelloControllerRedisDemoApplication测试读写分离分片测试宕机测试 【redis学习篇】主从哨兵集群架构详解
redis搭建集群模式、Cluster模式6节点3主3从集群模式添加删除节点
【已解决】redis集群创建的时候一直卡在Waiting for the cluster to join …上、一直没有反应
redis专栏
解决 阿里云 搭建redis集群 ip变成内网
解决云服务器搭建redis集群, 域名解析成内网ip
06-redis集群模式(中) 项目测试的云服务ip变内网等(解决大多数问题)
spring-data-example github代码示例代码
1. 单点Redis的问题 数据丢失问题
Redis是内存存储服务重启可能会丢失数据
并发能力问题
单节点Redis并发能力虽然不错但也无法满足如618这样的高并发场景
故障恢复问题
如果Redis宕机则服务不可用需要一种自动的故障恢复手段
存储能力问题
Redis基于内存单节点能存储的数据量难以满足海量数据需求
2. Redis持久化 - 数据丢失问题
RDB持久化
RDB全称Redis Database Backup fileRedis数据备份文件也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后从磁盘读取快照文件恢复数据。
快照文件称为RDB文件默认是保存在当前运行目录。 save命令由于redis是单线程执行的使用此save命令时主进程会阻塞其它的命令而将数据持久化到磁盘的耗时比较久等到save命令结束主进程才能执行其它命令。不推荐使用此命令它通常用在Redis停机时使用。
bgsave命令后台异步执行它会开启子进程执行RDB避免主进程收到影响推荐使用该命令作RDB。
Redis停机时会自动执行一次RDB通过redis-cli连接上redis服务之后输入shutdown命令即可让redis服务停止或者在redis未开启以守护模式运行时通过ctrlc停止运行时会自动执行一次RDB。
linux单机安装Redis步骤
首先需要安装Redis所需要的依赖
yum install -y gcc tcl然后将课前资料提供的Redis安装包上传到虚拟机的任意目录 例如我放到了/tmp目录 解压缩
tar -xvf redis-6.2.4.tar.gz解压后 进入redis目录
cd redis-6.2.4运行编译命令
make make install如果没有出错应该就安装成功了redis的默认安装位置是/usr/local/bin在此/usr/local/bin目录下有redis-server、redis-cli、redis-benchmark、redis-sentinel等可执行文件同时在redis-6.2.4目录下有redis.conf和sentinel.conf配置文件同时在redis-6.2.4目录下的是src目录中也有redis-server、redis-cli、redis-benchmark、redis-sentinel等可执行文件。
然后修改redis.conf文件中的一些配置
# 绑定地址默认是127.0.0.1会导致只能在本地访问。修改为0.0.0.0则可以在任意IP访问
bind 0.0.0.0# 数据库数量设置为1
databases 1启动Redis
redis-server redis.conf # 使用redis-server命令启动redis, 并指定配置文件; 其中redis-server命令可在任意目录下执行停止redis服务
redis-cli shutdown # 其中redis-cli命令可在任意目录下执行RDB持久化与恢复示例详细
按照【单机安装Redis步骤】中的步骤安装好redis后
在/usr/local/bin目录下有redis-server、redis-cli、redis-benchmark、redis-sentinel等可执行文件并且在/usr/local/redis6/redis6.2.4/src目录下也有这些可执行文件在/usr/local/redis6/redis6.2.4/src下有redis.conf和sentinel.conf配置文件。
这里主要是说明安装情况
在如上安装好redis之后这里演示下在关闭redis服务时redis会自动执行RDB的案例
现在切换/usr/local/redis6/redis-6.2.4目录下不是必须在这个目录在其它目录也可以执行redis-server命令 使用redis-server ./redis.conf 命令来指定对应的配置文件启动redis服务redis开始接收连接 现在开启另外1个窗口使用set num 123来保存1条数据到redis内存中然后发出shutdown的命令让redis关闭服务此时redis服务会自动做1次RDB操作将内存中的数据持久化到dump.rdb文件中此dump.rdb文件默认会生成在运行redis-server命令时所在的目录中这里在/usr/local/redis6/redis-6.2.4目录下 现在/usr/local/redis6/redis-6.2.4目录下继续在重新启动redis服务查看前面通过RDB持久化的文件是否恢复到内存当中这里就没有指定redis.conf了也可以指定对应的配置文件 在另外1个窗口使用redis-cli连接上redis服务查看数据发现数据没有丢失说明redis能够从持久化的文件恢复到内存中 RDB机制
上面案例演示了在redis服务关闭时会自动执行RDB命令将内存中的数据持久化到磁盘中。但是假设redis运行过程中突然宕机了此时还没持久化到磁盘中那么在存储在redis内存中的数据将会全部丢失所以redis应该要有一套自动持久化的机制。
Redis内部有触发RDB的机制可以在redis.conf文件中找到这3个配置默认是被注释的默认情况下RDB是开启的格式如下 RDB的其它配置也可以在redis.conf文件中设置 配置的含义就是 在指定的一段时间内有指定数量的key被修改了那么就执行1次RDB操作将内存中的数据持久化到指定的目录下的指定的文件中。当然redis启动时也会从这个指定的目录下查找这个指定的文件加载到内存中。
当有了RDB后即使不关闭redis服务也能通过配置将redis内存中的数据持久化到磁盘上但是它会每隔一段时间才会执行RDB操作。如果在某段时间内尚未执行RDB时此时宕机了那么这段时间内的数据就丢失了。所以可能会想着把间隔时间设置的尽可能短但如果间隔时间很短执行RDB的操作就太频繁了影响redis的性能。所以使用默认的就好了。
RDB配置示例
说明5s内如果有1个key发生变化那么持久化内存钟的数据到指定的文件中。其中修改redis.conf文件部分如下
# 5s内如果有1个key发生变化, 则触发1次RDB持久化如果需要禁用RDB, 则配置: save 即可
save 5 1# 指定持久化文件的名字
dbfilename test.data # 指定RDB持久化文件的所在目录
dir ./my_data_dir在/usr/local/redis6/redis-6.2.4下创建rdb_test目录并在此rdb_test目录下创建my_data_dir文件夹用于存放持久化文件。修改号redis.conf配置文件后使用该配置文件启动redis。 redis启动后使用redis-cli连接上redis服务并向redis中存储2条数据然后观察redis服务的控制台上观察输出看到了redis执行持久化的日志关闭redis后查看my_data_dir文件夹看到了test.data数据持久化文件 再次使用指定的配置文件启动redis使用redis-cli再次查询数据发现数据已恢复 RDB的fork原理
bgsave开始时会fork主进程得到子进程子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。bgsave是异步执行持久化的对主进程几乎零阻塞零阻塞的原因在于主进程在执行fork得到子进程时此fork操作会阻塞此时无法处理客户端请求
fork采用的是copy-on-write技术
当主进程执行读操作时访问共享内存当主进程执行写操作时则会拷贝一份数据执行写操作当此时针对很多key写操作时就相当于要拷贝大量数据作为副本此时就需要事先考虑给redis预留足够的空间 总结
RDB方式bgsave的基本流程
fork主进程得到一个子进程共享内存空间子进程读取内存数据并异步写入新的RDB文件用新RDB文件替换旧的RDB文件。
RDB会在什么时候执行save 60 1000代表什么含义
默认是服务停止时。代表60秒内至少执行1000次修改则触发RDB
RDB的缺点
RDB执行间隔时间长两次RDB之间写入数据有丢失的风险fork子进程、压缩、写出RDB文件都比较耗时
AOF持久化
AOF全称为Append Only File追加文件。Redis处理的每一个写命令都会记录在AOF文件可以看做是命令日志文件。 AOF默认是关闭的需要修改redis.conf配置文件来开启AOF AOF的命令记录的频率也可以通过redis.conf文件来配 配置项刷盘时机优点缺点Always同步刷盘redis接收到命令后使用命令操作完内存后把此命令写到AOF文件磁盘中此时主进程是阻塞的等到写完AOF才返回给用户主进程再处理其它请求可靠性高几乎不丢数据性能影响大everysec每秒刷盘redis接收到命令后使用命令操作完内存后把此命令写到内存缓冲区中写完缓冲区后主进程立即返回。1s后再通过异步的方式将缓冲区中的数据写到AOF文件磁盘中因为主进程是面对内存缓冲区中的读写所以效率高但是如果在写入的过程中宕机了那么就会丢失这1s内的所有操作。它是默认方案。性能适中最多丢失1秒数据no操作系统控制由操作系统决定可能频率会比较低性能最好可靠性较差可能丢失大量数据
AOF配置示例详细
说明AOF会记录每条执行的redis命令到aof文件中这里关闭了rdb机制开启了aof机制
# 关闭RDB机制
save # aof文件将会保存在此目录, 启动时会读取该目录下的aof文件与RDB持久化文件所保存的目录相同
dir ./aof_data_dir# 开启aof
appendonly yes# aof文件名
appendfilename my_aof.data# aof刷盘策略, 默认就是everysec, 不需要修改
appendfsync everysec在/usr/local/redis6/redis-6.2.4下创建aof_test目录并在此aof_test目录下创建aof_data_dir文件夹用于存放aof文件。修改好redis.conf配置文件后使用该配置文件启动redis。 使用redis-cli客户端连接上redis服务并且保存1条数据然后退出redis-cli客户端就可以在指定的目录下看到保存的aof文件了并且这里看到了aof文件的内容aof文件确实记录了每条redis命令 关闭redis时redis也会执行1次aof 重新启动redis服务会自动加载aof文件然后使用redis-cli客户端连接上redis服务查询redis服务关闭之前所保存的数据能够查询到说明aof文件被加载了 AOF文件重写
因为是记录命令AOF文件会比RDB文件大的多。而且AOF会记录对同一个key的多次写操作但只有最后一次写操作才有意义。通过执行bgrewriteaof命令可以让AOF文件执行重写功能用最少的命令达到相同效果此命令为异步执行他会让aof文件变小并对内容作编码处理。 Redis也会在触发阈值时自动去重写AOF文件。阈值也可以在redis.conf中配置 RDB与AOF对比
RDB和AOF各有自己的优缺点如果对数据安全性要求较高在实际开发中往往会结合两者来使用。
特点RDBAOF持久化方式定时对整个内存做快照记录每一次执行的命令数据完整性不完整两次备份之间会丢失相对完整取决于刷盘策略文件大小会有压缩文件体积小记录命令文件体积很大宕机恢复速度很快慢数据恢复优先级低因为数据完整性不如AOF高因为数据完整性更高系统资源占用高大量CPU和内存消耗低主要是磁盘IO资源但AOF重写时会占用大量CPU和内存资源使用场景可以容忍数分钟的数据丢失追求更快的启动速度对数据安全性要求较高常见 RDB与AOF数据恢复优先级当目录下同时存在AOF与RDB文件时会优先使用AOF文件来恢复数据因为AOF文件数据更加完整而RDB会丢失从上次备份的数据后到发生故障时这段时间内的数据。所以RDB更适合作为一种数据备份的手段。 AOF操作是异步的
3. Redis主从 - 并发能力问题
主从架构
单节点Redis的并发能力是有上限的要进一步提高Redis的并发能力就需要搭建主从集群而不是负载均衡的那种集群实现读写分离因为redis查询操作多增删改比较少所以需要更多的处理读的压力实现读写分离提高读的并发能力。 搭建主从架构示例详细
集群架构
我们搭建的主从集群结构如图 共包含三个节点一个主节点两个从节点。这里我们会在同一台虚拟机中开启3个redis实例模拟主从集群信息如下
IPPORT角色172.17.23.2347001master172.17.23.2347002slave172.17.23.2347003slave
准备实例和配置
要在同一台虚拟机开启3个实例必须准备三份不同的配置文件和目录配置文件所在目录也就是工作目录。
1创建目录
我们在/usr/local/redis6/redis-6.2.4/master-slave-cluster目录下创建三个文件夹名字分别叫redis7001、redis7002、redis7003和1个最初的redis.conf配置文件未作任何修改 修改redis.conf配置文件将其中的持久化模式改为默认的RDB模式AOF保持关闭状态配置bind允许远程连接虚拟机本身有多个IP为了避免将来混乱我们需要在redis.conf文件中指定每一个实例的绑定ip信息。然后将此redis.conf分别拷贝到redis7001、redis7002、redis7003中然后分别修改他们对应的端口为700170027003。
# 开启RDB
# save
save 3600 1
save 300 100
save 60 10000# 关闭AOF
appendonly no# 允许远程连接不设置此配置, 会无法同步
bind 0.0.0.0# 虚拟机本身有多个IP为了避免将来混乱我们需要在redis.conf文件中指定每一个实例的绑定ip信息
replica-announce-ip 172.17.23.234# 这个目录在本示例中为了方便就不改了, 但是注意启动的时候, 需要到对应的目录下去启动, 否则rdb生成的文件会在redis-server的运行目录下
dir ./# 端口: redis7001、redis7002、redis7003中然后分别修改他们对应的端口为700170027003
port 7001 # 这里以7001为例启动
分别在redis7001目录下启动7001redis7002目录下7002redis7003目录下7003注意运行redis-server命令的目录因为我们的dir配置的是./ 开启主从关系
现在三个实例还没有任何关系要配置主从可以使用replicaof 或者slaveof5.0以前命令。
有临时和永久两种模式 修改配置文件永久生效 在redis.conf中添加一行配置slaveof masterip masterport 使用redis-cli客户端连接到redis服务执行slaveof命令重启后失效 slaveof masterip masterport注意在5.0以后新增命令replicaof与salveof效果一致。
搭建完主从之后可以连接上任意一个节点通过info replication命令查看主从集群状态
这里让7002成为7001的slave即让7002成为7001的从节点执行该命令后就会把7001主节点的数据同步过来 让7003成为7001的slave并且从节点只能读取数据不能够写入数据只有主节点才能写入数据 测试
在主节点中写入数据再分别从7002、7003从节点中读取到了数据证明主从数据同步成功了。可以执行info replication查看主从集群状态。 主从数据同步原理
主从的全量同步原理
主从第一次同步是全量复制 master如何判断slave是不是第一次来同步数据这里会用到两个很重要的概念
Replication Id简称replid是数据集的标记id一致则说明是同一数据集。每一个master都有唯一的replidslave则会继承master节点的replidoffset偏移量随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset说明slave数据落后于master需要更新。
因此slave做数据同步必须向master声明自己的replication id 和offsetmaster才可以判断到底需要同步哪些数据
全量同步过程从节点将自己的replication id发给主节点主节点判断此replication id是否与自己的replication id是否一致如果replication id不一致说明该从节点是第一次来主节点需要执行bgsave命令来做RDB保存起来然后将自己的全量数据和offset同步到该从节点如果replication id一致说明从节点之前已经来过了做过了全量同步了并且从节点将offset也发过来了因此主节点就可以从offset得知从节点的同步进度因此主节点就将offset后面的数据发过去给从节点
简述全量同步的流程 第1步slave与master建立连接 第2步slave节点请求增量同步 第3步master节点判断replid发现不一致拒绝增量同步 第4步master将完整内存数据生成RDB发送RDB到slave 第5步slave清空本地数据加载master的RDB 第6步master将RDB期间的命令记录在repl_baklog并持续将log中的命令发送给slave 第7步slave执行接收到的命令保持与master之间的同步
主从的增量同步原理
主从第一次同步是全量同步但如果slave重启后同步则执行增量同步。
增量同步过程从节点重启后依然需要携带自己的replid和offset向主节点请求同步数据主节点收到该节点发过来的replid后与自己的replid比较发现一致说明不是第一次来同步的因此就查看该节点发过来的offset查看该节点之前的同步进度然后从repl_baklog中读取大于此offset的命令发送给从节点去同步。如果主节点这边检测到该未同步的数据已经被覆盖了那么就会要求该节点做全量同步。 repl_baklog大小有上限写满后会覆盖最早的数据。如果slave断开时间过久导致尚未备份的数据被覆盖则无法基于log做增量同步此时只能再次全量同步。
主从数据同步优化点
可以从以下几个方面来优化Redis主从就集群 在master中配置repl-diskless-sync yes启用无磁盘复制避免全量同步时的磁盘IO需要网络带宽足够大。 Redis单节点上的内存占用不要太大减少RDB导致的过多磁盘IO 适当提高repl_baklog的大小发现slave宕机时尽快实现故障恢复尽可能避免全量同步 限制一个master上的slave节点数量如果实在是太多slave则可以采用主-从-从链式结构减少master压力
总结
简述全量同步和增量同步区别
全量同步master将完整内存数据生成RDB发送RDB到slave。后续命令则记录在repl_baklog逐个发送给slave。增量同步slave提交自己的offset到mastermaster获取repl_baklog中从offset之后的命令给slave
什么时候执行全量同步
slave节点第一次连接master节点时slave节点断开时间太久repl_baklog中的offset已经被覆盖时
什么时候执行增量同步
slave节点断开又恢复并且在repl_baklog中能找到offset时
4. Redis哨兵 - 故障恢复问题
slave节点宕机恢复后可以找master节点同步数据那master节点宕机怎么办如果master宕机了那么redis集群仅靠从节点只能提供读的能力而无法提供写的能力即redis集群的写能力对外界不可用.因此需要有一种机制来监控redis的集群状态当主节点宕机了仍可以确保redis集群提供完整的读写能力从节点是有同步过主节点的数据的因此只需要把这个从节点改为主节点即可对外提供写的能力。
哨兵的作用和原理
Redis提供了哨兵Sentinel机制来实现主从集群的自动故障恢复。 哨兵的结构和作用如下
监控Sentinel 会不断检查您的master和slave是否按预期工作哨兵会监测redis集群中的所有节点状态自动故障恢复如果master故障Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主哨兵实现Redis集群的主从切换通知Sentinel充当Redis客户端的服务发现来源当集群发生故障转移时会将最新信息推送给Redis的客户端当redis集群中主节点宕机了发生主从切换后主节点的ip和端口信息已然发生变化因此哨兵需要让连接到该集群的redis客户端知道该往新的主节点写数据去其它从节点读数据
服务状态监控
Sentinel基于心跳机制监测服务状态每隔1秒向集群的每个实例发送ping命令
主观下线如果某sentinel节点发现某实例未在规定时间响应则认为该实例主观下线。客观下线若超过指定数量quorum这个在redis.conf中可配置的sentinel都认为该实例主观下线则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。 选举新的master
一旦发现master故障sentinel需要在salve中选择一个作为新的master选择依据是这样的
首先会判断slave节点与master节点断开时间长短如果超过指定值down-after-milliseconds * 10这个在redis.conf中可配置不配置的话会使用默认值则会排除该slave节点因为断开时间越长就越可能与原master节点的数据的差异越大然后判断slave节点的slave-priority值这个在redis.conf中可配置越小优先级越高如果是0则永不参与选举如果slave-prority一样则判断slave节点的offset值越大说明数据越新优先级越高offset其实就衡量了从节点与原主节点的数据同步的进度最后是判断slave节点的运行id大小越小优先级越高。
如何实现故障转移
当选中了其中一个slave为新的master后例如slave1故障的转移的步骤如下
sentinel给备选的slave1节点发送slaveof no one命令让该节点成为mastersentinel给所有其它slave发送slaveof 192.168.150.101 7002 命令让这些slave成为新master的从节点开始从新的master上同步数据。最后sentinel将故障节点标记为slave会直接改这个故障节点的配置文件写上slaveof 新的主节点ip 新的主节点port当故障节点恢复后会自动成为新的master的slave节点 总结
Sentinel的三个作用是什么
监控故障转移通知
Sentinel如何判断一个redis实例是否健康
每隔1秒发送一次ping命令如果超过一定时间没有相向则认为是主观下线如果大多数sentinel都认为实例主观下线则判定服务下线
故障转移步骤有哪些
首先选定一个slave作为新的master执行slaveof no one然后让所有节点都执行slaveof 新master修改故障节点配置添加slaveof 新master
搭建哨兵集群详细
集群结构
在前面我们在/usr/local/redis6/redis-6.2.4/master-slave-cluster目录下创建了3个文件夹redis7001、redis7002、redis7003并且分别在这3个文件夹下创建了redis.conf文件并且分别在这3个文件夹下启动了并且让7002和7003成为了7001的从节点形成了1个redis集群。现在在这个基础之上搭建1个sentinel集群来监控这个redis集群的状态。
这里我们搭建一个三节点形成的Sentinel集群来监管之前的Redis主从集群。如图 三个sentinel实例信息如下
节点IPPORTs1172.17.23.23427001s2172.17.23.23427002s3172.17.23.23427003
准备实例和配置
准备sentinel-cluster文件夹
在/usr/local/redis6/redis-6.2.4目录下创建sentinel-cluster文件夹并且在该sentinel-cluster文件夹下创建sentinel27001、sentinel27002、sentinel27003这3个文件夹这3个文件夹用来分别启动redis的sentinel哨兵。
在sentinel.conf文件添加下面的内容
# 当前sentinel实例的端口
port 27001# 虚拟机本身有多个IP为了避免将来混乱我们需要在redis.conf文件中指定每一个实例的绑定ip信息
sentinel announce-ip 172.17.23.234# 指定主节点信息这里监控的是master, 从master上可以知道该master下所有的slave信息, 因此能监控整个redis集群
sentinel monitor mymaster 172.17.23.234 7001 2# slave与master断开最大超时时间
sentinel down-after-milliseconds mymaster 5000# slave故障恢复超时时间
sentinel failover-timeout mymaster 60000# 哨兵存放数据的工作目录这里使用的是相对于redis-sentinel命令的运行目录, 所以redis-sentinel命令是在各自的目录下运行的
dir ./解读
port 27001是当前sentinel实例的端口sentinel monitor mymaster 172.17.23.234 7001 2指定主节点信息 mymaster主节点名称自定义任意写172.17.23.234主节点的ip和端口2选举master时的quorum值超过quarunm数量的sentinel认为主节点主观下线了那么该master就是客观下线了
并且此时我们的redis的3个服务是正在运行的 启动哨兵
将/usr/local/redis6/redis-6.2.4/sentinel-cluster文件夹下的sentinel.conf配置文件拷贝到sentinel27001、sentinel27002、sentinel27003文件夹下并修改为各自所对应的哨兵端口然后在各自的目录下使用各自的sentinel.conf配置文件使用redis-sentinel ./sentinel.conf命令分别启动这3个哨兵 模拟主节点宕机
现在连接上7001这台redis执行shutdown即关闭7001的服务。然后观察哨兵的日志发现发生了故障转移并且选举了7002作为新的master并且会修改700170027003的配置文件刚开始启动这3个redis服务时是使用redis命令来临时指定主节点的现在哨兵发现7001关闭后是直接改了700170027003的配置文件。 测试哨兵是否生效
连接上7002测试是否能够使用set命令能使用set命令说明它是主节点而7003不能使用set命令说明它是从节点。并且7003能够拿到7002设置的数据证明主从数据同步成功 重启原来的主节点
重新启动7001查看7001的节点信息发现原来的7001主节点成为了新主节点7002的从节点并且7001请求同步7002主节点的数据 RedisTemplate的哨兵模式
在Sentinel集群监管下的Redis主从集群其节点会因为自动故障转移而发生主从切换的变化Redis的客户端必须感知这种变化及时更新连接信息因为Redis主从集群中实现了读写分离只有主节点能读写从节点只能读。Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动切换。
前面提到哨兵的1个作用是通知也就是当使用sentinel监控redis主从集群时当redis主从集群中的主节点发生宕机发生主从切换时哨兵负责选举出新的主节点并让其它节点成为从节点并且哨兵需要通知给连接redis服务的客户端主节点的ip和端口信息已然发生变化。
配置步骤
1、在pom文件中引入redis的starter依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId
/dependency2、然后在配置文件application.yml中指定sentinel相关信息
注意这里配置的不是redis集群的地址而是sentinel的地址在使用sentinel监控redis集群时redis集群的主节点宕机时会发生主从切换主节点地址会发生变更所以主节点地址不能写死所以不需要知道redis集群的地址而只要知道sentinel的地址即可。基于哨兵来做对redis服务的发现因此下面配置的就是哨兵的地址了。因为哨兵通过监控哨兵配置文件里配置的的redis主节点就能知道所有redis从节点的信息。
spring:redis:sentinel:master: mymaster # 指定master名称哨兵配置文件设置的集群名称nodes: # 指定redis-sentinel集群信息- 172.17.23.234:27001- 172.17.23.234:27002- 172.17.23.234:270033、配置主从读写分离
Bean
public LettuceClientConfigurationBuilderCustomizer configurationBuilderCustomizer() {return configBuilder - configBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}这里的ReadFrom是配置Redis的读取策略是一个枚举包括下面选择
MASTER从主节点读取MASTER_PREFERRED优先从master节点读取master不可用才读取replicaREPLICA从slavereplica节点读取REPLICA _PREFERRED优先从slavereplica节点读取所有的slave都不可用才读取master推荐
哨兵配置示例详细
按照之前的步骤搭建redis主从集群7002是主节点7001和7003是从节点并且搭建1个sentinel集群2700127002和27003这3个哨兵来监控这个redis集群。在redis主从集群中只有主节点能够读取和写入数据从节点只能读取数据即读写分离。当redis主从集群中的主节点发生宕机时哨兵就会从redis主从集群中选举新的主节点并完成主从切换完成主从切换后哨兵就要将新的主从信息通知给连接该redis集群的客户端。
这里在测试前按照之前的配置只修改了ip配置为公网ip然后重新启动了redis主从集群和sentinel哨兵集群用于测试。
引入依赖
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.9.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcn.itcast/groupIdartifactIdredis-demo/artifactIdversion0.0.1-SNAPSHOT/versionnameredis-demo/namedescriptionDemo project for Spring Boot/descriptionpropertiesjava.version1.8/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scopeexclusionsexclusiongroupIdorg.junit.vintage/groupIdartifactIdjunit-vintage-engine/artifactId/exclusion/exclusions/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdconfigurationexcludesexcludegroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/exclude/excludes/configuration/plugin/plugins/build/project
配置文件
logging:level:io.lettuce.core: debugpattern:dateformat: MM-dd HH:mm:ss:SSS
spring:redis:sentinel:master: mymasternodes:- 119.23.61.24:27001- 119.23.61.24:27002- 119.23.61.24:27003HelloController
RestController
public class HelloController {Autowiredprivate StringRedisTemplate redisTemplate;GetMapping(/get/{key})public String hi(PathVariable String key) {return redisTemplate.opsForValue().get(key);}GetMapping(/set/{key}/{value})public String hi(PathVariable String key, PathVariable String value) {redisTemplate.opsForValue().set(key, value);return success;}}RedisDemoApplication
SpringBootApplication
public class RedisDemoApplication {public static void main(String[] args) {SpringApplication.run(RedisDemoApplication.class, args);}/* 配置优先从 从节点读取数据而只能是主节点才能写入 */Beanpublic LettuceClientConfigurationBuilderCustomizer configurationBuilderCustomizer() {return configBuilder - configBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);}
}
测试
访问http://localhost:8080/set/name/zzhua写入数据
再访问http://localhost:8080/get/name读取数据。
日志输出如下 此时关闭7002主节点的服务7003切换为主节点。此时客户端收到了通知日志输出如下 访问http://localhost:8080/set/a/b写入数据
再访问http://localhost:8080/get/a读取数据。
日志输出如下可以看到新的主节点负责写入数据了 5. Redis分片集群 - 存储能力问题
分片集群结构
主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决
海量数据存储问题单节点的redis服务的内存不能过高如果过高会导致RDB持久化或者全量同步时会导致大量的IO致使性能下降redis服务内存既然不能过高但是如果有海量数据需要存储又会无法应对高并发写的问题主从集群可以应对高并发读的问题但如果此时有大量写的请求需要处理那么就应对不了高并发写的问题了
使用分片集群可以解决上述问题分片集群特征
集群中有多个master每个master保存不同数据此时redis服务存储上限就是各master存储量之和应对了海量数据存储的问题写入时也可从多个master中选择写的能力也提升了应对了高并发写的问题每个master都可以有多个slave节点应对了高并发读的问题master之间通过ping监测彼此健康状态此时不需要哨兵了master与master之间会互相监测就起到了哨兵的作用客户端请求可以访问集群任意节点最终都会被转发到正确节点当某个master宕机了此时就会发生主从切换如果没有哨兵该怎么通知客户端呢其实master与master之间会做自动的路由客户端可以访问任意1个master节点它都会将请求转发到正确的节点因此就不再需要哨兵了但是又具备了哨兵的所有功能 搭建分片集群示例详细
集群结构
分片集群需要的节点数量较多这里我们搭建一个最小的分片集群包含3个master节点每个master包含一个slave节点结构如下 这里我们会在同一台虚拟机中开启6个redis实例模拟分片集群信息如下
IPPORT角色119.23.61.247001master119.23.61.247002master119.23.61.247003master119.23.61.248001slave119.23.61.248002slave119.23.61.248003slave
准备实例和配置
在/usr/local/redis6/redis-6.2.4下创建slice-redis-cluster目录并在这个slice-redis-cluster目录下创建node7001、node7002、node7003、node8001、node8002、node8003并创建1个redis.conf的配置文件文件内容如下。
# 端口指定各自的端口
port 7001# 开启集群功能
cluster-enabled yes# 集群的配置文件名称不需要我们创建由redis自己维护指定各自的集群配置文件, 记住千万不需要创建; 后面由于返回给java客户端的ip是内网ip, 所以生成node.conf之后, 将所有节点的内网ip改为公网ip然后重启整个集群
cluster-config-file /usr/local/redis6/redis-6.2.4/slice-redis-cluster/node7001/node.conf# 节点心跳失败的超时时间集群之间互相发送心跳, 如果5s之内没有收到, 那么认为宕机了
cluster-node-timeout 5000# 持久化文件存放目录指定各自的持久化目录
dir /usr/local/redis6/redis-6.2.4/slice-redis-cluster/node7001# 绑定地址设置为0.0.0.0, 则任何人都能访问
bind 0.0.0.0# 让redis后台运行以守护进程运行
daemonize yes# 注册的实例ip
replica-announce-ip 119.23.61.24# 保护模式不需要做用户名、密码的校验了
protected-mode no# 数据库数量
databases 1# 日志指定各自的日志文件的位置
logfile /usr/local/redis6/redis-6.2.4/slice-redis-cluster/node7001/run7001.log将redis.conf文件拷贝到这6个节点文件夹中并且修改对应的端口和其它配置就是全局替换比如将所有的7001改为7002即可vim中的操作命令如下依次修改即可
:%s/7001/7002/g现在/usr/local/redis6/redis-6.2.4/slice-redis-cluster目录下有6个节点文件夹和1个redis.conf配置文件每个节点文件夹下仅有1个redis.conf配置文件并且已经修改了配置。
启动各节点
因为已经配置了后台启动模式所以可以直接启动服务分别启动各个redis节点 查看redis是否都正常启动了如果要关闭所有的redis此时可执行如果要关闭所有redis的进程可以执行命令ps -ef | grep redis | awk {print $2} | xargs kill或 者使用redis-cli -p 端口挨个连接上去使用shutdown关闭即可 创建集群
虽然服务启动了但是目前每个服务之间都是独立的没有任何关联。
我们需要执行命令来创建集群在Redis5.0之前创建集群比较麻烦5.0之后集群管理命令都集成到了redis-cli中。
1Redis5.0之前
Redis5.0之前集群命令都是用redis安装包下的src/redis-trib.rb来实现的。因为redis-trib.rb是有ruby语言编写的所以需要安装ruby环境。
# 安装依赖
yum -y install zlib ruby rubygems
gem install redis然后通过命令来管理集群
# 进入redis的src目录
cd /tmp/redis-6.2.4/src
# 创建集群
./redis-trib.rb create --replicas 1 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:80032Redis5.0以后
我们使用的是Redis6.2.4版本集群管理以及集成到了redis-cli中格式如下
redis-cli --cluster create --cluster-replicas 1 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命令说明
redis-cli --cluster或者./redis-trib.rb代表集群操作命令create代表是创建集群--replicas 1或者--cluster-replicas 1 指定集群中每个master的副本个数为1此时节点总数 ÷ (replicas 1) 得到的就是master的数量。因此节点列表中的前n个就是master其它节点都是slave节点随机分配到不同master
实战操作如下
这里我们使用redis-cli --cluster命令来创建集群可以使用redis-cli --cluster help来查看帮助
使用如下命令创建集群命令的解释在上方已详述这里有个坑如果使用阿里云执行该命令时需要先放开7001、7002、7003、8001、8002、8003还有17001、17002、17003、18001、18002、18003其实就是节点的端口加上10000所得到的端口也得放开否则会一直卡在Waiting for the cluster to join那里不动这一点可以从成功启动后的下方日志可以看到
redis-cli --cluster create --cluster-replicas 1 119.23.61.24:7001 119.23.61.24:7002 119.23.61.24:7003 119.23.61.24:8001 119.23.61.24:8002 119.23.61.24:8003# 忽略下面的这些命令, 只是实战的时候, 出了些问题, 就记录下这些命令方便复制
ps -ef | grep redis | awk {print $2} | xargs kill
ps -ef|grep redis
rm -rf node700*/*.rdb node700*/node.conf node700*/*.log node800*/*.rdb node8*/node.conf node800*/*.log
redis-cli -p 7001 cluster nodes
redis-cli --cluster add-node 119.23.61.24:7004 119.23.61.24:7001创建集群后通过命令可以查看集群状态在其中可以看到不同范围插槽分布在哪些节点上
redis-cli -p 7001 cluster nodes散列插槽
Redis会把每一个master节点映射到0~16383共16384个插槽hash slot上查看集群信息时就能看到 数据key不是与节点绑定而是与插槽绑定。redis会根据key的有效部分计算插槽值分两种情况
key中包含{}且“{}”中至少包含1个字符“{}”中的部分是有效部分key中不包含“{}”整个key都是有效部分
例如key是num那么就根据num计算如果是{itcast}num则根据itcast计算。计算方式是利用CRC16算法得到一个hash值然后对16384取余得到的结果就是slot值。
为什么这样设计redis的主节点可能会出现宕机的情况或者是集群扩容增加节点或者是集群伸缩删除的节点如果1个节点删除了或者宕机了那么在这个节点上的数据也就丢失了而如果数据是跟插槽绑定的此时当节点宕机时将此宕机节点的插槽转移到还存活的节点上去当集群扩容时将插槽进行转移这样数据跟着插槽走就一定能找到数据存储的位置。 总结
Redis如何判断某个key应该在哪个实例
将16384个插槽分配到不同的实例根据key的有效部分计算哈希值对16384取余余数作为插槽寻找插槽所在实例即可
如何将同一类数据固定的保存在同一个Redis实例因为不在同一个redis节点那么就需要重定向到其它节点去获取值
这一类数据使用相同的有效部分例如key都以{typeId}为前缀使用这种方法可以设置key的有效部分来控制存储数据时一定落在相同的节点
集群伸缩
redis-cli --cluster提供了很多操作集群的命令可以通过下面方式查看 比如添加节点的命令如果添加上–cluster-slave表示添加进去就是从节点并且–cluster-master-id表示作为哪个主节点的从节点如果不加这2个配置项表示加进去就是主节点 添加集群节点分片插槽示例详细
在/usr/local/redis6/redis-6.2.4/slice-redis-cluster目录下创建node7004文件夹并且在node7004文件夹下创建redis.conf配置的内容就直接复制《搭建分片集群示例》中的配置内容并且把里面的7001全改成7004即可。然后启动7004服务。 在前面《搭建分片集群示例》的基础上使用redis-cli --cluster add-node 119.23.61.24:7004 119.23.61.24:7001可以通过redis-cli --cluster help命令查看帮助文档将7004服务添加到集群作为1个新的master节点可以通过redis-cli --cluster help命令查看add-node命令的参数介绍了解如果不添加后续参数那么添加的就是master主节点也可以指定后续参数来添加为指定主节点的从节点其中第二个参数只需要填1个已知的节点即可。这里再通过redis-cli -p 7001 cluster nodes命令可以查看到集群的状态和各个主节点的插槽范围。 现在使用redis-cli -c -p 7001命令访问分片集群连接上7001服务可以看到num是在7001服务上并且是2756插槽上。现在把7001的前3000个插槽即0-2999范围内的插槽移到7004服务上去。现在可以使用redis-cli -p 7001 cluster nodes命令查看集群状态和各个主节点的插槽范围可以看到7004服务现在负责0-2999范围内的插槽。 如果插槽移动成功那么原先在7001服务上的num现在应该到了7004服务上了。从下面可以看到原本在7001服务的key现在重定向到了7004服务说明插槽移动成功。 现在在上面的基础上再从分片集群上移除掉7004这个节点 查看集群状态7004节点的插槽范围没有了都转移到了7001节点上了 故障转移
分片集群虽然没有哨兵但是也具有故障转移的功能。
自动故障转移
redis分片集群不需要哨兵当某个master宕机时会自动的完成主从切换。
当集群中有一个master宕机会发生什么呢
首先是该实例与其它实例失去连接然后是疑似宕机最后是确定下线自动提升一个slave为新的master
自动故障转移示例详细
下面在前面搭建的分片集群的基础上测试通过watch redis-cli -p 7001 cluster nodes开启集群监控然后关闭7002节点在监控板上可以看到7002fail了然后8001成为了master主节点。随后启动7002此时的7002成为了slave。这说明分片集群自动具有哨兵的功能能自动完成故障转移。这种情况发生在某个主节点意外宕机时。 现在的主节点是700170038001
手动故障转移
利用cluster failover命令可以手动让集群中的某个master宕机切换到执行cluster failover命令的这个slave节点这个slave将替换这个master而成为master主节点实现无感知的数据迁移。其流程如下 手动的Failover支持三种不同模式
缺省默认的流程如图1~6歩force省略了对offset的一致性校验省略了2、3步不管数据是否同步上来就替换比较暴力takeover直接执行第5歩忽略数据一致性、忽略master状态和其它master的意见更暴力
手动故障转移示例详细
在前面自动故障转移示例的基础上作如下测试
在操作之前查看集群状态现在的主节点是700170038001可以看出7002的主节点是xxx5cef很明显5cef是8001这个节点所以后续7002执行failover会替换掉8001成为主节点
连接上7002然后执行cluster failover现在的主节点是700170037002。原来的8001成为了从节点。后续作服务升级时就可以先让新的节点成为要替换的主节点的从节点然后让新的节点执行failover即可 同时可查看监控集群状态的变化如下 RedisTemplate访问分片集群
配置步骤
RedisTemplate底层同样基于lettuce实现了分片集群的支持而使用的步骤与哨兵模式基本一致 引入redis的starter依赖 配置分片集群地址 配置读写分离 与哨兵模式相比其中只有分片集群的配置方式略有差异如下 spring:redis:cluster:nodes: # 指定分片集群的每一个节点信息- 119.23.61.24:7001- 119.23.61.24:7002- 119.23.61.24:7003- 119.23.61.24:8001- 119.23.61.24:8002- 119.23.61.24:8003配置示例
引入依赖
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.9.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcn.itcast/groupIdartifactIdredis-demo/artifactIdversion0.0.1-SNAPSHOT/versionnameredis-demo/namedescriptionDemo project for Spring Boot/descriptionpropertiesjava.version1.8/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scopeexclusionsexclusiongroupIdorg.junit.vintage/groupIdartifactIdjunit-vintage-engine/artifactId/exclusion/exclusions/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdconfigurationexcludesexcludegroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/exclude/excludes/configuration/plugin/plugins/build/project
配置文件
logging:level:io.lettuce.core: debugpattern:dateformat: MM-dd HH:mm:ss:SSS
spring:redis:cluster:nodes: # 指定分片集群的每一个节点信息- 119.23.61.24:7001- 119.23.61.24:7002- 119.23.61.24:7003- 119.23.61.24:8001- 119.23.61.24:8002- 119.23.61.24:8003HelloController
RestController
public class HelloController {Autowiredprivate StringRedisTemplate redisTemplate;GetMapping(/get/{key})public String hi(PathVariable String key) {return redisTemplate.opsForValue().get(key);}GetMapping(/set/{key}/{value})public String hi(PathVariable String key, PathVariable String value) {redisTemplate.opsForValue().set(key, value);return success;}}RedisDemoApplication
SpringBootApplication
public class RedisDemoApplication {public static void main(String[] args) {SpringApplication.run(RedisDemoApplication.class, args);}/* 配置优先从 从节点读取数据而只能是主节点才能写入 */Beanpublic LettuceClientConfigurationBuilderCustomizer configurationBuilderCustomizer() {return configBuilder - configBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);}
}
测试
此前遇到问题阿里云搭建redis分片集群在客户端里获取的ip是阿里云的内网ip导致连接超时报错所以修改了所有节点的node.conf文件将其中的所有内网ip改为公网ip。然后重启整个集群。
测试前先查看下集群状态信息。 注意添加-c的命令来访问集群否则会报Moved…。下图说明了3个key所属插槽分别在不同的节点上 读写分离
访问http://localhost:8080/set/name/zzhua写入数据
再访问http://localhost:8080/get/name读取数据。
日志输出如下 分片测试
访问http://localhost:8080/set/a/b写入数据
再访问http://localhost:8080/get/a读取数据。
日志输出如下
不同的key根据其hash值分布在不同的插槽上面即实现了分片集群 宕机测试
现在的主从情况是8001主7002从、8002主7003从、8003主7001从现在让8003主节点挂掉 此时访问http://localhost:8080/get/num 能够获取到数据
而访问http://localhost:8080/set/num/9写入数据会报错
所以这个是不是得使用哨兵才能通知到客户端因为写入数据日志输出表明还是访问8003这个服务。