1. redis的数据结构
1.1 String
1.1.1 底层结构
Redis 中的 string 类型底层实现使用了 SDS(Simple Dynamic String),是因为 SDS 提供了比 C 语言原生字符串(以空字符结尾的 char*)更灵活和高效的功能
- SDS 的结构如下:
struct sdshdr {
int len; // 已使用长度
int alloc; // 分配的总长度(包括 len 和 free 空间)
char buf[]; // 实际存储字符串的数据区域
};
- 二进制安全
SDS 支持存储二进制数据(如图片、序列化数据等),不会因遇到 \0 提前截断, 通过独立记录字符串长度(而不是依赖 \0),确保了对二进制数据的安全处理
- 避免缓冲区溢出
SDS 通过记录实际已用长度(len)和总分配长度(alloc),严格控制字符串操作范围,避免了这种问题
1.1.2 常用命令
1. 设置值
-
SET key value
-
设置指定
key的值。如果key已存在,会覆盖原有的值。 -
示例:
SET mykey "Hello, Redis" -
-
SETNX key value
-
仅在
key不存在时设置key的值。如果key已存在,不会做任何操作。可以用于分布式锁 -
示例:
SETNX mykey "Hello, Redis" -
-
SETEX key seconds value
-
设置
key的值,并同时设置过期时间(秒)。 -
示例:
SETEX mykey 10 "Hello, Redis" # 10 秒后过期 -
-
MSET key value [key value ...]
-
批量设置多个键值对。
-
示例:
MSET key1 "value1" key2 "value2" key3 "value3" -
-
MSETNX key value [key value ...]
-
批量设置多个键值对,仅在所有
key都不存在时才会设置。 -
示例:
MSETNX key1 "value1" key2 "value2" -
2. 获取值
-
GET key
-
获取指定
key的值。 -
示例:
GET mykey # 返回 "Hello, Redis" -
-
MGET key [key ...]
-
批量获取多个
key的值。 -
示例:
MGET key1 key2 key3 # 返回 ["value1", "value2", "value3"] -
3. 删除值
-
DEL key [key ...]
-
删除指定
key。 -
示例:
DEL mykey DEL key1 key2 key3 # 批量删除 -
4. 检查键是否存在
-
EXISTS key
-
检查
key是否存在。 -
示例:
EXISTS mykey # 返回 1(存在)或 0(不存在) -
5. 修改值
-
SET key value
- 设置或修改
key的值,如前面所示。
- 设置或修改
-
INCR key
-
将
key的值递增 1(适用于整数值)。 -
示例:
SET mynum 10 INCR mynum # 返回 11 -
-
INCRBY key increment
-
将
key的值递增指定的增量。 -
示例:
INCRBY mynum 5 # 返回 15 -
-
DECR key
-
将
key的值递减 1(适用于整数值)。 -
示例:
SET mynum 10 DECR mynum # 返回 9 -
-
DECRBY key decrement
-
将
key的值递减指定的减量。 -
示例:
DECRBY mynum 3 # 返回 6 -
6. 追加和截取
-
APPEND key value
-
将指定的值追加到
key的当前值的末尾。如果key不存在,等价于SET key value。 -
示例:
SET mykey "Hello" APPEND mykey ", Redis" # mykey 的值变为 "Hello, Redis" -
-
GETRANGE key start end
-
获取
key的值的子字符串(通过索引)。 -
示例:
GETRANGE mykey 0 4 # 返回 "Hello" -
-
SETRANGE key offset value
-
从指定的偏移量开始,将
key的值替换为新值。适用于修改字符串的部分内容。 -
示例:
SET mykey "Hello, World" SETRANGE mykey 7 "Redis" # mykey 的值变为 "Hello, Redis" -
7. 获取键的长度
-
STRLEN key
-
获取
key的值的长度。 -
示例:
STRLEN mykey # 返回字符串长度 -
8. 键的过期时间
-
EXPIRE key seconds
-
设置
key的过期时间(秒)。 -
示例:
EXPIRE mykey 10 # 10 秒后mykey过期 -
-
TTL key
-
获取
key的剩余过期时间(秒)。 -
示例:
TTL mykey # 返回剩余时间或 -1(永不过期) -
-
PERSIST key
-
移除
key的过期时间,使其成为持久化键。 -
示例:
PERSIST mykey # 取消过期时间 -
9. 常用命令示例
SET mykey "Hello"
GET mykey # 返回 "Hello"
APPEND mykey " World" # mykey 的值变为 "Hello World"
STRLEN mykey # 返回 11
INCR mycounter # mycounter 自增 1
MSET key1 "value1" key2 "value2" # 批量设置
1.2 Hash
1.2.1 常用命令
设置字段值
-
命令:
HSET key field value- 设置 Hash 中字段的值,如果字段不存在则创建。
-
示例:
HSET user:1001 name Alice HSET user:1001 age 30
获取字段值
-
命令:
HGET key field- 获取 Hash 中指定字段的值。
-
示例:
HGET user:1001 name # 返回 "Alice"
删除字段
-
命令:
HDEL key field [field ...]- 删除一个或多个字段。
-
示例:
HDEL user:1001 age
检查字段是否存在
-
命令:
HEXISTS key field- 检查 Hash 中是否存在指定字段。
-
示例:
HEXISTS user:1001 name # 返回 1(存在)
获取所有字段和值
-
命令:
HGETALL key- 返回 Hash 中所有字段和值。
-
示例:
HGETALL user:1001 # 返回: # name Alice # age 30
获取字段列表
-
命令:
HKEYS key- 获取 Hash 中所有字段的列表。
-
示例:
HKEYS user:1001 # 返回 ["name", "age"]
获取值列表
-
命令:
HVALS key- 获取 Hash 中所有值的列表。
-
示例:
HVALS user:1001 # 返回 ["Alice", "30"]
获取字段数量
-
命令:
HLEN key- 返回 Hash 中字段的数量。
-
示例:
HLEN user:1001 # 返回 2
递增字段值
-
命令:
HINCRBY key field increment- 将指定字段的值加上增量(适用于整数值)。
-
示例:
HSET user:1001 age 30 HINCRBY user:1001 age 5 # age = 35
1.2.2 使用场景
缓存对象信息, 可精确修改对象属性
1.3 List
1.3.1 是什么
一个双端链表的结构,容量是2的32次方减1个元素,大概40多亿,主要功能有push/pop等,一般用在栈、队列、消息队列等场景。
left、right都可以插入添加;
如果键不存在,创建新的链表;
如果键已存在,新增内容;
如果值全移除,对应的键也就消失了。
它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。
1.3.2 常用命令
1. 插入元素
-
LPUSH key value [value ...]
-
在 List 的左侧插入一个或多个元素。
-
示例:
LPUSH mylist "a" LPUSH mylist "b" "c" -
-
RPUSH key value [value ...]
-
在 List 的右侧插入一个或多个元素。
-
示例:
RPUSH mylist "d" RPUSH mylist "e" "f" -
2. 获取元素
-
LRANGE key start stop
-
返回指定范围内的元素(从
start到stop)。 -
示例:
LRANGE mylist 0 -1 # 返回整个列表 LRANGE mylist 1 3 # 返回索引从 1 到 3 的元素 -
-
LINDEX key index
-
获取 List 中指定索引的元素。
-
示例:
LINDEX mylist 2 # 返回索引为 2 的元素 -
3. 修改元素
-
LSET key index value
-
将 List 中指定索引的元素修改为新值。
-
示例:
LSET mylist 1 "z" # 将索引 1 处的元素修改为 "z" -
4. 删除元素
-
LREM key count value
-
删除 List 中指定值的元素。count 控制删除的方向和数量:
-
count > 0:从左到右,删除第一个匹配的元素。 -
count < 0:从右到左,删除第一个匹配的元素。 -
count = 0:删除所有匹配的元素。 -
示例:
LREM mylist 1 "a" # 删除第一个匹配的 "a" LREM mylist 0 "b" # 删除所有匹配的 "b" -
-
LPOP key
-
从 List 的左侧弹出并返回一个元素。
-
示例:
LPOP mylist # 删除并返回 List 左侧的元素 -
-
RPOP key
-
从 List 的右侧弹出并返回一个元素。
-
示例:
RPOP mylist # 删除并返回 List 右侧的元素 -
-
RPOPLPUSH source destination
-
从
sourceList 的右侧弹出元素,并将其插入到destinationList 的左侧。 -
示例:
RPOPLPUSH mylist anotherlist -
5. 获取 List 的长度
-
LLEN key
-
返回 List 的元素个数。
-
示例:
LLEN mylist # 返回 mylist 的长度 -
6. 队列和栈操作
- LPUSH key value [value ...] 和 RPOP key
- 实现队列的“先进先出”操作。
- RPUSH key value [value ...]和 LPOP key
- 实现栈的“后进先出”操作。
7. 修剪 List
-
LTRIM key start stop
-
修剪 List,保留从
start到stop范围内的元素。 -
示例:
LTRIM mylist 1 3 # 保留索引 1 到 3 的元素 -
1.4 Zset
1.4.1 ziplist/listpack
用于元素较少 适用于数量不超过128, 大小不超过64字节
- 压缩列表(ziplist)
- 定义:
ziplist是 Redis 中的一种紧凑型双向链表,专为节省小数据量的内存设计 - 结构:
ziplist 是一个连续的内存块,由以下部分组成:
- zlbytes:整个
ziplist占用的总字节数。 - zltail:指向
ziplist最后一个元素的偏移量,方便快速访问尾部。 - zllen:
ziplist中元素的数量。 - entries:存储实际数据,每个 entry 是一个数据项。
- zlend:特殊标志(值为 255),表示列表的结束。
- 用途:
当 ZSet 的元素数量较少(默认小于 128 个)且每个成员和分数较短时,ZSet 会用 ziplist 存储
- 优点: 节省内存:所有数据连续存储,没有指针, 适合小数据量的集合
- 缺点: 随着数据量增大,插入、删除的效率变低,因为需要对内存块进行重分配。不适合频繁增删和大数据量场景
- 列表包(listpack)
- 定义:
listpack是 Redis 6.0 引入的新型压缩结构,旨在替代ziplist,以提高性能和易用性, 设计理念类似ziplist,但解决了ziplist存在的一些缺陷 - 结构:
与 ziplist 类似,listpack 也是一个连续的内存块,由以下部分组成:
-
total_bytes:整个
listpack占用的字节数。 -
num_elements:列表中的元素数量(可能是估计值)。
-
entries:存储实际数据,每个 entry 紧密排列,支持多种数据类型。
-
lp_end:结束标志(值为 255)。
- 用途:
在 Redis 6.0+ 版本中,listpack 替代了 ziplist,作为小型 ZSet 的底层存储结构
- 改进点:
解决了 ziplist 在增删数据时频繁重新分配内存的问题,
比较:ziplist vs listpack
| 特性 | ziplist | listpack |
|---|---|---|
| 引入版本 | Redis 2.x | Redis 6.0 |
| 数据存储方式 | 连续内存,元素交替存储 | 连续内存,元素交替存储 |
| 内存管理 | 容易导致内存碎片 | 改进了内存分配和布局 |
| 操作效率 | 插入/删除效率较低 | 插入/删除效率较高 |
| 兼容性 | 已逐渐被弃用 | 未来的新标准 |
| 设计缺陷 | 操作复杂,容易出现越界或崩溃问题 | 更稳定,解决了越界问题 |
1.4.2 skiplist
用于元素较多 自动由listpack升级为skipList
Redis 的跳表(skiplist)在 ZSet 的实现中结合了两种数据结构:跳表和哈希表(dict)
- 跳表(skipList)
作用: 实现元素的 有序存储 和 按分数排序 的能力, 支持范围操作(如 ZRANGEBYSCORE、ZRANGEBYRANK)
优势: 插入、删除、按分数查询的时间复杂度是 O(logN)
- 哈希表(dict)
作用: 提供 元素到分数的快速映射,通过元素值直接定位分数, 用于快速判断元素是否存在
优势: 精确查找的时间复杂度是 O(1), 插入和删除效率高,不受有序性限制
1.4.3 常用命令
1. 添加/更新元素
命令格式:
ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
实例:
ZADD leaderboard 100 alice 200 bob 150 charlie
作用:
在 leaderboard 中添加以下成员及分数:
alice分数 100bob分数 200charlie分数 150
2. 查询排名范围内的成员
命令格式:
ZRANGE key start stop [WITHSCORES]
实例:
ZRANGE leaderboard 0 -1 WITHSCORES
作用:
返回 leaderboard 中按分数递增排序的所有成员及其分数。
3. 查询分数范围内的成员
命令格式:
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
实例:
ZRANGEBYSCORE leaderboard 100 150 WITHSCORES
作用:
返回 leaderboard 中分数在 100 到 150 范围内的成员及其分数。
4. 查询递减排名范围内的成员
命令格式:
ZREVRANGE key start stop [WITHSCORES]
实例:
ZREVRANGE leaderboard 0 -1 WITHSCORES
作用:
返回 leaderboard 中按分数递减排序的所有成员及其分数。
1.5 BitMap
1.5.1 是什么
Bitmap 本质上是一个 位数组,每一位可以存储 0 或 1,对应于布尔值 false 或 true
1.5.2 常用命令
设置位(SETBIT)
-
命令:
SETBIT key offset value- 设置指定偏移量的位值(
0或1)。 - key:存储 Bitmap 的键。
- offset:位的偏移量,从 0 开始。
- value:只能为
0或1。
- 设置指定偏移量的位值(
-
示例:
SETBIT user_active 5 1 # 将偏移量 5 的位设置为 1
获取位(GETBIT)
-
命令:
GETBIT key offset- 获取指定偏移量的位值。
-
示例:
GETBIT user_active 5 # 获取偏移量 5 的位值
统计位值(BITCOUNT)
-
命令:
BITCOUNT key [start end]- 统计 Bitmap 中值为
1的位的数量。 - 可指定范围(字节索引)进行统计。
- 统计 Bitmap 中值为
-
示例:
BITCOUNT user_active # 统计整个 Bitmap 中值为 1 的位的数量 BITCOUNT user_active 0 1 # 统计第 0 到第 1 字节范围内的值为 1 的位
按位操作(BITOP)
-
命令:
BITOP operation destkey key [key ...]- 对多个 Bitmap 进行按位操作,并将结果保存到目标键。
- 支持的操作包括:
AND(按位与)OR(按位或)XOR(按位异或)NOT(按位取反,仅支持一个键)
-
示例:
BITOP AND result_bitmap key1 key2
查找第一个位的位置(BITPOS)
-
命令:
BITPOS key bit [start] [end]- 查找第一个指定值(
0或1)出现的位的偏移量。 - 可以指定范围(字节索引)进行查找。
- 查找第一个指定值(
-
示例:
BITPOS user_active 1 # 查找第一个值为 1 的位的位置
Bitmap 的存储机制
- Redis 使用 String 类型 来存储 Bitmap,每个字符串中的位可以看作一个 Bitmap 单元。
- 假如一个字符串有 N 个字节,它总共有
N × 8位可用。 - Bitmap 的存储非常紧凑,每 8 位只占用 1 字节内存。
1.5.3 Bitmap 的典型应用场景
用户活跃标记
-
用一个 Bitmap 记录每天用户的活跃情况:
- 用户 ID 对应于位的偏移量。
- 如果用户活跃,设置对应的位为
1,否则为0。
-
示例:
SETBIT user_active_20241201 1001 1 # 用户 ID 为 1001 活跃 GETBIT user_active_20241201 1001 # 检查用户是否活跃
签到打卡
-
将 Bitmap 用于用户签到记录,每位代表一天:
- 通过
SETBIT和GETBIT操作更新或查询签到情况。
- 通过
-
示例:
SETBIT user_checkin 5 1 # 用户第 5 天签到 BITCOUNT user_checkin # 统计用户总签到天数
1.6 Stream
Redis版的MQ redis 5.x之后出现
1.6.1 stream是什么
stream: 用于处理 消息队列 和 日志数据 场景。Stream 提供了一种高效的、可持久化的消息存储方式,支持高并发数据写入和读取,并允许消息被多个消费者组同时消费。说白了就是一个消息队列
消息: 每条消息都有一个全局唯一的 ID,格式为 ms-seq,例如 1680000000000-0:
- ms:毫秒级时间戳。
- seq:序列号,用于同一毫秒内的多条消息区分
消息内容: 每条消息是一个键值对(field-value)集合,允许存储多个字段的数据
可以理解为一个ID为一条消息, 每个ID里面可以有多个键值对代表内容
1.6.2 Stream的基础操作
添加消息:
-
命令:
XADD key ID field value [field value ...]-
ID:可以指定,也可以用*让 Redis 自动生成。 -
示例:
XADD mystream * temperature 22 humidity 60 -
读取消息:
-
命令:
XRANGE key start end [COUNT count]-
读取从
start到end范围的消息。 -
示例:
XRANGE mystream - +-表示最小 ID,+表示最大 ID。
-
删除消息:
-
命令:
XDEL key ID [ID ...]- 删除指定 ID 的消息。
- 消费者组
作用: 一个 Stream 可以有多个消费者组,每个消费者组独立管理自己的消费进度, 消息会被分配给消费者组中的多个消费者,避免重复消费
创建消费者组
-
命令:
XGROUP CREATE key groupname id- 创建一个消费者组。
id:指定起始读取 ID,通常使用$表示从最后一条消息开始。
读取消息
-
命令:
XREADGROUP GROUP groupname consumername COUNT count STREAMS key id-
消费者组读取消息。
-
consumername:指定消费者名称。 -
id:通常为>,表示读取未消费的消息。 -
示例:
XREADGROUP GROUP mygroup consumer1 COUNT 10 STREAMS mystream > -
确认消息
-
命令:
XACK key groupname id [id ...]- 确认已处理完消息,避免重复分配。
未确认消息
-
命令:
XPENDING key groupname- 查看消费者组的未确认消息。
Stream 与其他数据结构的比较
| 功能 | Stream | List | Pub/Sub |
|---|---|---|---|
| 消息存储 | 持久化 | 持久化 | 非持久化 |
| 消息有序性 | 有序 | 有序 | 无序 |
| 消费者管理 | 支持消费者组,消息确认 | 无消费者管理 | 无消费者管理 |
| 场景适配 | 适合日志流、任务队列 | 简单队列 | 广播或通知 |
1.7 GEO
1.7.1 基本概念
Geo 类型可以存储经度和纬度坐标
1.7.2 常用命令
1 添加地理位置
-
GEOADD key longitude latitude member
-
将指定位置的地理信息(经纬度和名称)添加到 Geo 集合中。
-
示例:
GEOADD mygeokey 13.361389 38.115556 "Palermo" GEOADD mygeokey 15.087269 37.502669 "Catania" -
2. 获取地理位置的坐标
-
GEOPOS key member [member ...]
-
返回给定成员的经纬度坐标。
-
示例:
GEOPOS mygeokey "Palermo" # 返回 [13.361389, 38.115556] -
3. 获取两点之间的距离
-
GEODIST key member1 member2 [unit]
-
返回两个成员之间的距离。
unit可以是m(米)、km(千米)、mi(英里)、ft(英尺)。 -
示例:
GEODIST mygeokey "Palermo" "Catania" km # 返回两者之间的距离 -
4 获取成员的周边
-
GEORADIUS key longitude latitude radius [unit] [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]
-
返回给定坐标点半径范围内的所有成员。可以选择输出成员的坐标、距离和 Geohash 值。
-
示例:
GEORADIUS mygeokey 15 37 100 km WITHCOORD WITHDIST # 返回经纬度和距离 -
-
GEORADIUSBYMEMBER key member radius [unit] [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count]
-
在指定的成员周围返回半径范围内的所有成员。
-
示例:
GEORADIUSBYMEMBER mygeokey "Catania" 50 km WITHCOORD -
5 获取成员的排序和排名
-
GEOSORT key [ASC|DESC] [BY pattern] [GET pattern] [COUNT count] [STORE destination]
-
对 Geo 集合进行排序并返回排序结果。可以指定排序顺序和字段。
-
示例:
GEOSORT mygeokey ASC -
1.7.3 使用 Geo 类型的场景
位置查询:可以用于找出给定位置附近的商店、餐馆或其他兴趣点
地图应用:如地图服务提供位置搜索和附近地点推荐功能
1.7.4 Geo 类型的实现原理
Redis 使用 Geohash 编码来存储地理空间数据。每个位置的经纬度会被转换成一个 Geohash 字符串,从而能够在一个空间内索引和排序数据。使用 Geohash 的好处在于,它支持空间的范围查询和点到点的距离计算,同时具有良好的性能和精确度
2. 持久化机制
2.1 RDB
2.1.1 定义
RDB 是以快照的形式将内存中的数据保存到磁盘的一种机制
2.1.2 工作原理:
- 在指定的时间间隔(或触发条件)生成数据库的快照,并保存到
.rdb文件。 - RDB 文件是一个紧凑的二进制文件,适合备份和传输。
优点:
- 文件小,加载速度快,适合冷启动和灾难恢复。
- 对性能的影响较小,因为快照是在后台子进程中完成的。
- 文件格式紧凑,便于跨平台迁移。
缺点:
- 不适合对数据一致性要求很高的场景,因为如果 Redis 在快照之间宕机,会丢失最后一次快照后的数据。
- 需要占用额外的磁盘空间。
2.1.1 配置:
使用 save 指令控制快照条件。例如:
save 900 1 # 900秒内至少有1个写操作时触发快照
save 300 10 # 300秒内至少有10个写操作时触发快照
save 60 10000 # 60秒内至少有10000个写操作时触发快照
为什么 60秒不到 + 10000次写操作 会触发
save 60 10000表示在最多 60 秒内,写操作数累计达到 10000 次时触发快照。- 如果 Redis 检测到在 10 秒 内已经达到了 10000 次写操作,时间和写操作条件都满足,立即触发快照,无需等待到第 60 秒。
为什么 900秒不到 + 1次写操作 不会触发
save 900 1的设计目的是保障长时间低频写操作场景。- 这种规则的特点是写操作条件容易满足,但需要等时间条件达到,即完整的 900 秒窗口内至少有一次写操作才触发。
总结: Redis 不会因为一个低频的写操作立即触发快照,而是等待窗口结束后确认条件是否满足。这是为了避免频繁保存带来的性能开销。高频规则 (save 60 10000):条件满足立即触发,因为目的是保障高并发下的数据安全。低频规则 (save 900 1):时间窗口需要完成,因为目标是以较低频率保存数据。
2.2 AOF
2.2.1 定义
AOF 是将每次写操作以日志形式记录下来,从而实现持久化。
2.2.2 工作原理
- Redis 将每个写命令追加到
.aof文件中, 共有(基本文件、增量文件、清单文件) - AOF 文件定期进行重写(Rewrite)以压缩日志文件的大小。
优点:
- 提供更高的数据安全性,可以通过配置实现秒级甚至毫秒级的数据持久化。
- AOF 文件更易于阅读和分析,因为它是纯文本格式。
缺点:
- 文件相对较大,恢复速度较慢。
- 写入速度比 RDB 略低,对性能有一定影响。
2.2.3 配置
关键参数:
appendonly yes # 启用AOF
appendfsync always # 每次写入都同步到磁盘,性能最低,安全性最高
appendfsync everysec # 每秒同步一次,性能与安全性折中(默认)
appendfsync no # 不同步,由操作系统决定刷盘时机,性能高但不安全
2.3 两种方式对比
2.3.1 对比
| 特性 | RDB | AOF |
|---|---|---|
| 数据安全性 | 丢失快照后数据 | 丢失最近未写入磁盘的命令 |
| 性能影响 | 性能开销低,适合大数据量备份 | 性能开销较大 |
| 文件大小 | 紧凑,较小 | 日志记录多,文件较大 |
| 恢复速度 | 恢复速度快 | 恢复速度较慢 |
| 可读性 | 二进制文件,不易读 | 文本格式,易于分析 |
2.3.2 混合持久化
Redis 从 4.0 开始支持混合持久化模式(Hybrid Persistence),将 RDB 和 AOF 的优点结合:
- 在持久化时,先保存 RDB 快照,再将最近的 AOF 日志追加到 RDB 文件后。
- 这样既保证了恢复速度(RDB),也减少了数据丢失(AOF)。
配置方式:
aof-use-rdb-preamble yes
3. 淘汰策略
3.1 过期策略
3.1.1. 设置键的过期时间
可以通过以下命令为键设置过期时间:
EXPIRE key seconds:为键设置过期时间,单位为秒。PEXPIRE key milliseconds:为键设置过期时间,单位为毫秒。EXPIREAT key timestamp:设置键在指定的 Unix 时间戳(秒)到期。PEXPIREAT key timestamp:设置键在指定的 Unix 时间戳(毫秒)到期。
查询剩余过期时间:
TTL key:查询键的剩余过期时间(秒)。PTTL key:查询键的剩余过期时间(毫秒)。
3.1.2 过期策略
.1 惰性删除(Lazy Deletion)
- Redis 在读取某个键时会检查该键是否已过期。
- 如果已过期,则立即删除该键,并返回空结果。
- 优点:对 CPU 资源影响最小,仅在访问过期键时触发检查。
- 缺点:如果过期键从未被访问,则可能一直占用内存。
2 定期删除(Periodic Deletion)
- Redis 会定期扫描过期字典中的键,主动删除过期的键。
- 扫描频率和时间片由配置参数控制:
hz:每秒钟 Redis 事件循环的执行次数,影响定期任务的频率(默认为 10)。- 定期删除的扫描是分片进行的,避免一次性检查大量键导致性能抖动。
- 优点:能清理一部分过期键,避免内存占用过多。
- 缺点:可能无法及时删除所有过期键,导致内存占用延迟释放。
3.2 内存淘汰
当 Redis 内存不足时,配合过期键的清理机制,Redis 提供了以下淘汰策略:
1 不淘汰(noeviction)
- 当内存达到限制时,对写操作直接返回错误,不会删除任何键。
- 默认策略。
2 删除最近最少使用的键(LRU,Least Recently Used)
volatile-lru:从设置了过期时间的键中,删除最近最少使用的键。allkeys-lru:从所有键中删除最近最少使用的键。
3 删除最近最少访问的键(LFU,Least Frequently Used)
volatile-lfu:从设置了过期时间的键中,删除访问频率最低的键。allkeys-lfu:从所有键中删除访问频率最低的键。
4 随机删除(Random)
volatile-random:从设置了过期时间的键中,随机删除。allkeys-random:从所有键中,随机删除。
5 删除即将过期的键(TTL,Time To Live)
volatile-ttl:从设置了过期时间的键中,删除过期时间最接近的键。