Redis 中如何保证缓存与数据库的数据一致性?
1. Cache Aside(旁路缓存模式)
- 读操作:先查询缓存,如果缓存命中则返回数据;若缓存未命中,则从数据库读取数据,并将数据写入缓存。
- 写操作:先更新数据库,再删除缓存,确保缓存中的数据无效化。
优化方法:引入分布式锁避免并发读写冲突,配合延时双删进一步保障缓存一致性。
2. Read-Through(直读缓存模式)
- 读操作:若缓存命中,直接返回数据;若缓存未命中,缓存系统从数据库加载数据并返回,同时更新缓存。
- 写操作:同时更新缓存和数据库,确保两者一致。
优化方法:增加缓存操作的重试机制,引入消息队列确保缓存更新的可靠性。
3. Write-Through(直写缓存模式)
- 写操作:所有写操作直接更新缓存,同时缓存负责同步更新数据库;所有读操作都通过缓存获取数据。
优化方法:引入分布式事务确保缓存和数据库更新的原子性,将失败的写操作记录到队列中稍后重试。
4. 双写策略
- 写操作:同时更新数据库和缓存,读操作仅从缓存获取数据。
优化方法:为缓存更新和数据库写入增加事务机制,增加幂等性校验避免缓存重复更新。
5. 延时双删策略
- 写操作:先删除缓存,更新数据库后,等待一定时间,再次删除缓存,确保一致性。
6. 异步消息队列同步
写操作:更新数据库后,通过消息队列发送事件通知缓存服务,异步更新或删除缓存,实现最终一致性。
优化方法:配置消息队列的持久化和重试机制,确保消息不会丢失,使用延迟队列重新尝试更新缓存。
7. Binlog 同步
- 原理:利用数据库 Binlog 捕获增量数据变更,实时同步缓存数据,确保缓存与数据库的一致性。
优化方法:提高 Binlog 同步的实时性,使用高性能的消息传输协议减少延迟。
8. 设置缓存过期时间
- 兜底方案:为 Redis 中的缓存数据设置合理的 TTL(如 30 分钟),最终强制重新加载最新数据。
9. 引入分布式锁
- 读写锁控制:在业务层对同一数据加分布式锁,确保读写顺序性。
10. 版本号或时间戳
- 版本号校验:在缓存和数据库中存储数据的版本号,更新时校验版本,确保数据一致性。
通过以上策略,可以根据具体的业务需求和系统架构选择合适的方案来保证 Redis 缓存与数据库的数据一致性。
Redis 中的缓存击穿、缓存穿透和缓存雪崩是什么?
1. 缓存击穿
概念:
- 指缓存中某个热点数据(频繁被访问的数据)突然过期,此时大量请求同一时间通过缓存直击数据库,导致数据库压力骤增,甚至可能崩溃。
- 原因:
- 缓存的过期时间设置不合理,导致热点数据和缓存过期时间集中在同一时刻。
- 高并发场景下,大量请求几乎同时到达,访问同一缓存数据。
2. 缓存穿透
概念:
- 指客户端请求的数据在缓存和数据库中都不存在,导致所有请求都直接穿透到数据库进行查询,增加了数据库的压力。
- 原因:
- 请求携带非法参数,例如黑客故意构造不存在的 ID 请求。
- 系统中存在大量不存在或无效的查询。
3. 缓存雪崩
概念:
- 指缓存中大量数据在短时间内同时失效,导致大量的请求在极短时间内直接访问数据库,数据库因瞬时压力过大而崩溃。
- 原因:
- 缓存服务器突然宕机,导致所有缓存数据丢失。
- 缓存数据的过期时间设置得过于集中,导致多个缓存数据同时过期。
Redis String 类型的底层实现是什么?(SDS)
SDS 的结构
SDS 是 Redis 用于表示字符串的底层数据结构,它由以下部分组成:
len
:记录字符串的实际长度(不包括结尾的\0
)。alloc
:记录分配的总空间(不包括头部和结尾的\0
)。flags
:标志位,用于标识 SDS 的类型(如SDS_TYPE_8
、SDS_TYPE_16
等)。buf
:存储字符串内容的动态数组,末尾会自动添加\0
,以兼容 C 语言的字符串函数。
SDS 的编码类型
SDS 有三种编码类型:
embstr:占用 64 Bytes 的空间,存储 44 Bytes 的数据。embstr 结构存储小于等于 44 个字节的字符串,其内存结构是字符串 SDS 结构体与其对应的 redisObject
对象分配在同一块连续的内存空间。
raw:存储大于 44 Bytes 的数据。此时,动态字符串 SDS 的内存与其依赖的 redisObject
的内存不在连续。
int:存储整数类型。如果存储的是整数,Redis 会直接使用整数类型存储,而不是字符串。
SDS 的优点
SDS 相比传统的 C 语言字符串(char*)具有以下优点:
- 常数复杂度获取字符串长度:通过
len
属性可以直接获取字符串长度,时间复杂度为 O(1)。 - 避免缓冲区溢出:通过
alloc
属性记录分配的总空间,可以有效避免缓冲区溢出。 - 减少内存重分配次数:SDS 通过预分配空间的方式,减少了内存重分配的次数。
- 二进制安全:SDS 不依赖于字符串中的
\0
来判断字符串是否结束,因此可以存储任意二进制数据。 - 兼容 C 语言字符串函数:SDS 的末尾会自动添加
\0
,因此可以兼容 C 语言的字符串函数。
SDS 的操作
Redis 提供了一系列函数来操作 SDS,例如:
- 创建 SDS:
sdsnew
、sdsempty
- 追加字符串:
sdscat
- 复制字符串:
sdscpy
- 释放 SDS:
sdsfree
- 获取长度:
sdslen
- 获取剩余空间:
sdsavail
通过 SDS,Redis 的 String 类型实现了高效、安全和灵活的字符串存储和操作。