Appearance
String(字符串)
1. 概述
String是Redis最基本的数据类型,用于存储字符串、整数或浮点数。
常用命令:SET、GET、INCR、DECR、APPEND、STRLEN
2. 底层数据结构:SDS
2.1 SDS结构定义
c
// Redis 源码中的 SDS 结构
struct sdshdr {
int len; // 已使用长度
int free; // 未使用长度
char buf[]; // 柔性数组,存储实际数据
};2.2 SDS vs C字符串
| 特性 | C字符串 | SDS |
|---|---|---|
| 获取长度 | O(n) | O(1) |
| 缓冲区溢出 | 可能 | 自动检测 |
| 内存分配 | N+1 | 预分配+惰性释放 |
| 二进制安全 | 否 | 是 |
2.3 SDS内存分配策略
c
// 预分配策略
if (newlen < SDS_MAX_PREALLOC) {
// 小于1MB,分配 2*newlen
alloc = newlen * 2;
} else {
// 大于1MB,分配 newlen + 1MB
alloc = newlen + SDS_MAX_PREALLOC;
}3. 编码方式
3.1 编码类型
| 编码 | 说明 | 最大容量 |
|---|---|---|
| int | 8字节长整型 | 2^63-1 |
| embstr | embstr编码的简单动态字符串 | 44字节 |
| raw | 简单动态字符串 | 512MB |
3.2 编码转换
int → embstr → raw当值不再是整数或超过44字节时,发生编码转换。
注意:embstr和raw的区别在于内存分配次数:
- embstr:一次分配,redisObject + sdshdr 连续内存
- raw:两次分配,redisObject和sdshdr分开
4. 面试考点
Q1: String的底层实现?
答案: Redis的String使用SDS(Simple Dynamic String)实现,结构包含len、free、buf三部分。相比C字符串,SDS支持O(1)获取长度、避免缓冲区溢出、内存预分配和惰性释放、二进制安全。
Q2: embstr vs raw的区别?
答案: embstr将redisObject和sdshdr放在连续内存中,减少内存分配次数和缓存失效开销。raw则分开存储。当字符串长度超过44字节或值被修改时,会从embstr转为raw编码。
Q3: 为什么不使用C字符串?
答案:
- C字符串获取长度需要O(n)遍历
- 不支持二进制安全(不能包含\0)
- 缓冲区溢出风险
- 频繁内存分配/释放
5. 应用场景
5.1 缓存
bash
SET user:1001:name "张三"
SET user:1001:age "25"
GET user:1001:name5.2 计数器
bash
INCR article:1001:views # 原子递增
DECR stock:100 # 原子递减
INCRBY order:1001:count 10 # 指定增量5.3 分布式锁
lua
-- SETNX实现
SET lock_key unique_value NX EX 30
-- 释放锁(Lua脚本)
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end5.4 限流
lua
-- 滑动窗口限流
local key = "rate_limit:" .. ARGV[1]
local limit = tonumber(ARGV[2])
local window = tonumber(ARGV[3])
local current = redis.call("incr", key)
if current == 1 then
redis.call("expire", key, window)
end
return current <= limit and 1 or 06. 实践问题
Q: String在Redis中最大能存多大?
答案: 512MB。实际应用中建议控制在MB级别以内,大值会影响网络传输和内存管理。
Q: INCR的原子性如何保证?
答案: Redis单线程模型保证了所有命令的原子性,INCR操作不会被其他命令打断。Lua脚本中可组合多个操作保证原子性。
