Redis5种基本数据类型和3种特殊数据类型介绍

引言

在当今这个数据驱动的时代,高效的数据存储与访问能力成为衡量系统性能的关键标尺。作为最受欢迎的内存数据库之一,Redis以其惊人的速度和灵活的数据模型,成为了高并发场景下的明星选手。但你是否曾好奇,究竟是什么让Redis能在微秒级别响应海量请求?答案就藏在它精心设计的数据结构之中。

Redis远不止是一个简单的键值存储------它是一个拥有丰富数据类型生态系统的数据结构服务器。从看似简单的字符串到复杂的概率数据结构,每一种数据类型都针对特定场景进行了深度优化。理解这些数据结构的特性、底层原理和适用场景,就像是掌握了Redis这位"瑞士军刀"中的每一把工具,让你能够根据不同的业务需求选择最合适的解决方案。

想象一下这样的场景:你需要统计千万级用户的日活跃量,但又不想消耗数十GB内存;你需要实现实时排行榜,同时支持快速的范围查询;你需要管理地理位置数据,提供"附近的人"这样的功能......这些看似复杂的需求,在Redis中都有优雅的解决方案。

本篇博客将带你深入探索Redis的五种基本数据类型和三种特殊数据类型。我们将从底层实现原理出发,通过实际案例解析,为你揭示:

如何用String实现高性能分布式锁

如何用HyperLogLog仅用12KB统计亿级UV

如何用Geospatial构建实时地理位置服务

以及更多数据结构的实战技巧与性能调优策略

无论你是正在学习Redis的初学者,还是希望优化现有系统性能的资深开发者,这篇文章都将为你提供有价值的见解和实用的解决方案。让我们一同开启这场Redis数据结构探索之旅,解锁高效数据处理的密码。

2. 五种基础数据类型

2.1 String(字符串)

底层数据结构: SDS(Simple Dynamic String)简单动态字符串

自动扩容,减少内存分配次数

二进制安全,可存储任意数据

O(1)获取字符串长度

bash 复制代码
SET key value [EX seconds] [PX milliseconds] [NX|XX]
GET key
INCR key          # 原子递增
DECR key          # 原子递减
APPEND key value  # 追加字符串
STRLEN key        # 获取长度
MSET/MGET         # 批量操作

优点

操作简单高效

支持丰富的数据操作(原子增减等)

可存储任意二进制数据

缺点

大字符串可能占用大量内存

频繁修改可能导致内存碎片

应用场景

缓存数据(用户信息、商品信息)

计数器(浏览量、点赞数)

分布式锁

Session存储

2.2 List(列表)

底层数据结构: quicklist(Redis 3.2+)

双向链表 + ziplist组合

每个节点是一个压缩的ziplist

早期版本:ziplist(元素少时)/ linkedlist(元素多时)

bash 复制代码
LPUSH/RPUSH key element1 [element2...]
LPOP/RPOP key
LRANGE key start stop  # 获取指定范围元素
LINDEX key index       # 按索引获取元素
LLEN key               # 获取长度
BLPOP/BRPOP            # 阻塞式弹出

优点

  • 两端插入删除O(1)
  • 支持分页查询
  • 可实现队列和栈

缺点

  • 随机访问性能差
  • 中间插入删除成本高

应用场景

  • 消息队列
  • 最新消息/文章列表
  • 关注列表
  • 历史记录

2.3 Hash(哈希表)

底层数据结构 ziplist(元素少时)

压缩列表,内存连续

dict(hashtable)(元素多时)

数组+链表实现

渐进式rehash

bash 复制代码
HSET key field value
HGET key field
HGETALL key           # 获取所有字段
HMSET key field1 value1 [field2 value2...]
HDEL key field1 [field2...]
HINCRBY key field increment

优点

适合存储对象

字段可单独操作

内存效率较高(ziplist时)

缺点

大Hash可能rehash阻塞

不适合存储超大规模数据

应用场景

用户信息存储

购物车

对象属性存储

配置信息

2.4 Set(集合)

底层数据结构

  • intset(元素都为整数且数量少时)有序整数数组
  • dict(hashtable)(默认), 只使用键,值为NULL
bash 复制代码
SADD key member1 [member2...]
SMEMBERS key         # 获取所有成员
SISMEMBER key member # 判断是否存在
SINTER key1 key2     # 交集
SUNION key1 key2     # 并集
SDIFF key1 key2      # 差集
SCARD key            # 获取基数

优点

去重能力强

集合运算高效

判断存在性O(1)

缺点

无序存储

大数据集可能占用较大内存

应用场景

标签系统

共同好友/关注

抽奖/随机推荐

数据去重

2.5 ZSet(有序集合)

底层数据结构

  • ziplist(元素少时)
  • skiplist + dict(默认)
    • skiplist:支持范围查询
    • dict:O(1)查找分数
bash 复制代码
ZADD key score1 member1 [score2 member2...]
ZRANGE key start stop [WITHSCORES]  # 按排名范围查询
ZREVRANGE key start stop            # 倒序查询
ZRANGEBYSCORE key min max           # 按分数范围查询
ZSCORE key member                   # 获取分数
ZRANK key member                    # 获取排名
ZINCRBY key increment member        # 增加分数

优点

有序且支持范围查询

元素唯一性

性能优秀(查询、插入O(logN))

缺点

内存占用相对较大

比普通集合复杂

应用场景

排行榜

延时队列

带权重的队列

时间轴(按时间排序)

实际案例

String案例:电商用户会话管理

实际场景:用户登录后,存储会话信息,记录用户状态,实现简单的分布式锁控制。

bash 复制代码
# 存储用户会话信息(30分钟过期)
SET session:user123 '{"userId":123,"username":"张三","loginTime":"2024-02-15 10:00","cartCount":3}' EX 1800

# 获取会话信息
GET session:user123

# 用户添加商品到购物车,更新购物车数量
INCRBY user:123:cart:count 1

# 记录用户最后活跃时间
SET user:123:last_active "2024-02-15 10:30:00"

# 分布式锁:秒杀商品库存
SET product:1001:lock "locked" NX PX 10000  # 10秒锁
# 业务处理...
DEL product:1001:lock

List案例:微博消息时间线

实际场景:社交媒体平台的消息流,最新消息在列表前端,支持分页查看。

bash 复制代码
# 用户发表新微博
LPUSH user:123:timeline "今天天气真好!#阳光#"
LPUSH global:timeline "user123:今天天气真好!#阳光#"

# 获取用户最近20条微博
LRANGE user:123:timeline 0 19

# 关注系统:当用户关注的人发微博时
# 用户123关注了用户456,当456发微博时:
LPUSH user:123:feed "user456:发布了新照片"

# 实现简单的消息队列(任务队列)
LPUSH task:queue '{"type":"email","to":"user@example.com","content":"..."}'
BRPOP task:queue 30  # 工作进程阻塞获取任务

Hash案例:电商商品详情页

实际场景:商品信息缓存,购物车实现,可以单独更新某个字段而不用读取整个对象。

bash 复制代码
# 存储商品信息
HSET product:1001 "name" "iPhone 15 Pro"
HSET product:1001 "price" "8999"
HSET product:1001 "stock" "100"
HSET product:1001 "description" "最新款苹果手机"
HSET product:1001 "sales" "1500"

# 获取商品所有信息
HGETALL product:1001

# 用户购物车(用户ID: 123)
HSET cart:user:123 "product:1001" 2  # 商品1001,数量2
HSET cart:user:123 "product:2002" 1  # 商品2002,数量1

# 更新商品库存(原子操作)
HINCRBY product:1001 "stock" -1

# 用户下单后获取购物车所有商品
HGETALL cart:user:123

Set案例:社交网络好友关系

实际场景:社交网络的好友关系、共同兴趣推荐、标签系统、抽奖活动。

bash 复制代码
# 用户添加好友
SADD user:123:friends "456" "789" "101"
SADD user:456:friends "123" "789"

# 判断是否是好友
SISMEMBER user:123:friends "456"  # 返回1,是好友

# 共同好友计算
SINTER user:123:friends user:456:friends  # 返回789,共同好友

# 可能认识的人(差集)
SDIFF user:456:friends user:123:friends  # 用户456的好友中,用户123没有的

# 标签系统
SADD product:1001:tags "手机" "苹果" "高端"
SADD product:1002:tags "手机" "安卓" "性价比"

# 查找包含"手机"标签的商品
SINTER product:1001:tags product:1002:tags  # 共同标签"手机"

# 抽奖活动参与用户
SADD lottery:202402 "user1" "user2" "user3"
SCARD lottery:202402  # 统计参与人数
# 随机抽取3名获奖者
SRANDMEMBER lottery:202402 3

ZSet案例:游戏排行榜系统

实际场景:游戏积分排行榜、热点新闻排序、延时任务调度、时间轴功能。

bash 复制代码
# 玩家得分更新
ZADD game:leaderboard 1500 "player1"
ZADD game:leaderboard 2200 "player2"
ZADD game:leaderboard 1800 "player3"

# 玩家得分增加
ZINCRBY game:leaderboard 100 "player1"  # player1得分增加100

# 获取排行榜前10名
ZREVRANGE game:leaderboard 0 9 WITHSCORES
# 返回:
# 1) "player2"
# 2) "2200"
# 3) "player3"
# 4) "1800"
# ...

# 获取玩家排名
ZREVRANK game:leaderboard "player1"  # 返回排名(从0开始)

# 获取分数在1700-2000之间的玩家
ZRANGEBYSCORE game:leaderboard 1700 2000 WITHSCORES

# 延时任务队列(时间戳作为score)
ZADD delay:queue 1707962400 "task:send_email:123"  # 2024-02-15 10:00:00
ZADD delay:queue 1707962460 "task:clean_cache"

# 获取当前时间之前的所有任务
ZRANGEBYSCORE delay:queue 0 1707962410 WITHSCORES  # 获取10:00:10之前的所有任务

综合案例:电商系统完整实现

bash 复制代码
# 1. 用户登录(String)
SETEX session:${userId} 1800 '{"userId":123,"username":"张三"}'

# 2. 浏览商品历史(List)
LPUSH user:123:history "product:1001"
LTRIM user:123:history 0 49  # 只保留最近50条

# 3. 商品详情(Hash)
HMSET product:1001 name "iPhone" price "8999" stock "100"

# 4. 商品标签(Set)
SADD product:1001:tags "手机" "苹果" "高端"

# 5. 商品销量排行榜(ZSet)
ZINCRBY product:sales:daily 1 "product:1001"

# 6. 用户购物车(Hash)
HSET cart:user:123 "product:1001" 2

# 7. 限时秒杀(String + 过期时间)
SET product:1001:seckill:stock 100 EX 3600  # 1小时秒杀
DECR product:1001:seckill:stock  # 原子减库存

# 8. 用户订单队列(List作为队列)
LPUSH order:queue '{"orderId":"ORD123","userId":123,"products":[{"id":1001,"qty":2}]}'
BRPOP order:queue 30  # 订单处理服务消费

各数据类型选择技巧总结

String:简单KV,需要原子操作的计数器

List:需要按时间顺序排列的数据,队列/栈

Hash:对象存储,需要单独操作字段

Set:需要去重,集合运算(交集/并集)

ZSet:需要排序和范围查询

性能优化提示

bash 复制代码
# 批量操作减少网络开销
MGET session:user1 session:user2 session:user3

# Pipeline批量执行
# 客户端代码示例(伪代码):
pipeline = redis.pipeline()
pipeline.incr("counter")
pipeline.set("key", "value")
pipeline.execute()

# 合理设置过期时间避免内存泄漏
SETEX key 3600 value  # 1小时后过期
EXPIRE key 3600       # 设置已有key的过期时间

小结

类型 时间复杂度 内存效率 适用场景
String O(1) 简单KV缓存、计数器
List O(1)两端操作 队列、栈、时间线
Hash O(1)单字段 对象存储、购物车
Set O(1)存在性 标签、去重、社交关系
ZSet O(logN)操作 排行榜、优先级队列

3. 三种特殊数据类型

3.1 HyperLogLog(基数统计)

HyperLogLog是一种用于估计集合中不重复元素数量的概率数据结构。

底层原理

使用16384个6KB的寄存器(共约12KB内存)

基于概率算法,不是精确计数

标准错误率约为0.81%

使用调和平均数来减少异常值影响

bash 复制代码
PFADD key element [element...]  # 添加元素
PFCOUNT key [key...]            # 计算基数估计值
PFMERGE destkey sourcekey [sourcekey...]  # 合并多个HLL

优点

内存占用极小:无论集合多大,都只需要约12KB

合并成本低:可以快速合并多个HyperLogLog

添加元素快速:O(1)时间复杂度

缺点

不精确:存在约0.81%的标准误差

无法获取具体元素:只能估算数量,不能获取元素内容

无法删除元素:只能整体重置

应用场景

网站UV统计(每日/每月独立访客数)

大数据去重估算(如:估算某关键词搜索用户数)

社交网络共同好友数估算

API调用去重统计

bash 复制代码
# 统计某文章页面的UV
PFADD article:123:uv user1 user2 user3
PFCOUNT article:123:uv

# 合并三天的UV数据
PFADD day1 user1 user2 user3
PFADD day2 user2 user3 user4
PFMERGE total_uv day1 day2
PFCOUNT total_uv  # 估算总UV

3.2 Bitmap(位图)

概述

Bitmap本质上是一个String类型,但可以当作位数组操作,每个bit只能存储0或1。

底层原理

基于String类型实现

使用SDS(简单动态字符串)存储二进制位

自动扩容,按需分配内存

支持位操作(AND、OR、NOT、XOR)

bash 复制代码
SETBIT key offset value    # 设置指定偏移量的位值
GETBIT key offset          # 获取指定偏移量的位值
BITCOUNT key [start end]   # 统计值为1的位数
BITOP operation destkey key [key...]  # 位运算
BITPOS key bit [start] [end]  # 查找第一个为0/1的位

优点

内存效率极高:每个用户仅需1bit

操作速度快:支持大规模位运算

支持复杂查询:支持范围统计和位运算

持久化方便:可以导出为二进制文件

缺点

稀疏数据浪费内存:稀疏分布时效率低

偏移量限制:最大偏移量为2^32-1

需要预分配内存:可能产生内存碎片

应用场景

用户签到系统

用户活跃度统计

布隆过滤器实现

用户标签系统

实时用户在线状态

bash 复制代码
# 用户签到系统
SETBIT user:sign:202402:1001 15 1  # 用户1001在2月16日签到
GETBIT user:sign:202402:1001 15    # 查看是否签到
BITCOUNT user:sign:202402:1001     # 统计本月签到次数

# 用户在线状态
SETBIT online:users 1001 1         # 用户1001上线
SETBIT online:users 1001 0         # 用户1001下线
BITCOUNT online:users              # 统计在线人数

3.3 Geospatial(地理空间)

Geospatial用于存储地理位置信息,支持半径查询、距离计算等地理空间操作。

底层原理

基于ZSet(有序集合)实现

使用GeoHash算法将经纬度编码为52位整数

使用Haversine公式计算球面距离

数据存储在ZSet中,分数为GeoHash值

bash 复制代码
GEOADD key longitude latitude member [longitude latitude member...]
GEOPOS key member [member...]       # 获取位置
GEODIST key member1 member2 [unit]  # 计算距离
GEORADIUS key longitude latitude radius unit [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC]
GEORADIUSBYMEMBER key member radius unit [options...]  # 按成员查询附近
GEOHASH key member [member...]      # 获取GeoHash字符串

优点

查询效率高:基于ZSet的跳表实现,范围查询快

功能丰富:支持多种地理查询

集成度高:原生支持,无需外部依赖

精度可控:可通过GeoHash精度控制

缺点

精度有限:存在边缘效应问题

无法处理复杂多边形

距离计算为直线距离(不考虑地形)

删除操作需要特殊处理

应用场景

附近的人/商家

共享单车/车辆定位

配送范围计算

地理位置围栏

疫情风险区域标注

bash 复制代码
# 添加地理位置
GEOADD restaurants 116.397128 39.916527 "全聚德" 116.407526 39.904030 "海底捞"

# 查找附近5km的餐厅
GEORADIUS restaurants 116.400000 39.900000 5 km WITHDIST WITHCOORD COUNT 10

# 计算两个地点距离
GEODIST restaurants "全聚德" "海底捞" km

# 获取所有餐厅的位置
GEOPOS restaurants "全聚德" "海底捞"

小结

数据类型 内存占用 查询复杂度 精度 适用场景
HyperLogLog 固定12KB O(1) 99.19% 大数据去重统计
Bitmap 随偏移量变化 O(1) 100% 布尔标记、状态记录
Geospatial 与ZSet相同 O(logN+N) 地理位置服务

HyperLogLog

适用于海量数据去重统计

不关心具体元素内容时使用

注意误差范围,不适合精确计数场景

可以合并多个数据集的统计结果

Bitmap

适合用户ID连续或范围集中的场景

稀疏数据考虑使用压缩算法

定期清理过期数据

配合SETBIT和BITCOUNT实现复杂统计

Geospatial

合理选择GeoHash精度

考虑使用辅助索引提升查询性能

定期更新位置数据

结合其他数据类型(如Hash)存储额外信息

实际案例

案例1:网站UV统计系统

bash 复制代码
# 使用HyperLogLog统计每日UV
PFADD uv:20240215 user1 user2 user3
PFCOUNT uv:20240215

# 月度UV统计
PFMERGE uv:202402 uv:20240201 uv:20240202 ... uv:20240228

案例2:用户签到与活跃度分析

bash 复制代码
# 用户签到(Bitmap)
SETBIT sign:202402:1001 15 1

# 活跃用户统计(Bitmap位运算)
# 统计连续7天都签到的用户
BITOP AND active_users sign:day1 sign:day2 ... sign:day7
BITCOUNT active_users

案例3:附近商家推荐系统

bash 复制代码
# 商家地理位置存储
GEOADD merchants 116.397128 39.916527 "商家A"

# 查找用户附近3km的商家
GEORADIUS merchants 116.400000 39.900000 3 km WITHDIST WITHCOORD COUNT 20
相关推荐
二宝哥11 小时前
CentOS 7.9 系统下 Redis 5.0.4 单机安装与源码编译安装指南
redis·centos
ShiXZ21317 小时前
Redis docker-compose 部署指南
redis·docker·eureka
青山木20 小时前
Redis 缓存一致性全解:从 Cache Aside 到延迟双删、Canal+MQ 的进阶之路
java·数据库·redis·后端·缓存
YDS82921 小时前
大营销平台 —— 活动SKU库存扣减业务及其一致性处理
java·spring boot·redis·rabbitmq·ddd
爬也要爬着前进1 天前
redis主从搭建
数据库·redis·缓存
用户095367515832 天前
在系统的学习 redis 前的疑惑
redis·go
吴声子夜歌2 天前
Redis 3.x——集群故障转移
java·数据库·redis·集群
明豆2 天前
Redis 分布式缓存生产实战
redis·分布式·缓存
海兰2 天前
【高速缓存】RedisVL 指南:从向量搜索到 AI 应用落地
人工智能·redis
weixin_727535622 天前
双Token认证体系深度拆解:Spring Security + JWT + Redis
redis·spring·wpf