Redis 高级篇之分布式缓存

目录

单点Redis的问题

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

Redis数据持久化

RDB持久化

实现方式

命令

如何触发RDB

RDB原理——fork(复刻)

AOF持久化

记录命令频率的三种策略及性能对比

AOF文件重写命令(BGREWRITEAOF)

如何触发AOF

持久化方案两者之间区别

Redis主从集群

读写分离策略

实现主从数据同步

全量同步

增量同步

优化Redis主从集群

总结

Redis哨兵

哨兵的作用

1.监测 

2.自动故障恢复 

3.通知 

RedisTemplate的哨兵模式

Redis分片集群

分片集群特征

散列插槽

集群伸缩

故障转移

手动故障转移

RedisTemplate访问分片集群


单点Redis的问题

1.数据丢失问题 :Redis是内存存储 。重启服务可能会丢失数据

2.并发能力 :高并发场景下

3.故障恢复问题 :Redis宕机,服务就不可用

4.存储能力:单点存储的数据有限

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

1.Redis数据持久化

2.redis主从集群 读写分离 s

3. Redis哨兵,健康检测和自动恢复

4.分片集群,动态孔融

Redis数据持久化

两种方案实现数据持久化:RDB和AOF

RDB持久化

RDB文件称为快照文件。

实现方式

把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后,从磁盘读取快照文件来恢复数据。

命令

save :由Redis主进程来执行。Redis是单线程的因此是阻塞命令。

bgsave:开启一个子线程执行RDB,可以异步执行RDB,不影响Redis主进程执行,不会阻塞其他命令执行。

如何触发RDB

默认情况下,Redis正常停机(关机)时会执行一次RDB。但是突然宕机是来不及执行RDB。

使用bgsave语法:save <秒数> <key被修改次数>

RDB原理——fork(复刻)

bgsave开始时会fork(复刻)主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。
fork采用的是copy-on-write技术:
    - 当主进程执行读操作时,子进程访问共享内存;
    - 当主进程执行写操作时,则会拷贝一份数据,内存中多一份数据,主进程操作备份数据执行写操作。
注意

子进程共享主进程的内存数据指的是:主进程创建子进程,然后子进程复刻主进程中的页表。采用页表的映射方式就只用复制映射物理内存的页表即可,不需要复制物理内存中的实际内存数据,非常的快捷和节省内存空间。

AOF持久化

AOF全称为Append Only File(追加文件)。存储的是Redis的命令

AOF把每条Redis执行的命令都记录下来,读取AOF文件的时候从一个空的Redis开始执行记录的命令。

记录命令频率的三种策略及性能对比

appendfsync always:每执行一次写命令,立即记录到AOF文件。

appendfsync always:写命令执行完先放入AOF缓冲区,然后表示每隔1秒将缓冲区数据写到AOF文件,是默认方案。不需要主进程自己去执行写入磁盘操作,异步操作。

appendfsync no:写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。不需要主进程自己去执行写入磁盘操作,异步操作。

性能对比:

AOF文件重写命令(BGREWRITEAOF)

执行bgrewriteaof命令,让AOF文件执行重写功能。通过算法把AOF中记录的无效命令去除,最大化整合有效命令,同时做到最终结果一致,节省AOF文件占用空间。

如何触发AOF

Redis也会在触发阈值时自动去重写AOF文件。阈值也可以在redis.conf中配置
# AOF文件比上次文件 增长超过多少百分比则触发重写,默认为 100
auto-aof-rewrite-percentage 100
# AOF文件体积最小多大以上才触发重写,默认为 64mb
auto-aof-rewrite-min-size 64mb

持久化方案两者之间区别


1、RDB是完全的异步操作,AOF除了频率为appendfsync always情况外,也都是异步操作。
2、AOF和RDB同时存在的混合模式也是可以的,如果AOF和RDB同时存在的时候,RDB和AOF的写入互不干扰,但是读取的话,Redis会优先使用从AOF文件来还原数据库状态,如果AOF关闭状态时,则从RDB中恢复。
3、在Redis版本更新的计划中,计划把RDB和AOF两者融合为一种,因为RDB和AOF混合使用非常常见。
4、AOF的重写命令BGREWRITEAOF会占用大量CPU和内存资源。

Redis主从集群

读写分离策略

采用读写分离,而非负载均衡。对Redis的操作大部分都是读,写操作占少数,所以在集群中采用读写分离。

实现主从数据同步

全量同步

第一次主从同步是全量同步。

注意事项

1.master如何判断slave是不是第一次来同步数据?
    Replication Id:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave会继承master节点的replid。
    offset:偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。
    因此slave做数据同步,必须向master声明自己的replication id 和offset,master才可以判断到底需要同步哪些数据。

2.全量同步的流程:

        1、slave节点请求增量同步
        2、master节点判断replid,发现不一致,拒绝增量同步,向slave返回master的replid和offset让其记住
        3、master将完整内存数据生成RDB,发送RDB到slave
        4、slave清空本地数据,加载master的RDB
        5、master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave
        6、slave执行接收到的命令,保持与master之间的同步

增量同步

repl_backlog原理:

        全量同步时的repl_baklog文件是一个固定大小的数组,只不过数组是环形,也就是说角标到达数组末尾后,会再次从0开始读写,这样数组头部的数据就会被覆盖。
    repl_baklog中会记录Redis处理过的命令日志及offset,包括master当前的offset,和slave已经拷贝到的offset。

master怎么知道slave与自己的数据差异在哪里呢?

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

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

实现数据同步的完整流程

        1、slave第一次向master发送请求,发送的是增量同步请求,此时的replid和offset就是slave自身的,因为slave成为slave节点之前,自身就是一个master,即使它没有slave节点,所以replid和offset是它自身的。
        2、master收到slave的请求后,会先判断replid是否一致,如果是第一次,master会拒绝slave的增量请求,然后返回master的replid和offset给slave,让slave记住。同时master会使用bgsave开启新线程,向这个第一次来的slave进行RDB全量同步。
        3、由于RDB全量同步非常耗时,那么在这期间master数据会改变,期间的命令全都会被master记录在repl_baklog中,并修改自己的offset,然后等待RDB全量同步完成后,通过比较replid和offset,来进行增量同步(类似于AOF),然后发送repl_caklog的命令给slave让其执行。
        4、那么要是在发送repl_caklog期间,master又改变了,结果是依旧会被记录到repl_caklog中,然后比较replid和offset,再次然后发送repl_caklog的命令给slave让其执行。这是一个类似于监听的任务,只要不是第一次以后,都是借助replid和offset、repl_caklog来实现的增量同步。

优化Redis主从集群


   1、在master中配置repl-diskless-sync yes启用无磁盘复制,即直接从内存中往网络中发,避免全量同步时的磁盘IO。
    2、Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO。
    3、适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步。
    4、限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主-从-从链式结构,减少master压力。

总结

Redis哨兵

master节点宕机。只能进行读操作,不能写。可用性下降。

解决方案:选择一个slave作为master。

哨兵的作用

 检测集群的健康状况,及时选择新的master,保证系统的可用性。Sentinel也是集群。

1.监测 

Sentinel 不断检查master和slave是否按预期工作。判断节点是否健康。

Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:
    主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
    客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。quorum可以在redis.conf配置文件中进行设置。
    
总结:
    单个Sentinel节点认为某个Redis节点主观下线,可能不是真正的下线,网络阻塞也有可能。
    当多个(多个>=qourum)Sentinel节点认为某个Redis节点主观下线,此时达到客观下线要求,Sentinel哨兵认为该Redis节点真的下线了,会进行一系列操作。(如主节点下线就进行故障转移)

2.自动故障恢复 

2.1 选举新的master :Sentinel会将一个slave提升为master。

选举规则:
    1、首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds * 10)则会排除该slave节点
    2、然后判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举
    3、如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高
    4、最后是判断slave节点的运行id大小,越小优先级越高。
 

2.2 实现故障转移 :当故障实例恢复后也变为一个slave,也以新的master为主

当选中了其中一个slave为新的master后(例如slave1),故障的转移的步骤如下:
    1、sentinel给备选的slave1节点发送slaveof no one命令,让该节点成为master
    2、sentinel给所有其它slave发送slaveof (slave1的IP地址) 命令,让这些slave成为新master的从节点,开始从新的master上同步数据。
    3、最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点

3.通知 

        当集群发生故障转移时,会将最新信息推送给Redis的客户端。即作Redis为主节点、从节点的地址的通知者,发生变更后第一时间通知Redis客户端。


RedisTemplate的哨兵模式

步骤:

1.引入依赖

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

2.配置sentinel信息

spring:
  redis:
    sentinel:
      master: mymaster
      nodes:	# 下面是yaml的列表写法,也可以是以","分割的字符串
        - 192.168.182.160:27001
        - 192.168.182.160:27002
        - 192.168.182.160:27003
    # 要记得给出Redis实例的密码,Sentinel只是根据需要执行的命令类型返回给我们对应的节点地址信息而已,该节点的密码还是要我们自己给出的。
    # 没有密码可以不指定
    password: Redis实例密码		# 由于不能指定多个密码,所以这里主从节点密码必须一致

3.配置主从读写分离

@Bean
// 自定义Lettuce连接配置类,指定Redis集群的主从节点读写分离策略,这里也没有讲默认的策略是什么
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
    // lamda表达式写法
    return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}

总结

    读数据,simpleStringTemplate会连接所有的redis(主从节点)。读数据会任选一个节点读取数据;写数据,simpleStringTemplate选择master节点写入数据。

        Redis的客户端必须感知这种变化,及时更新连接信息。Spring的RedisTemplate底层利用lettuce实现了节点的感知和自动切换。哨兵进行主从切换之后,Java客户端要发现并获取最新的节点信息。得到最新的master。

Redis分片集群

主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:
    1、海量数据存储问题
    2、高并发写的问题
为解决上述问题使用分片集群

分片集群特征


    1、集群中有多个master,每个master保存不同数据(分段存储,通过哈希实现),类似于60g的数据,分别分配到3台20g内存的服务器)
    2、每个master都可以有多个slave节点
    3、master之间通过ping监测彼此健康状态(代替哨兵Sentinel。分片集群自身具备故障转移等功能,可以不需要哨兵了)
    4、客户端请求可以访问集群任意节点,最终都会被转发到正确节点(类似于路由的功能,通过插槽(hash slot)实现)
 

散列插槽

        Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上。

1.为什么数据的key与插槽绑定?

    数据key不是与节点绑定,而是与插槽绑定。当要操作一个Key时,都是先计算其插槽值(hash slot),然后去寻找插槽区间,再根据插槽区间寻找对应的节点,然后重定向到该节点,对Key进行操作。
    因为Redis节点会出现宕机、Redis集群后期可能会进行扩容节点等情况,都会导致Key直接与Redis节点进行绑定非常不稳定,而与插槽进行绑定,当节点出现宕机、集群扩容节点等情况,重新分配插槽区间给节点就好了,非常方便。

2.Redis如何计算计算插槽值?

redis会根据key的有效部分计算插槽值,分两种情况:
    1、key中包含"{}",且“{}”中至少包含1个字符,“{}”中的部分是有效部分
    2、key中不包含“{}”,整个key都是有效部分
例如:key是num,那么就根据num计算,如果是{itcast}num,则根据itcast计算。计算方式是利用CRC16(应该是哈希算法的一种)算法得到一个hash值,然后对16384取余,得到的结果就是slot值。 
 

3.使用redis-cli -c 命令连接Redis客户端,不加 -c 参数无法进行节点自动重定向。

总结

1.Redis如何判断某个key应该在哪个实例?
    将16384个插槽分配到不同的实例
    根据key的有效部分计算哈希值,对16384取余
    余数作为插槽,寻找插槽所在Redis实例即可 
2.如何将同一类数据固定的保存在同一个Redis实例?
    这一类数据使用相同的有效部分,例如key都以{typeId}为前缀
    如:商品包含手机、电脑、笔,那他们的key格式:{商品}商品id

集群伸缩

集群动态的增加或者删除节点。

Redis的伸缩功能命令 :redis-cli-cluster

添加节点

语法:

        redis -cli --cluster 新Redis节点的ip地址:新Redis节点的端口号 想要添加的集群中的某一个节点ip地址:端口号 [可选参数]

详细:new_host:new_port 就是新增的节点,必须指定,而后面的 existing_host:existing_port 从目标集群中随便取一个节点地址就可以了,不管是哪个都可以,因为这个节点起的作用就是,把新增节点的消息通知给集群中每一个节点。
可选参数:
    --cluster-slave:指定新增节点为从节点
    --cluster-master-id:指定新增节点的主节点

注意事项:
1、要是不指定上面两个可选参数(--cluster-slave、--cluster-master-id),那么新增节点默认就是作为集群的一个主节点。
2、默认情况下,新增节点作为主节点添加以后,不会自动重新分配插槽区间给新增节点的,需要我们手动从别的主节点那里去获取。

案例:

需求:向集群中添加一个新的master节点,并向其中存储 num = 10
    1、启动一个新的redis实例,端口为7004
    2、添加7004到之前的集群,并作为一个master节点
    3、给7004节点分配插槽,使得num这个key可以存储到7004实例

这里需要两个新的功能:
    1、添加一个节点到集群中
    2、将部分插槽分配到新插槽

    1、添加一个节点到集群中

·创建Redis实例

#创建文件夹
mkdir 7004
#拷贝配置文件
cp redis.conf 7004
#修改配置文件
sed -i s/6379/7004/g 7004/redis.conf
#启动Redis实例
redis-server 7004/redis.conf

·添加节点到Redis集群中

redis-cli --cluster add-node  192.168.182.160:7004 192.168.182.160:7001

·转移插槽

故障转移

分片集群同样具有哨兵的功能。

手动故障转移

    在新的slave节点cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。


这种failover命令可以指定三种模式:
    - 缺省:默认的流程
    - force:强制,省略了对offset的一致性校验
    - takeover:强制,直接执行第5歩,忽略数据一致性、忽略master状态和其它master的意见

RedisTemplate访问分片集群

1.引入依赖

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

2. 配置yml文件

spring:
  redis:
    cluster:
      nodes: # 指定分片集群的每一个节点信息
        - 192.168.182.160:7001
        - 192.168.182.160:7002
        - 192.168.182.160:7003
        - 192.168.182.160:8001
        - 192.168.182.160:8002
        - 192.168.182.160:8003

3.配置读写分离

@Bean
// 自定义Lettuce连接配置类,指定Redis集群的主从节点读写分离策略,这里也没有讲默认的策略是什么
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
    // lamda表达式写法
    return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
黑马Redis高级笔记提供了关于Redis的一些最佳实践和高级功能的知识点。其中包括了以下几个主题: 1. Redis键值设计:介绍了如何设计优雅的key结构,避免BigKey的出现,并选择恰当的数据类型来提高性能。 2. 批处理优化:介绍了在Redis集群中如何进行批处理优化,包括使用Pipeline来提高性能,并分享了在集群环境下的批处理技巧。 3. 服务端优化:介绍了一些Redis服务器的优化方法,包括持久化配置、慢查询优化、命令及安全配置以及内存配置。 4. 集群最佳实践:提供了关于Redis集群的最佳实践,包括如何查询集群状态以及散列插槽的相关知识。 这些笔记提供了关于如何优化Redis性能及使用Redis集群的一些实用技巧和建议。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [黑马Redis笔记高级 | 分布式缓存](https://blog.csdn.net/2301_77450803/article/details/130547696)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [黑马Redis笔记高级 | Redis最佳实践](https://blog.csdn.net/2301_77450803/article/details/130659537)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值