Redis(一):Redis中的数据类型

94 阅读6分钟

Redis 是一个高性能的键值数据库,但它远超普通的 key-value,因为它的值支持 5 大核心数据结构,包括:String、Hash、List、Set、ZSet,以及高级的结构:Bitmaps、HyperLogLog、GEO。每个都适用于不同的场景。理解它们的底层结构和行为差异,是写出高性能 Redis 应用的前提。

基本数据类型

String(字符串)

特点:

● 最常用的数据结构,占据 Redis 使用的 90% ● value 最大支持 512MB

底层结构:

使用 SDS(Simple Dynamic String) 实现(而不是 C 字符串)

struct sdshdr {
    int len;       // 实际长度
    int free;      // 剩余空间
    char buf[];    // 字符数组,结尾不带 \0
}

O(1) 获取长度,不需遍历 支持动态扩容和缩容,避免内存碎片

应用形式

● 普通值(name → "Tom") ● 数字型(可以做 INCR/DECR) ● 二进制(可存图片、压缩包等)

SET key value
GET key
INCR age
APPEND key "abc"
GETRANGE key 0 3

List(双端链表)

特点:

● 有序、可重复,适合队列、消息流等场景 ● 可从两端插入/弹出(LPUSH / RPUSH)

底层结构:

  1. QuickList(Redis 3.2+):由多个 ZipList 压缩表组成的链表
  2. 老版本用 LinkedList,但性能差、内存碎

插入复杂度:

操作时间复杂度
LPUSH/ RPUSHO(1)
LPOP / RPOPO(1)
LINDEO(N)
LRANGEO(N)

示例命令:

LPUSH queue task1
RPUSH queue task2
LPOP queue
LRANGE queue 0 -1

Set(无序集合)

特点:

● 无序、去重 ● 支持交集、并集、差集操作

底层结构:

● 小数据量:intset(整数集合,总元素数量不超过 set-max-intset-entries(默认是 512)) ● 大数据量或者非整数成员:hashtable(哈希表) 自动切换,无需手动干预。

示例命令:

SADD tags "java"
SADD tags "redis"
SMEMBERS tags
SISMEMBER tags "java"
SUNION set1 set2

SortedSet(有序集合)

特点:

  • 元素唯一,分值(score)可重复
  • 按分值排序 → 实现排行榜、延迟队列

底层结构:

组合结构:

  • 跳表(SkipList):支持按分数排序
  • 哈希表:支持快速查找

复杂度:

操作时间复杂度
ZADD/ZREMO(logN)
ZRANGE/ZREVRANKO(logN + M)
ZSCORE/ZCARDO(1)

示例命令:

ZADD rank 100 "Alice"
ZADD rank 200 "Bob"
ZRANGE rank 0 -1 WITHSCORES
ZREVRANK rank "Alice"
ZREM rank "Bob"

Hash(键值对集合)

特点:

  • 存储对象结构(如用户信息、商品信息)
  • 可以看成小型的 Redis 本身:user:1001 → {name:Tom, age:18}

底层结构:

  • 小数据量:ziplist(压缩列表)
  • 大数据量:hashtable 自动转换触发条件:
  • 单个键值 > 64 字节
  • 元素数量 > 512 个
HSET user:1001 name "Tom"
HSET user:1001 age 18
HGETALL user:1001
HINCRBY user:1001 age 1
HDEL user:1001 name

重点汇总表

数据结构是否有序是否唯一是否支持分数典型场景底层结构
String缓存、计数器SDS
List消息队列QuickList
Set✅ 是标签系统intset/hashtable
SortedSet✅ 是✅ 是✅ 是排行榜、延迟队列跳表 + 哈希表
Hash无序key 唯一用户对象缓存ziplist/hashtable

高级类型

Bitmaps

现代计算机用二进制(位)作为信息的基础单位,1个字节等于8位,例如“big”字符串是由3个字节组成,但实际在计算机存储时将其用二进制表示,“big”分别对应的ASCII码分别是98、105、103,对应的二进制分别是01100010、01101001和 01100111。 Bitmaps本身不是一种数据结构,实际上它就是字符串,但是它可以对字符串的位进行操作。 Bitmaps单独提供了一套命令,所以在Redis中使用Bitmaps和使用字符串的方法不太相同。可以把 Bitmaps想象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在 Bitmaps 中叫做偏移量。

布隆过滤器

1970 年布隆提出了一种布隆过滤器的算法,用来判断一个元素是否在一个集合中。 这种算法由一个二进制数组和一个 Hash 算法组成。 本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。 相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的。 实际上,布隆过滤器广泛应用于网页黑名单系统、垃圾邮件过滤系统、爬虫网址判重系统等,Google 著名的分布式数据库 Bigtable 使用了布隆过滤器来查找不存在的行或列,以减少磁盘查找的IO次数,Google Chrome浏览器使用了布隆过滤器加速安全浏览服务。

HyperLogLog

HyperLogLog(简称 HLL)是 Redis 提供的一种基数估计(Cardinality Estimation)数据结构,用于统计唯一元素的数量,比如:

有多少个不同的 IP 访问了网站?有多少个独立用户点击了广告?

命令:
pfadd
PFADD uv:2025-07-28 user_id_001
(integer) 1
PFADD uv:2025-07-28 user_id_002
(integer) 1
pfcount
PFCOUNT uv:2025-07-28
(integer) 2
pfmerge
PFADD uv:2025-07-27 user_id_003
PFMERGE uv:all uv:2025-07-27 uv:2027-07-28
(integer) 3

HyperLogLog 基于概率论中伯努利试验并结合了极大似然估算方法,并做了分桶优化。

  1. List item 转为比特串 通过hash函数,将数据转为64位二进制的比特串。
  2. 分桶 前14位作为桶编号, 分布不同数据,避免局部集中偏差。 Redis 开发者(antirez)根据实验选择了 p=14(即 m=16384),因为这在误差与空间上达到了最佳平衡。
哈希值(64位):1010 1100 0110 0101 ...(64位总长)

→ 前 14 位(桶编号):10101110011001 → bucket 11257
→ 后 50 位(估稀有性):00000010011... → 前导 0 = 6 → register[11257] = max(当前, 6)
Redis 为什么选 14 记录桶的编号?

Redis 开发者(antirez)根据实验选择了 p=14(即 m=16384),因为这在误差与空间上达到了最佳平衡:

p桶数m误差(约)空间消耗(每个桶 6 bits)
101024~3.25%0.75 KB
124096~1.63%3 KB
✅14✅16384✅~0.81%✅12 KB(Redis 使用的)
1665536~0.40%48 KB
18262144~0.20%192 KB

主要特点

特性描述
功能统计去重后的元素数量(基数)
占用内存永远不超过 12 KB,无论添加多少元素
是否精确❌ 不精确(误差约 0.81%),但足够用于大数据统计场景
支持命令PFADD、PFCOUNT、PFMERGE
典型用途UV统计、独立用户量、唯一访问者、唯一交易用户等

GEO

Redis GEO 用于 存储地理坐标(经纬度) 并支持基于位置的 距离计算、范围查询、附近搜索 等操作。

# 添加两个坐标
GEOADD locations 116.397128 39.916527 beijing
GEOADD locations 121.473701 31.230416 shanghai

# 计算两地距离(默认单位:米)
GEODIST locations beijing shanghai

# 指定单位为千米
GEODIST locations beijing shanghai km