# 分布式缓存

# Redis 持久化

# RDB 持久化

# 概念

RDB 全称 Redis Database Backup file(Redis 数据备份文件),也被叫做 Redis 数据快照,简单来说就是把内存中所有数据都记录到磁盘中,当 Redis 实例故障重启后,从磁盘读取快照文件,恢复数据,快照文件为 RDB 文件,默认是保存在当前运行目录

image-20230405132332781

# RDB 机制

redis.conf 文件中,有触发 RDB 机制的配置

image-20230405132624667

RDB 的其它配置也可以在 redis.conf 文件中设置

image-20230405132703431

# RDB 的 fork 原理

bgsave 开始时会 fork 主进程得到子进程,子进程共享主进程的内存数据,完成 fork 后读取内存数据并写入 RDB 文件

  1. 当主进程执行读操作时,访问共享内存
  2. 当主进程执行写操作时,则会拷贝一份数据,执行写操作

image-20230405133629745

# AOF 持久化

# 概念

AOF 全称为 Append Only FIle(追加文件),Redis 处理的每一个写命令都会记录在 AOF 文件,可以看做是命令日志文件

# 配置

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

image-20230405134210521

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

image-20230405134315177

配置项 刷盘时机 优点 缺点
Alaways 同步刷盘 可靠性高,几乎不丢失数据 性能影响大
everysec 每秒刷盘 性能适中 最多丢失一秒数据
no 操作系统控制 性能最好 可靠性较差,可能丢失大量数据

因为是记录命令,AOF 文件会比 RDB 文件大得多,而且 AOF 会记录同一个 key 的多次写操作,但只有最后一次写操作才有意义,通过执行 bgrewiteaof 命令,可以让 AOF 文件执行重写功能,用最少的命令达到相同效果

image-20230405135346225

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

image-20230405135609682

# RDB && AOF 对比

RDB AOF
持久化方式 定时对整个内存做快照 记录每一次执行的命令
数据完整性 不完整,两次备份之间会丢失 相对完整,取决于刷盘策略
文件大小 会有压缩,文件体积小 记录命令,文件体积很大
宕机恢复速度 很快
数据恢复优先级 低,因为数据完整性不如 AOF 高,因为数据完整性更高
系统资源占用 高,大量 CPU 和内存消耗 低,主要是磁盘 IO 资源,但 AOF 重写时会占用大量 CPU 和内存资源
使用场景 可以容忍数分钟的数据丢失,追求更快的启动速度 对数据安全性要求较高

# Redis 主从

# 搭建主从架构

image-20230405140600792

# 数据同步原理

# 第一次同步(全量同步原理)

image-20230405142959401

# 两个概念

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

# 增量同步原理

image-20230405145836105

# 优化 Redis 主从集群

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

image-20230405151403017

# Redis 哨兵

# Redis 哨兵的作用及工作原理

# 哨兵的作用

Redis 提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复

  1. 监控:Sentinel 会不断检查您的 master 和 slave 是否按预期工作
  2. 自动故障恢复:如果 master 故障,Sentinel 会将一个 slave 提升为 master,当故障恢复后也以新的 master 为主
  3. 通知:Sentinel 充当 Redis 客户端的服务发现来源,当集群发生故障转移时,会将最新消息推送给 Redis 的客户端

image-20230405152312038

# 服务状态监控

Sentinel 基于心跳机制监测服务状态,每隔一秒向集群的每个实例发送 ping 命令

  1. 主观下线:如果某 sentinel 节点发现某实例未在规定时间响应,则认为该实例主观下线
  2. 客观下线:若超过指定数量(quorum)的 sentinel 都认为该实例主观下线,则该实例客观下线,quorun 值最好超过 Sentinel 实例数量的一半

image-20230405152753126

# 选举新的 master

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

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

# 实现故障转移

  1. sentinel 给备选的 slave 节点发送 slaveof no one 命令,让该节点称为 master
  2. sentinel 给其它 slave 发送 slaveof 192.168.150.101 7002 命令,让这些 slave 成为新的 master 从节点,开始从新的 master 上同步数据
  3. 最后,sentinel 将故障节点标记为 slave,当故障节点恢复后会自动成为新的 master 的 slave 节点

image-20230405153818197

# RedisTemplate 的哨兵模式

# 引入依赖

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

# 配置 application.yaml

spring:
	redis:
		sentinel:
			master: mymaster
			nodes:
				- 192.168.150.101:27001
				- 192.168.150.101:27002
				- 192.168.150.101:27003

# 配置主从读写分离

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

# Redis 分片集群

# 搭建分片集群

# 分片集群结构

  1. 集群中有多个 master,每个 master 保存不同数据
  2. 每个 master 都可以有多个 slave 节点
  3. master 之间通过 ping 检测彼此健康状态
  4. 客户端请求可以访问集群任意节点,最终会转发到正确的节点上

# 散列插槽

Redis 会把每一个 master 节点映射到 0~16383 个插槽上,从而寻找不同的 master

# 集群伸缩

添加或者移除节点到集群中

# 故障转移

# 过程

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

  1. 然后疑似宕机
  2. 最后是确定下线,自动提升一个 slave 节点为新的 master

# 数据迁移

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

image-20230405162554961

# RedisTemplate 访问分片集群

# 引入依赖

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

# 配置 application.yaml

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

# 配置读写分离

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

# 多级缓存

# JVM 进程缓存

# 本地进程缓存

  1. Redis 分布式缓存,例如 Redis
    1. 优点:存储容量更大,可靠性好,可以在集群间共享
    2. 缺点:访问缓存有网络开销
    3. 场景:缓存数据量较大,可靠性要求较高,需要在集群间共享
  2. 进程本地缓存,例如 HashMap、GuavaCache
    1. 优点:读取本地内存,没有网络开销,速度更快
    2. 缺点:存储容量有限、可靠性较低、无法共享
    3. 场景:性能要求较高,缓存数据量较小

# Lua 语法

# 初识 Lua

Lua 是一个轻量小巧的脚本语言,用标准语言编写并以源代码形式开放,其设计目的为了让嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能

# 变量和循环

# 数据类型

数据类型 描述
nil 这个最简单,只有值 nil 属于该类,表示一个无效值(在条件表达式中相当于 false)
boolean 包含两个值,false 和 true
number 表示双精度类型的实浮点数
string 字符串由一对双引号或单引号来表示
function 由 C 或 Lua 编写的函数
table Lua 中的表 table 其实一个关联数组,数组索引可以是数字、字符串或表类型,在 Lua 里,table 的创建是通过构造表达式来完成,最简单构造表达式是 {},用来创建一个空表

# 变量

Lua 声明变量的时候,并不需要指定数据类型

-- 声明字符串
local str = 'hello'
-- 声明数字
local num = 21
-- 声明布尔类型
local flag = true
-- 声明数字 key 为索引的 table
local str = {'java', 'python', 'lua'}
-- 声明 table,类似 java 的 map
local map = {name='jack', age=21}

访问 table

-- 访问数组,lua 数组的角标从 1 开始
print(arr[1])
-- 访问 table
print(map['name'])
print(map.name)

# 循环

数组、table 都可以利用 for 循环来遍历

遍历数组

-- 声明数组 key 为索引的 table
local str = {'java', 'python', 'lua'}
-- 遍历数组
for index,value in ipairs(arr) do
    print(index, value)
end

遍历 table

-- 声明 map,也就是 table
local map = {name='jack', age=21}
-- 遍历 table
for key,value in pairs(map) do
    print(key,value)
end

# 函数和条件控制

# 函数

定义函数语法

function 函数名(argument11, argument2..., argumentn)
    -- 函数体
    return 返回值
end

# 条件控制

if(布尔表达式)
then
    -- [布尔表达式为 true 时 执行该语句块]
else
    -- [布尔表达式为 false 时 执行该语句块]
end
操作符 描述 实例
and 逻辑与操作符,若 A 为 false,则返回 A,否则返回 B (A and B)
or 逻辑或操作符,若 A 为 true,则返回 A,否则返回 B (A or B)
not 逻辑非操作符,若逻辑运算结果相反,如果条件为 true,逻辑非为 false not(A and B)

# OpenResty

# 缓存同步

# 缓存同步策略

  1. 设置有效期:给缓存设置有效期,到期后自动删除,再次查询更新
    1. 优势:简单、方便
    2. 缺点:时效性差,缓存过期之前可能不一致
    3. 场景:更新频率低,时效性要求低的业务
  2. 同步双写:在修改数据的同时,直接修改缓存
    1. 优势:时效性强,缓存与数据库一致
    2. 缺点:有代码侵入,耦合度高
    3. 场景:对一致性、时效性要求较高的缓存数据
  3. 异步通知:修改数据库时发送事件通知,相关服务监听到通知后修改缓存数据
    1. 优势:低耦合,可以同时通知多个缓存服务
    2. 缺点:时效性一般,可能存在中间不一致状态
    3. 场景:时效性要求一般,有多个服务需要同步
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Baozi 微信支付

微信支付

Baozi 支付宝

支付宝

Baozi 微信

微信