持久化
redis
三种数据持久化的方式
AOF
日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里RDB
快照:将某一时刻的内存数据,以二进制的方式写入磁盘- 混合持久化方式:Redis 4.0 新增的方式,集成了 AOF 和 RBD 的优点
AOF
日志
AOF 持久化是将执行的每一条写命令记录到 AOF 文件中
先执行写操作,再写入AOF
日志:
- 避免额外的检查开销(语句是否执行成功)
- 不会阻塞当前写操作的执行,但是可能数据会丢失(因为故障,未写入日志)
- 写日志和执行命令都在主进程进行
AOF 重写机制:
- Redis 为了避免 AOF 文件越写越大,当 AOF 文件的大小超过所设定的阈值后,Redis 就会启用 AOF 重写机制,来压缩 AOF 文件
- AOF 重写机制是在重写时,读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到新的 AOF 文件,等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF 文件
- 重写在子进程执行
- AOF 重写缓冲区:在重写 AOF 期间,当 Redis 执行完一个写命令之后,它会同时将这个写命令写入到 AOF 缓冲区和 AOF 重写缓冲区,
三种写回操作
- Always:每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;
- Everysec:每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
- No:意味着不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。
RDB
快照
RDB 持久化是将 Redis 在某个时间点上的全部数据生成快照(snapshot)保存到硬盘上的一个二进制文件中。执行 bgsave 过程中,Redis 依然可以继续处理操作命令的,也就是数据是能被修改的,关键的技术就在于写时复制技术(Copy-On-Write, COW
)。
触发RDB
持久化的方式:
手动触发: 使用
save
或bgsave
命令。save
命令会阻塞 Redis 主线程执行快照操作,bgsave
命令会在后台异步执行快照操作。- 执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程;
- 执行了 bgsave 命令,会创建一个子进程来生成 RDB 文件,这样可以避免主线程的阻塞;
- 自动触发: 可以在
redis.conf
配置文件中设置触发条件,比如在 900 秒内有 1 个 key 发生变化时自动触发。
混合持久化
当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。(AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据)
主从复制
主服务器可以进行读写操作,当发生写操作时自动将写操作同步给从服务器,而从服务器一般是只读,并接受主服务器同步过来写操作命令,然后执行这条命令。
主从服务器间的同步的过程
- 建立链接、协商同步
- 主服务器同步数据给从服务器:主服务器会执行 bgsave 命令来生成 RDB 文件,然后把文件发送给从服务器;从服务器收到 RDB 文件后,会先清空当前的数据,然后载入 RDB 文件。
- 主服务器发送新写操作命令给从服务器。
- 主从服务器在完成第一次同步后,双方之间就会维护一个 TCP 连接,后续主服务器可以通过这个连接继续将写操作命令传播给从服务器
增量复制
从 Redis 2.8 开始,网络断开又恢复后,从主从服务器会采用增量复制的方式继续同步,也就是只会把网络断开期间主服务器接收到的写操作命令,同步给从服务器
- 从服务器在恢复网络后,会发送 psync 命令给主服务器,(offset 参数不是 -1);
- 主服务器收到该命令后,然后用 CONTINUE 响应命令告诉从服务器接下来采用增量复制的方式同步数据;
- 然后主服务将主从服务器断线期间,所执行的写命令发送给从服务器,然后从服务器执行这些命令。
哨兵模式
哨兵模式的核心思想是在主从复制的基础上,引入哨兵进程来监控主从节点的状态。当主节点出现故障时,哨兵会自动选举出新的主节点,并完成主从切换,从而实现 Redis 服务的高可用性。
- 集群监控:哨兵会定期检查 Redis 主从复制集群中各个节点的健康状态,确保它们都在正常运行。
- 故障监测与通知:哨兵会监测主节点是否发生故障。当主节点出现故障时,哨兵会进行"主观下线"和"客观下线"的判断。如果超过半数的哨兵认为主节点已经下线,则会触发故障转移。
- 自动故障转移:当主节点发生故障时,哨兵会从从节点中选举出一个新的主节点,并自动完成主从切换,确保整个 Redis 服务的可用性
集群模式
将数据分布在不同的服务器上,以此来降低系统对单主节点的依赖,从而提高 Redis 服务的读写性能。Redis Cluster 方案采用哈希槽(Slot),来处理数据和节点之间的映射关系。在 Redis Cluster 方案中,一个切片集群共有 16384 个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据它的 key,被映射到一个哈希槽中
- 平均分配: 在使用 cluster create 命令创建 Redis 集群时,Redis 会自动把所有哈希槽平均分布到集群节点上。比如集群中有 9 个节点,则每个节点上槽的个数为 16384/9 个。
手动分配: 可以使用 cluster meet 命令手动建立节点间的连接,组成集群,再使用 cluster addslots 命令,指定每个节点上的哈希槽个数。
Redis 过期删除与内存淘汰
过期删除策略
每当我们对一个 key 设置了过期时间时,Redis 会把该 key 带上过期时间存储到一个过期字典(expires dict)。当我们查询一个 key 时,Redis 首先检查该 key 是否存在于过期字典中:
- 如果不在,则正常读取键值;
- 如果存在,则会获取该 key 的过期时间,然后与当前系统时间进行比对,如果比系统时间大,那就没有过期,否则判定该 key 已过期。
Redis 使用的过期删除策略是「惰性删除+定期删除」
惰性删除:
定期删除:
持久化时对过期键怎么处理
- RDB 文件生成阶段:过期的键「不会」被保存到新的 RDB 文件中
RDB 加载阶段:
- 主服务器运行模式,在载入 RDB 文件时,程序会对文件中保存的键进行检查,过期键不会被载入到数据库中
- 从服务器运行模式,在载入 RDB 文件时,不论键是否过期都会被载入到数据库中。_但由于主从服务器在进行数据同步时,从服务器的数据会被清空。所以一般来说,过期键对载入 RDB 文件的从服务器也不会造成影响。_
- AOF 文件写入阶段:如果数据库某个过期键还没被删除,那么 AOF 文件会保留此过期键,当此过期键被删除后,Redis 会向 AOF 文件追加一条 DEL 命令来显式地删除该键值。
AOF 重写阶段:已过期的键不会被保存到重写后的 AOF 文件中
内存淘汰策略
- 不进行数据淘汰的策略:noeviction(Redis3.0之后,默认的内存淘汰策略) :它表示当运行内存超过最大设置内存时,不淘汰任何数据,而是不再提供服务,直接返回错误。
- 进行数据淘汰的策略:进行数据淘汰,又可以细分为在设置了过期时间的数据中进行淘汰和在所有数据范围内进行淘汰这两类策略。
缓存设计
缓存雪崩
什么是缓存雪崩:
在某个时间点,缓存中的大部分或全部数据同时失效,导致大量的请求直接落到数据库上,从而引发数据库的压力过大,甚至崩溃的情况。
解决:
- 分散缓存失效时间
- 设置缓存不过期
缓存击穿
什么是缓存击穿:
如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是缓存击穿的问题。
解决:
- 使用互斥锁控制,只有一个请求可以访问数据库,其他请求需要等待。当第一个请求重新生成缓存后,其他请求可以从缓存中获取数据。
- 采用预加载(
Cache Preloading
)的方式,在缓存失效前提前加载热点数据到缓存中,避免热点数据缓存失效时的突然访问峰值。 - 采用降级策略(
Fallback Strategy
),在缓存失效的情况下提供备用方案,如直接返回默认值或从数据库中获取数据。这样可以保证系统的稳定性。
缓存穿透
什么是缓存穿透:
用户请求的数据在缓存中不存在,同时在数据库中也不存在,导致每次请求都要去数据库中查询一遍,然后返回空结果。如果有恶意攻击者不断请求系统中不存在的数据,会导致大量请求落到数据库上,造成数据库压力过大
解决:
- 使用布隆过滤器:布隆过滤器是一种高效的概率型数据结构,可以快速判断某个元素是否存在于集合中。在缓存层面使用布隆过滤器可以过滤掉一些无效的请求,避免无效请求直接访问数据库。
- 缓存空值处理:当查询的数据在数据库中确实不存在时,可以将空值也缓存起来,设置一个较短的过期时间。这样,下次查询同样的数据时,就可以从缓存中获取到空值,避免再次访问数据库。
- 限流降级:对于一些非法的、恶意的请求,可以使用接口限流或者服务降级的方式,快速返回错误响应,避免持续地打击数据库。
常见的缓存更新策略
Cache Aside (旁路缓存)策略
该策略又可以细分为读策略和写策略。
- 读策略: 先查询缓存, 如果缓存未命中则从数据库读取数据并写入缓存。
写策略: 先更新数据库, 然后删除或更新缓存中的数据。
Read/Write Through(读穿 / 写穿)策略
- 读策略: 先查询缓存, 如果缓存未命中则由缓存组件从数据库加载数据并写入缓存。
写策略: 先更新缓存, 然后由缓存组件同步更新数据库。应用程序只与缓存交互, 缓存负责与数据库的交互。
Write Back (写回)策略
写策略:只更新缓存, 不直接更新数据库, 而是由缓存异步批量更新数据库。这种策略可以提高写性能, 但数据不是强一致性的, 且可能会丢失数据。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。