Skip to content

String(字符串)

1. 概述

String是Redis最基本的数据类型,用于存储字符串、整数或浮点数。

常用命令SETGETINCRDECRAPPENDSTRLEN

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 编码类型

编码说明最大容量
int8字节长整型2^63-1
embstrembstr编码的简单动态字符串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字符串?

答案:

  1. C字符串获取长度需要O(n)遍历
  2. 不支持二进制安全(不能包含\0)
  3. 缓冲区溢出风险
  4. 频繁内存分配/释放

5. 应用场景

5.1 缓存

bash
SET user:1001:name "张三"
SET user:1001:age "25"
GET user:1001:name

5.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
end

5.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 0

6. 实践问题

Q: String在Redis中最大能存多大?

答案: 512MB。实际应用中建议控制在MB级别以内,大值会影响网络传输和内存管理。

Q: INCR的原子性如何保证?

答案: Redis单线程模型保证了所有命令的原子性,INCR操作不会被其他命令打断。Lua脚本中可组合多个操作保证原子性。

最后更新: