Redis作为高性能的键值存储系统,提供了丰富的数据类型来满足不同场景的需求。下面我将详细介绍Redis的9种核心数据类型及其典型应用场景。
一、基础数据类型
1. String(字符串)
特点:
- 二进制安全,可存储文本、序列化数据或二进制
- 最大512MB
- 支持原子增减操作
常用命令:
SET key value [EX seconds] # 设置值,可带过期时间
GET key # 获取值
INCR key # 原子+1
DECRBY key decrement # 原子减指定值
APPEND key value # 追加内容
GETRANGE key start end # 获取子串
应用场景:
- 缓存HTML片段
- 计数器(文章阅读量)
- 分布式锁
- 存储序列化对象
示例:
> SET article:1001:views 0
> INCR article:1001:views # 阅读量+1
> GET article:1001:views
"1"
2. Hash(哈希表)
特点:
- 字段值对集合
- 适合存储对象
- 每个hash可存储2³²-1个字段
常用命令:
HSET key field value # 设置字段值
HGET key field # 获取字段值
HGETALL key # 获取所有字段值
HINCRBY key field increment # 字段原子增加
HDEL key field # 删除字段
应用场景:
- 用户属性存储
- 商品信息缓存
- 聚合统计数据
示例:
> HSET user:1001 name "张三" age 28
> HGET user:1001 name
"张三"
> HINCRBY user:1001 age 1 # 年龄+1
3. List(列表)
特点:
- 双向链表结构
- 元素可重复
- 最大长度2³²-1
常用命令:
LPUSH key value # 左侧插入
RPUSH key value # 右侧插入
LPOP key # 左侧弹出
RPOP key # 右侧弹出
LRANGE key start stop # 获取范围元素
BLPOP key timeout # 阻塞式左侧弹出
应用场景:
- 消息队列
- 最新消息排行
- 记录用户操作历史
示例:
> LPUSH news:latest "新闻A"
> LPUSH news:latest "新闻B"
> LRANGE news:latest 0 1
1) "新闻B"
2) "新闻A"
4. Set(集合)
特点:
- 无序唯一元素集合
- 支持交并差运算
- 最大成员数2³²-1
常用命令:
SADD key member # 添加成员
SMEMBERS key # 获取所有成员
SISMEMBER key member # 判断成员是否存在
SINTER key1 key2 # 集合交集
SUNION key1 key2 # 集合并集
应用场景:
- 标签系统
- 共同好友
- 唯一IP统计
示例:
> SADD article:1001:tags "科技" "数据库"
> SADD article:1002:tags "科技" "AI"
> SINTER article:1001:tags article:1002:tags
1) "科技"
5. Sorted Set(有序集合)
特点:
- 唯一成员关联分数(score)
- 按分数自动排序
- 相同分数按字典序
常用命令:
ZADD key score member # 添加成员
ZRANGE key start stop # 按索引范围获取
ZREVRANGE key start stop # 逆序获取
ZRANK key member # 获取成员排名
ZSCORE key member # 获取成员分数
应用场景:
- 排行榜
- 带权重的消息队列
- 范围查询
示例:
> ZADD leaderboard 100 "玩家A" 200 "玩家B"
> ZREVRANGE leaderboard 0 1 WITHSCORES
1) "玩家B"
2) "200"
3) "玩家A"
4) "100"
二、高级数据类型
6. Bitmaps(位图)
本质:String类型的位操作扩展
常用命令:
SETBIT key offset value # 设置位
GETBIT key offset # 获取位
BITCOUNT key [start end] # 统计置1位数
BITOP operation destkey key [key...] # 位运算
应用场景:
- 用户签到记录
- 活跃用户统计
- 布隆过滤器
示例:
> SETBIT user:sign:1001 0 1 # 第1天签到
> SETBIT user:sign:1001 1 1 # 第2天签到
> BITCOUNT user:sign:1001
(integer) 2
7. HyperLogLog(基数统计)
特点:
- 用于估计集合基数
- 误差率约0.81%
- 固定使用12KB内存
常用命令:
PFADD key element [element...] # 添加元素
PFCOUNT key [key...] # 估算基数
PFMERGE destkey sourcekey [sourcekey...] # 合并多个HLL
应用场景:
- 网站UV统计
- 大规模去重计数
示例:
> PFADD visitors 192.168.1.1 192.168.1.2
> PFCOUNT visitors
(integer) 2
8. Geospatial(地理空间)
本质:基于Sorted Set实现
常用命令:
GEOADD key longitude latitude member # 添加位置
GEODIST key member1 member2 [unit] # 计算距离
GEORADIUS key longitude latitude radius unit # 查找范围内成员
应用场景:
- 附近的人
- 地理位置搜索
- 配送范围计算
示例:
> GEOADD cities 116.40 39.90 "北京" 121.47 31.23 "上海"
> GEODIST cities 北京 上海 km
"1067.9888"
9. Stream(流)
特点:
- Redis 5.0引入
- 消息队列增强版
- 支持消费者组
常用命令:
XADD key ID field value [field value...] # 添加消息
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key...] ID [ID...] # 读取消息
XGROUP CREATE key groupname ID # 创建消费者组
应用场景:
- 消息队列
- 事件溯源
- 日志收集
示例:
> XADD mystream * sensor-id 1234 temperature 19.8
"1609459200000-0"
> XREAD COUNT 2 STREAMS mystream 0
1) 1) "mystream"
2) 1) 1) "1609459200000-0"
2) 1) "sensor-id"
2) "1234"
3) "temperature"
4) "19.8"
三、数据类型选择指南
数据类型 | 适用场景 | 不适用场景 |
---|---|---|
String | 简单键值、计数器 | 需要存储结构化数据 |
Hash | 对象属性、频繁部分更新 | 需要单独过期字段 |
List | 队列、最新列表 | 随机访问中间元素 |
Set | 无序唯一集合、关系运算 | 需要排序的场景 |
Sorted Set | 排行榜、范围查询 | 内存敏感场景 |
Bitmap | 布尔统计、位操作 | 非二进制标记场景 |
HyperLogLog | 大数据去重统计 | 需要精确结果的场景 |
Geo | 地理位置应用 | 非地理数据 |
Stream | 消息队列、事件流 | 简单队列需求 |
四、内存优化技巧
-
小Hash优化:
- 当field数量较少时,Redis使用ziplist编码
- 配置阈值:
hash-max-ziplist-entries 512
-
Int编码优化:
SET counter 100 # 使用int编码 OBJECT ENCODING counter "int"
-
共享对象:
- Redis会共享0-9999的整数对象
- 配置:
redis.hz 10
(影响回收频率)
-
内存碎片整理:
CONFIG SET activedefrag yes
五、数据类型底层实现
5.1 编码方式
数据类型 | 可能编码 | 条件 |
---|---|---|
String | int/embstr/raw | 值长度≤39字节用embstr |
Hash | ziplist/hashtable | field数≤512且值≤64字节用ziplist |
List | ziplist/linkedlist/quicklist | Redis 3.2后默认quicklist |
Set | intset/hashtable | 元素全为整数且数量≤512用intset |
Sorted Set | ziplist/skiplist | 元素数≤128且值≤64字节用ziplist |
5.2 内存占用对比
存储1百万个简单键值(测试结果):
类型 | 内存占用 | 特点 |
---|---|---|
String | ~85MB | 每个键独立存储 |
Hash | ~50MB | 字段紧凑存储 |
Set | ~65MB | 哈希表开销 |
ZSet | ~90MB | 跳表+哈希表 |
六、实战应用案例
6.1 电商系统设计
# 商品缓存
HMSET product:1001 id 1001 name "iPhone13" price 6999 stock 100
# 购物车
HSET cart:user1001 product:1001 2 # 商品ID:数量
# 秒杀库存
SET inventory:seckill:1001 100
DECR inventory:seckill:1001
# 用户标签
SADD user:tags:1001 "高消费" "数码爱好者"
6.2 社交网络系统
# 关注关系
SADD following:1001 1002 1003 # 用户1001的关注
SADD followers:1002 1001 # 用户1002的粉丝
# 共同关注
SINTER following:1001 following:1002
# 时间线
LPUSH timeline:1001 "post:923"
LPUSH timeline:1001 "post:924"
6.3 实时监控系统
# 设备状态
HSET device:temp:sensor01 last_value 23.5 update_time 1630000000
# 报警阈值
ZADD device:alerts 50 "sensor01" 60 "sensor02"
# 地理位置
GEOADD devices:geo 116.404 39.915 "sensor01"
GEORADIUS devices:geo 116.404 39.915 10 km
七、常见问题解答
Q1:Redis的List和Java的LinkedList有什么区别?
A:
- Redis List是双向链表+ziplist的混合实现
- 当元素少时使用ziplist压缩存储
- 支持阻塞操作(BLPOP)
- 可持久化到磁盘
Q2:Sorted Set如何实现范围查询?
A:
- 内部使用跳表(skiplist)和哈希表组合实现
- 跳表支持O(logN)复杂度的范围查询
- 哈希表保证O(1)的单元素访问
Q3:为什么Redis不提供树结构?
A:
- 跳表已能满足排序需求且实现更简单
- 树结构的平衡操作影响性能
- 可通过Sorted Set实现大多数树结构功能
Redis丰富的数据类型使其能够灵活应对各种场景,从简单的缓存到复杂的实时系统。理解每种类型的特点和底层实现,可以帮助开发者做出最优的技术选型和设计决策。