更多特训营笔记详见个人主页【面试鸭特训营】专栏
250104
1. Redis 中如何保证缓存与数据库的数据一致性?
旁路缓存
- 流程
- 先删除缓存,再写数据库
- 存在的问题
- 可能会发生脏读现象
- 若某读线程
恰好
在写线程的删除缓存
和写数据库
之间发生了读操作,则:- 缓存未命中,该读线程会从数据库中查询到脏数据,并回写到缓存中。
- 随后导致在该缓存数据过期时间内,后续的全部查询请求都直接从缓存中得到了脏数据。
- 可以发现,一个
恰好
就会导致很多脏数据的出现,数据一致性最差。
双删缓存
- 双删缓存策略就是在旁路缓存的基础上,在写完数据库后,立刻再删除一遍缓存,减少脏数据,提升了一些数据一致性,但也不是最优解。
- 存在的问题
- 可能会发生脏读现象,相较于旁路缓存,脏读现象发生的概率更低
- 若某读线程若恰好在写线程的
删除缓存
和写数据库
之间发生了读操作,则: - 缓存未命中,该读线程会从数据库中查询到脏数据。
- 又恰好该读线程因为某些原因发生了延迟,在写线程的第二次删除缓存操作后才完成缓存回写。
- 则仍然会导致在该缓存数据过期时间内,后续的全部查询请求都直接从缓存中得到了脏数据。
- 可以发现,两个
恰好
才会导致很多脏数据的出现,数据一致性比旁路缓存策略强一点。
延迟双删
- 延时双删策略就是在立即双删的基础上,将第二次的删除操作修改为通过MQ延时删除,一般延迟1到2秒即可,以保证读线程回写的脏数据最终可以被删除,数据一致性最好。
2. Redis 中的缓存击穿、缓存穿透和缓存雪崩是什么?
Redis并发现象 | 简述 | 解决方案 |
---|---|---|
缓存击穿 | 缓存突然过期 | 1. 临时设置缓存永不过期 2. 自动续期 |
缓存穿透 | 请求访问不存在的数据 | 1. 在缓存中存储null值 2. 使用布隆过滤器 |
缓存雪崩 | 大量数据同时过期 | 1. 临时设置缓存永不过期 2. 为过期时间随机盐 3. 多地缓存 |
3. Redis String 类型的底层实现是什么?(SDS)
- Redis 中的 String 的底层实现是基于简单动态字符串 (SDS),并结合 int 、embstr 、raw 等不同的编码方式进行存储优化
SDS 底层设计
- 有
len
、alloc
、flags
、buf[]
四个字段 - len:记录字符串的实际长度
- alloc:表示为字符串预分配的空间大小
- 剩余空间大小 == alloc - len
- flags:表示 SDS 的类型
- SDS 共有五种类型,分别是 sdshdr 5、 sdshdr 8、 sdshdr 16、 sdshdr 32、 sdshdr 64
- buf[]:存储数据,如字符串和二进制数据
int 、embstr 、raw编码
-
int编码
-
可以解析成整数的字符串,且整数较小,直接放在 redisObject 的 ptr 指针字段中
-
struct redisObject { // 数据类型(字符串、哈希等) unsigned type:4; // 编码类型(int、embstr、raw等),这里是int unsigned encoding:4; // 实际的数据指针,这里直接存储整数值 int64_t ptr; }
-
-
embstr编码
-
字符串长度小于 44 时使用,
redisObject
和sdshdr
内存连续存储,减少内存分配和管理的开销,适用于小字符串 -
struct redisObject { // 数据类型(字符串、哈希等) unsigned type:4; // 编码类型(int、embstr、raw等),这里是embstr unsigned encoding:4; // 实际的数据指针,这里直接存储整数值 void *ptr; } struct sdshdr { // 当前字符串长度 uint32_t len; // 已分配的内存大小 uint32_t alloc; // 编码类型 unsigned char flags; // 实际字符串数据 char buf[]; }
-
-
raw编码
-
字符串长度大于 44 时使用,
redisObject
和sdshdr
内存分开存储,redisObject
中的ptr
指针指向sdshdr
-
struct redisObject { // 数据类型(字符串、哈希等) unsigned type:4; // 编码类型(int、embstr、raw等),这里是embstr unsigned encoding:4; // 实际的数据指针,这里直接存储整数值 void *ptr; } struct sdshdr { // 当前字符串长度 uint32_t len; // 已分配的内存大小 uint32_t alloc; // 编码类型 unsigned char flags; // 实际字符串数据 char buf[]; }
-
SDS优点
- 可以直接获取字符串长度
- 二进制安全
- 支持预分配字符串空间
- 支持动态空间大小