Redis核心数据类型与底层实现
Redis支持的核心数据类型
Redis作为一款高性能的键值存储系统,其最大的特点之一就是支持丰富的数据类型。掌握这些数据类型及其底层实现原理,对于合理使用Redis至关重要。
五种基础数据类型
Redis中最常用的数据类型包括以下五种:
- 字符串(String): 最基础的类型,可以存储字符串、整数或浮点数
- 哈希(Hash): 键值对集合,适合存储对象
- 列表(List): 有序的字符串列表,支持双端操作
- 集合(Set): 无序的字符串集合,自动去重
- 有序集合(Sorted Set): 带分数的有序集合,常用于排行榜
高级数据类型
除了基础类型,Redis还提供了一些高级数据结构:
- Streams: Redis 5.0引入的日志型数据结构,用于消息队列场景
- Bitmap: 位图,用于高效的位级别操作
- Geospatial: 地理位置数据结构,支持附近的人等功能
- HyperLogLog: 基数统计,用于大数据量下的去重计数
SDS - Redis的字符串实现
为什么需要自定义SDS
Redis虽然使用C语言开发,但并未直接采用C语言的字符数组来实现字符串,而是自己设计了SDS(Simple Dynamic Strings)简单动态字符串。这样设计的原因主要有两点:
需求1: 支持任意字符存储
C语言的字符串使用\0作为结束标识符。当遇到\0时,C语言会认为字符串已经结束。这就导致C字符串无法存储包含\0的二进制数据,限制了其使用场景。
需求2: 高效的字符串操作
在C语言中,获取字符串长度需要遍历整个字符数组直到遇到\0,时间复杂度为O(N)。字符串追加、复制等操作同样需要遍历,效率较低。
SDS的设计思路
Redis的SDS在字符数组基础上增加了额外的元数据:
// 示例:商品信息存储
struct ProductInfo {
int len; // 当前已使用长度: 28
int alloc; // 总分配空间: 64
char buf[]; // 实际数据: "iphone_15_pro_max_256gb_blue"
}
核心优势:
- O(1)获取长度: 直接返回
len字段,无需遍历 - 高效的追加操作: 比较
len + 追加长度与alloc的大小关系,如果未超过则直接追加,超过才重新分配空间 - 支持二进制数据: 不依赖
\0判断字符串结束,可存储任意二进制内容 - 预分配策略: 减少频繁的内存重分配,提升性能
应用场景示例
// 电商订单号拼接
订单前缀 "ORD" + 时间戳 "20231201" + 用户ID "10086" + 随机数 "8899"
最终: "ORD2023120110086889"
// SDS优势: 多次拼接只需检查alloc是否足够,无需每次都重新分配内存
Redis通信协议 - RESP
RESP协议设计
Redis使用自行设计的RESP(REdis Serialization Protocol)协议进行客户端与服务端通信。该协议基于TCP,采用请求/响应模式,具有简单、高效、易于解析的特点。
协议格式
RESP协议使用\r\n作为分隔符,每条消息由以下部分组成:
*<参数个数>\r\n
$<参数1长度>\r\n
<参数1内容>\r\n
$<参数2长度>\r\n
<参数2内容>\r\n
...
实战案例
场景: 电商库存更新
// 命令: 设置商品SKU库存
SET product:sku:888888 1500
// RESP协议格式:
*3\r\n
$3\r\n
SET\r\n
$18\r\n
product:sku:888888\r\n
$4\r\n
1500\r\n
// 响应:
+OK\r\n
场景解析:
*3: 表示3个参数(SET, key, value)$3: 第一个参数长度为3SET: 命令名称$18: key的长度为18个字符product:sku:888888: 商品SKU键名$4: value长度为41500: 库存数量
协议优势
- 可读性好: 文本协议,便于调试
- 解析高效: 格式固定,解析速度快
- 支持批量操作: Pipeline、事务、Lua脚本都基于RESP实现
- 多数据类型: 支持字符串、整数、数组等多种数据类型的传输