一、String(字符串)
核心实现:SDS(Simple Dynamic String)
struct sdshdr {
uint64_t len; // 已使用长度(O(1)获取长度)
uint64_t alloc; // 总分配空间(不含header)
unsigned char flags; // 类型标识(SDS_TYPE_8/16/32/64)
char buf[]; // 柔性数组(实际数据)
};
Java视角特性:
二进制安全:可存储任意二进制数据(如图片),非仅文本
内存预分配:
扩容策略:
len < 1MB
时双倍扩容,≥1MB
时每次扩1MB惰性释放:缩短时不立即回收内存
编码优化:
EMBSTR:redisObject和SDS连续存储(减少内存碎片)
INT:直接存储长整型值(无额外内存开销)
二、List(列表)
底层结构演进:
≤Redis 2.8:双向链表(
linkedlist
)Redis 3.2+:QuickList
typedef struct quicklist {
quicklistNode *head;
quicklistNode *tail;
unsigned long count; // 总元素数
unsigned long len; // quicklistNode数量
int fill : QL_FILL_BITS; // ziplist最大大小
unsigned int compress : QL_COMP_BITS; // 压缩深度
} quicklist;
typedef struct quicklistNode {
struct quicklistNode *prev;
struct quicklistNode *next;
unsigned char *zl; // 指向ziplist
size_t sz; // ziplist字节大小
unsigned int count : 16; // ziplist元素数
unsigned int encoding : 2; // 编码类型
unsigned int container : 2; // 存储方式
unsigned int recompress : 1; // 临时解压标记
} quicklistNode;
Java应用场景:
消息队列:LPUSH/RPOP实现FIFO
分页查询:LRANGE key 0 9 获取前10条
性能优化点:
调整
list-max-ziplist-size
控制ziplist节点大小设置
list-compress-depth
压缩中间节点(首尾不压缩)
三、Hash(哈希表)
底层结构:
Ziplist(紧凑列表):
触发条件:字段数≤
hash-max-ziplist-entries
(默认512)且 所有值长度≤hash-max-ziplist-value
(默认64字节)存储格式:
<key1><value1><key2><value2>...
Dict(哈希表):
typedef struct dict { dictEntry **table; // 哈希桶数组 unsigned long size; // 桶数量 unsigned long sizemask; // size-1(用于取模) unsigned long used; // 元素数量 } dict; typedef struct dictEntry { void *key; union { void *val; uint64_t u64; double d; } v; struct dictEntry *next; // 链地址法解决冲突 } dictEntry;
Java最佳实践:
存储对象:替代JSON序列化,支持字段级操作
配置优化:根据字段平均大小调整
hash-max-ziplist-value
四、Set(集合)
底层结构:
Intset(整数集合):
typedef struct intset {
uint32_t encoding; // INTSET_ENC_INT16/32/64
uint32_t length;
int8_t contents[]; // 有序数组
} intset;
触发条件:元素均为整数且数量≤
set-max-intset-entries
(默认512)Dict(哈希表):
特殊实现:value固定为NULL,仅用key存储数据
Java场景对比:
操作 | Intset复杂度 | Dict复杂度 | Java等价类 |
---|---|---|---|
SADD | O(n) | O(1) | HashSet.add() |
SISMEMBER | O(log n) | O(1) | HashSet.contains() |
SUNION | O(n) | O(n) | Set.addAll() |
五、ZSet(有序集合)
双重结构:
typedef struct zset {
dict *dict; // 哈希表:member->score
zskiplist *zsl; // 跳跃表:按score排序
} zset;
// 跳跃表节点
typedef struct zskiplistNode {
sds ele;
double score;
struct zskiplistNode *backward; // 后退指针
struct zskiplistLevel {
struct zskiplistNode *forward;
unsigned long span; // 跨越节点数
} level[]; // 柔性数组
} zskiplistNode;
跳跃表特性:
层高随机生成(1/4概率升级)
支持O(log n)范围查询(ZRANGEBYSCORE)
平衡树替代方案:避免旋转操作,更易实现
Java使用场景:
排行榜:ZINCRBY + ZREVRANGE
延迟队列:score设为执行时间戳
六、高级类型实现
1. Bitmap(位图)
本质:String的位操作扩展
SETBIT key offset 1:扩展SDS并置位
GETBIT:位运算定位字节位置
BITCOUNT:使用SWAR算法加速统计
// 汉明重量计算(每4位查表) static const unsigned char popcount_tab[256] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4, ... };
2. HyperLogLog
核心结构:
struct hllhdr {
char magic[4]; // "HYLL"
uint8_t encoding; // HLL_DENSE 或 HLL_SPARSE
uint8_t notused[3]; // 保留字段
uint8_t card[8]; // 缓存基数
uint8_t registers[]; // 16384个6bit寄存器
};
误差率:0.81%/√m(m=16384)
3. Stream
实现:Rax树(Radix Tree)
typedef struct raxNode {
uint32_t iskey:1; // 是否包含key
uint32_t isnull:1; // 关联值是否为空
uint32_t iscompr:1; // 是否压缩存储
uint32_t size:29; // 子节点数/压缩字符串长度
unsigned char data[]; // 柔性数组存储路径
} raxNode;
消息ID:<millisecondsTime>-<sequenceNumber>
消费者组:通过PEL(Pending Entries List)实现
Java开发者启示
内存优化:
小数据用ziplist/intset(调整max-*-entries参数)
避免大Key:String≤10KB,集合元素≤5000
API选择:
// 错误:多次网络IO for(String item : items) { jedis.sadd(key, item); } // 正确:管道批处理 Pipeline p = jedis.pipelined(); for(String item : items) { p.sadd(key, item); } p.sync();
持久化影响:
RDB:ziplist可直接序列化,内存页高效dump
AOF:SDS格式对rewrite友好
集群分片:
HashTag确保相关数据同slot:
{user1000}.orders
黄金法则:
理解底层结构才能写出高性能Redis代码,特别是当QPS > 10k时,数据结构选择直接影响集群规模成本。