跳到主要内容

Redis核心数据类型与底层实现

Redis支持的核心数据类型

Redis作为一款高性能的键值存储系统,其最大的特点之一就是支持丰富的数据类型。掌握这些数据类型及其底层实现原理,对于合理使用Redis至关重要。

五种基础数据类型

Redis中最常用的数据类型包括以下五种:

  1. 字符串(String): 最基础的类型,可以存储字符串、整数或浮点数
  2. 哈希(Hash): 键值对集合,适合存储对象
  3. 列表(List): 有序的字符串列表,支持双端操作
  4. 集合(Set): 无序的字符串集合,自动去重
  5. 有序集合(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"
}

核心优势:

  1. O(1)获取长度: 直接返回len字段,无需遍历
  2. 高效的追加操作: 比较len + 追加长度alloc的大小关系,如果未超过则直接追加,超过才重新分配空间
  3. 支持二进制数据: 不依赖\0判断字符串结束,可存储任意二进制内容
  4. 预分配策略: 减少频繁的内存重分配,提升性能

应用场景示例

// 电商订单号拼接
订单前缀 "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: 第一个参数长度为3
  • SET: 命令名称
  • $18: key的长度为18个字符
  • product:sku:888888: 商品SKU键名
  • $4: value长度为4
  • 1500: 库存数量

协议优势

  1. 可读性好: 文本协议,便于调试
  2. 解析高效: 格式固定,解析速度快
  3. 支持批量操作: Pipeline、事务、Lua脚本都基于RESP实现
  4. 多数据类型: 支持字符串、整数、数组等多种数据类型的传输