Skip to content

Redis的数据类型介绍

基础数据类型

1. 字符串 (String)

字符串是 Redis 最基本的数据类型,可以存储任何形式的二进制安全数据,例如文本、序列化的 JSON 对象或图片,最大容量可达 512MB。

底层实现: Redis 并没有直接使用 C 语言传统的字符串表示(以空字符 \0 结尾的字符数组),而是自行设计了一种名为简单动态字符串 (Simple Dynamic String, SDS) 的结构。

SDS 的优势: * O(1) 复杂度获取字符串长度:SDS 结构中直接记录了字符串的长度,避免了 C 字符串需要遍历整个字符串才能确定长度的低效操作。 * 杜绝缓冲区溢出:在对字符串进行修改时,SDS 会自动检查可用空间并按需扩容,防止了 C 字符串常见的缓冲区溢出问题。 * 二进制安全:SDS 可以存储包含 \0 字符的数据,因为它不依赖 \0 来判断字符串的结尾,而是通过 len 属性。 * 空间预分配与惰性空间释放:为了优化性能,SDS 在扩容时会分配比实际需要更多的空间(空间预分配),而在缩短字符串时并不会立即释放多余的空间,以备将来使用(惰性空间释放)。

编码方式: 为了节约内存,String 类型会根据存储内容的不同选择不同的编码方式: * int: 当字符串可以被解析为长整型时,会使用该编码,直接存储整数值。 * embstr: 当字符串长度小于等于 44 字节时,采用 embstr 编码。redisObject 的元信息和 SDS 结构在内存中是连续分配的,可以更好地利用缓存。 * raw: 当字符串长度大于 44 字节时,使用 raw 编码。redisObject 和 SDS 结构在内存中是分开分配的。

2. 列表 (List)

列表是简单的字符串列表,按照插入顺序排序。你可以在列表的头部(左侧)或尾部(右侧)添加元素。 列表非常适合实现消息队列、栈等数据结构。

底层实现: List 类型的底层实现在 Redis 的不同版本中有所演进: * Redis 3.2 之前: 使用双向链表 (linkedlist)压缩列表 (ziplist)。 * 当列表元素较少且每个元素都较小时,使用 ziplist 来节省内存。 * 当数据量变大时,则转换为 linkedlist。 * Redis 3.2 及之后: 引入了 quicklistquicklistlinkedlistziplist 的混合体,它是一个由 ziplist 构成的双向链表。 这种结构在空间效率和操作性能之间取得了很好的平衡。 * Redis 7.0 及之后: ziplist 被功能更优的 listpack 所取代。listpack 解决了 ziplist 存在的连锁更新问题。

3. 哈希 (Hash)

哈希是一个键值对 (field-value) 集合,非常适合用于存储对象。

底层实现: Hash 类型的底层实现也有两种方式: * ziplist/listpack: 当哈希对象中保存的键值对数量较少,并且所有键和值的字符串长度都小于特定阈值(默认为 64 字节)时,会使用 ziplist(7.0 版本后为 listpack)进行存储,以节约内存。 * 哈希表 (hashtable): 当不满足上述条件时,Redis 会使用哈希表来存储。Redis 的哈希表实现类似于 Java 中的 HashMap,采用数组加链表的形式,通过“拉链法”解决哈希冲突。 为了在扩容或缩容时保持高效,Redis 采用了渐进式 rehash 的策略。

4. 集合 (Set)

集合是字符串类型的无序集合,集合中的元素是唯一的。 它支持高效的成员检查、添加、删除操作,以及并集、交集、差集等集合运算。

底层实现: Set 类型的底层实现同样根据情况选择不同的数据结构: * 整数集合 (intset): 当集合中的所有元素都是整数,并且元素数量不超过一定阈值(默认为 512)时,会使用 intsetintset 是一个有序的、无重复的整数数组。 * 哈希表 (hashtable): 当集合中的元素不全是整数,或者元素数量超过阈值时,会转换为使用哈希表存储。 在这种情况下,哈希表的键存储集合的元素,而值则统一设置为 NULL

5. 有序集合 (Sorted Set / ZSet)

有序集合和集合一样,也是字符串的集合,且不允许重复。不同的是,每个元素都会关联一个 double 类型的分数 (score)。Redis 正是通过分数来为集合中的成员进行从小到大的排序。 有序集合非常适合用于实现排行榜等功能。

底层实现: * ziplist/listpack: 当有序集合的元素数量较少,且每个元素的长度都小于特定阈值时,使用 ziplist(7.0 版本后为 listpack)。在 ziplist 中,每个集合元素由两个紧邻的节点表示,一个存成员 (member),一个存分数 (score)。 * 跳跃表 (skiplist) + 哈希表 (dict): 当不满足上述条件时,有序集合会使用跳跃表和哈希表的组合。 * 跳跃表 (skiplist): 用于按分数范围高效地进行范围查找和排序。 * 哈希表 (dict): 用于以 O(1) 的时间复杂度快速查找指定成员的分数。 这种结合使用空间换时间的方式,保证了单元素查询和范围查询的高效性。

特殊数据类型

除了基础数据类型,Redis 还提供了一些针对特殊场景的强大数据类型。

1. Bitmap (位图)

Bitmap 本身并不是一个独立的数据类型,而是基于 String 类型实现的一种统计二值状态(0 或 1)的数据类型。 Redis 将字节数组的每个 bit 位都利用起来,非常适合用于记录大量的布尔值状态,例如用户签到、在线状态等,能极大地节省内存空间。

2. HyperLogLog

HyperLogLog 是一种概率数据结构,用于对集合的基数(不重复元素的数量)进行估算。 它的优点是,在输入元素的数量或者体积非常大的时候,计算基数所需的空间总是固定的,并且是很小的。 虽然存在一定的误差率(标准误差为 0.81%),但对于像统计网站 UV 这种不需要绝对精确计数的场景非常适用。

3. Geospatial (GEO)

GEO 是 Redis 3.2 版本新增的功能,主要用于存储地理位置信息,并对存储的信息进行操作。 它的底层是基于 Sorted Set 实现的。 GEO 类型使用 GeoHash 算法将二维的经纬度编码成一维的整数(作为 score),从而可以利用 Sorted Set 的范围查询能力,高效地实现“附近的人”等地理位置相关的查询功能。