Skip to content

Redis 数据结构详解

字符串(String)

字符串是Redis最基本的数据结构,也是其他数据结构的基础。

常用命令

txt
# 设置键值对
SET key value

# 获取值
GET key

# 设置键值对,带过期时间(秒)
SETEX key seconds value

# 设置键值对,仅当键不存在时
SETNX key value

# 递增整数
INCR key

# 递增指定步长
INCRBY key increment

# 递减整数
DECR key

# 递减指定步长
DECRBY key decrement

# 追加字符串
APPEND key value

# 获取字符串长度
STRLEN key

# 获取子字符串
GETRANGE key start end

# 设置子字符串
SETRANGE key offset value

内部实现

  • raw:适用于长字符串(大于39字节),使用简单动态字符串(SDS)实现
  • embstr:适用于短字符串(小于等于39字节),连续内存分配,减少内存碎片
  • int:适用于整数值,直接存储为long类型

使用场景

  • 缓存简单键值对
  • 计数器(如页面访问量、点赞数)
  • 分布式锁
  • 存储用户会话信息
  • 验证码存储

列表(List)

列表是有序的字符串集合,支持在两端进行插入和删除操作。

常用命令

txt
# 从左侧插入元素
LPUSH key value [value ...]

# 从右侧插入元素
RPUSH key value [value ...]

# 从左侧弹出元素
LPOP key

# 从右侧弹出元素
RPOP key

# 获取列表长度
LLEN key

# 获取指定范围的元素
LRANGE key start stop

# 获取指定索引的元素
LINDEX key index

# 设置指定索引的元素
LSET key index value

# 移除指定值的元素
LREM key count value

# 修剪列表,只保留指定范围的元素
LTRIM key start stop

# 阻塞式从左侧弹出元素
BLPOP key [key ...] timeout

# 阻塞式从右侧弹出元素
BRPOP key [key ...] timeout

内部实现

  • quicklist(Redis 3.2+):结合ziplist和linkedlist的优点,是列表的默认实现
    • 当列表元素较少且较小时,使用ziplist编码
    • 当列表元素较多或较大时,自动转换为quicklist

使用场景

  • 消息队列
  • 最新消息展示
  • 任务队列
  • 栈和队列实现
  • 用户活动记录

哈希(Hash)

哈希是键值对的集合,适合存储对象数据。

常用命令

txt
# 设置哈希字段值
HSET key field value

# 获取哈希字段值
HGET key field

# 设置多个哈希字段值
HMSET key field1 value1 field2 value2 ...

# 获取多个哈希字段值
HMGET key field1 field2 ...

# 获取所有哈希字段和值
HGETALL key

# 获取所有哈希字段
HKEYS key

# 获取所有哈希值
HVALS key

# 获取哈希字段数量
HLEN key

# 检查哈希字段是否存在
HEXISTS key field

# 删除哈希字段
HDEL key field [field ...]

# 哈希字段值递增
HINCRBY key field increment

# 哈希字段值递增浮点数
HINCRBYFLOAT key field increment

内部实现

  • ziplist:适用于字段数量少且值小的哈希
  • hashtable:适用于字段数量多或值大的哈希

使用场景

  • 存储用户信息
  • 存储商品信息
  • 存储配置信息
  • 统计数据聚合

集合(Set)

集合是无序的字符串集合,不允许重复元素。

常用命令

txt
# 添加元素到集合
SADD key member [member ...]

# 从集合中移除元素
SREM key member [member ...]

# 获取集合中的所有元素
SMEMBERS key

# 检查元素是否在集合中
SISMEMBER key member

# 获取集合元素数量
SCARD key

# 随机获取集合中的一个元素
SRANDMEMBER key [count]

# 随机移除并返回集合中的一个元素
SPOP key [count]

# 集合差集(A - B)
SDIFF key [key ...]

# 集合交集(A ∩ B)
SINTER key [key ...]

# 集合并集(A ∪ B)
SUNION key [key ...]

# 集合差集存储到新集合
SDIFFSTORE destination key [key ...]

# 集合交集存储到新集合
SINTERSTORE destination key [key ...]

# 集合并集存储到新集合
SUNIONSTORE destination key [key ...]

内部实现

  • intset:适用于元素都是整数且数量少的集合
  • hashtable:适用于元素包含字符串或数量多的集合

使用场景

  • 用户标签
  • 好友关系
  • 兴趣爱好
  • 数据去重
  • 抽奖活动

有序集合(Sorted Set)

有序集合是有序的字符串集合,每个元素关联一个分数,用于排序。

常用命令

txt
# 添加元素到有序集合
ZADD key score member [score member ...]

# 从有序集合中移除元素
ZREM key member [member ...]

# 获取有序集合中的元素数量
ZCARD key

# 获取指定分数范围内的元素数量
ZCOUNT key min max

# 有序集合元素分数递增
ZINCRBY key increment member

# 获取元素在有序集合中的排名(从小到大)
ZRANK key member

# 获取元素在有序集合中的排名(从大到小)
ZREVRANK key member

# 获取指定排名范围内的元素(从小到大)
ZRANGE key start stop [WITHSCORES]

# 获取指定排名范围内的元素(从大到小)
ZREVRANGE key start stop [WITHSCORES]

# 获取指定分数范围内的元素(从小到大)
ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

# 获取指定分数范围内的元素(从大到小)
ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

# 移除指定排名范围内的元素
ZREMRANGEBYRANK key start stop

# 移除指定分数范围内的元素
ZREMRANGEBYSCORE key min max

# 有序集合交集
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

# 有序集合并集
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

内部实现

  • ziplist:适用于元素数量少且值小的有序集合
  • skiplist + hashtable:适用于元素数量多或值大的有序集合
    • skiplist:用于快速范围查询和排序
    • hashtable:用于快速查找元素分数

使用场景

  • 排行榜
  • 优先级队列
  • 时间线
  • 范围查询
  • 延迟队列

流(Stream)

流是Redis 5.0引入的数据结构,用于存储和处理流式数据。

常用命令

txt
# 添加消息到流
XADD key [NOMKSTREAM] [MAXLEN|MINID [=|~] threshold [LIMIT count]] *|id field value [field value ...]

# 读取流中的消息
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]

# 读取流中的消息(消费者组)
XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] id [id ...]

# 创建消费者组
XGROUP CREATE key groupname id [MKSTREAM]

# 删除消费者组
XGROUP DESTROY key groupname

# 确认消息处理完成
XACK key groupname id [id ...]

# 获取流信息
XINFO STREAM key

# 获取消费者组信息
XINFO GROUPS key

# 获取消费者信息
XINFO CONSUMERS key groupname

# 修剪流
XTRIM key MAXLEN|MINID [=|~] threshold [LIMIT count]

# 删除消息
XDEL key id [id ...]

内部实现

  • 基于基数树(Radix Tree)和链表实现
  • 支持持久化存储
  • 支持消息ID的自动生成

使用场景

  • 实时消息队列
  • 事件流处理
  • 日志存储
  • 时间序列数据
  • 物联网数据采集

位图(Bitmap)

位图是字符串的扩展,用于位级操作。

常用命令

txt
# 设置位值
SETBIT key offset value

# 获取位值
GETBIT key offset

# 统计值为1的位数量
BITCOUNT key [start end]

# 位与操作
BITOP AND destkey key [key ...]

# 位或操作
BITOP OR destkey key [key ...]

# 位异或操作
BITOP XOR destkey key [key ...]

# 位非操作
BITOP NOT destkey key

# 查找第一个值为0或1的位
BITPOS key bit [start] [end]

内部实现

  • 基于字符串实现,每个字节存储8个位
  • 支持最大512MB的位图(2^32个位)

使用场景

  • 用户签到
  • 在线状态
  • 权限管理
  • 布隆过滤器
  • 统计活跃用户

地理空间索引(Geospatial)

地理空间索引用于存储和查询地理位置数据。

常用命令

txt
# 添加地理位置
GEOADD key longitude latitude member [longitude latitude member ...]

# 获取地理位置
GEOPOS key member [member ...]

# 计算两个地理位置之间的距离
GEODIST key member1 member2 [m|km|mi|ft]

# 获取指定范围内的地理位置
GEORADIUS key longitude latitude radius m|km|mi|ft [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

# 获取指定成员范围内的地理位置
GEORADIUSBYMEMBER key member radius m|km|mi|ft [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]

# 获取地理位置的哈希值
GEOHASH key member [member ...]

# 删除地理位置
ZREM key member [member ...]

内部实现

  • 基于有序集合实现,使用Geohash算法将经纬度编码为64位整数
  • 支持经纬度范围查询和距离计算

使用场景

  • 附近的人
  • 附近的商家
  • 地理位置推荐
  • 物流跟踪
  • 地理围栏

基数统计(HyperLogLog)

基数统计用于估算集合中的不同元素数量,占用内存固定且很小。

常用命令

txt
# 添加元素到HyperLogLog
PFADD key element [element ...]

# 获取HyperLogLog的基数估算值
PFCOUNT key [key ...]

# 合并多个HyperLogLog到新的HyperLogLog
PFMERGE destkey sourcekey [sourcekey ...]

内部实现

  • 基于HyperLogLog算法实现
  • 占用内存固定,约12KB
  • 误差率约为0.81%

使用场景

  • 独立访客统计(UV)
  • 独立IP统计
  • 搜索词统计
  • 页面浏览来源统计

数据结构最佳实践

1. 选择合适的数据结构

  • 根据业务需求选择最合适的数据结构
  • 考虑数据访问模式和性能要求
  • 考虑内存使用效率

2. 避免大键

  • 将大集合拆分为多个小集合
  • 对大哈希进行分片存储
  • 定期清理过期数据

3. 使用合适的编码方式

  • 了解各种数据结构的内部编码
  • 优化数据结构参数,如hash-max-ziplist-entries
  • 避免频繁的编码转换

4. 合理设置过期时间

  • 对临时数据设置过期时间
  • 使用EXPIRE或SETEX命令设置过期时间
  • 考虑使用TTL命令检查键的剩余存活时间

5. 批量操作优化

  • 使用MSET、MGET等批量命令减少网络开销
  • 考虑使用管道(Pipeline)提高吞吐量
  • 避免在管道中执行过多命令

6. 利用Redis特性

  • 使用Redis的原子操作,避免竞态条件
  • 利用Redis的发布订阅功能
  • 考虑使用Redis模块扩展功能

常见问题(FAQ)

Q1: Redis支持哪些数据结构?

A1: Redis支持以下数据结构:

  • 字符串(String)
  • 列表(List)
  • 哈希(Hash)
  • 集合(Set)
  • 有序集合(Sorted Set)
  • 流(Stream)
  • 位图(Bitmap)
  • 地理空间索引(Geospatial)
  • 基数统计(HyperLogLog)

Q2: 如何选择合适的Redis数据结构?

A2: 选择Redis数据结构应考虑以下因素:

  • 数据类型:需要存储什么类型的数据?
  • 访问模式:如何访问和操作数据?
  • 性能要求:对读写性能有什么要求?
  • 内存使用:数据量大小如何?
  • 业务场景:用于什么业务场景?

Q3: Redis数据结构的内存使用情况如何?

A3: Redis数据结构的内存使用情况:

  • 字符串:取决于字符串长度
  • 列表:使用quicklist实现,内存使用较高效
  • 哈希:小哈希使用ziplist,大哈希使用hashtable
  • 集合:小集合使用intset,大集合使用hashtable
  • 有序集合:小有序集合使用ziplist,大有序集合使用skiplist+hashtable
  • :内存使用随消息数量增长
  • 位图:非常高效,每个字节存储8个位
  • 地理空间:基于有序集合实现
  • HyperLogLog:固定约12KB内存

Q4: 如何优化Redis数据结构的内存使用?

A4: 优化Redis数据结构内存使用的方法:

  • 选择合适的数据结构
  • 调整数据结构的编码参数
  • 避免大键,拆分大集合
  • 使用过期时间自动清理数据
  • 利用Redis的内存压缩功能(Redis 6.0+)
  • 定期检查和清理无效数据

Q5: Redis数据结构的性能如何?

A5: Redis数据结构的性能特点:

  • 所有数据结构的操作都是O(1)或O(log N)复杂度
  • 内存存储,访问速度极快
  • 单线程命令执行,避免并发开销
  • 支持批量操作和管道,提高吞吐量
  • 适合高并发场景

Q6: 如何处理Redis数据结构的并发问题?

A6: 处理Redis数据结构并发问题的方法:

  • 利用Redis的原子操作
  • 使用分布式锁
  • 利用Redis的事务功能
  • 考虑使用Lua脚本
  • 设计幂等操作

Q7: Redis数据结构支持持久化吗?

A7: 是的,Redis数据结构支持持久化:

  • 支持RDB持久化,将数据快照保存到磁盘
  • 支持AOF持久化,记录所有写命令
  • 支持混合持久化,结合RDB和AOF的优点
  • 不同数据结构的持久化方式相同

Q8: 如何监控Redis数据结构的使用情况?

A8: 监控Redis数据结构使用情况的方法:

  • 使用INFO命令查看Redis统计信息
  • 使用MEMORY USAGE命令查看单个键的内存使用
  • 使用MEMORY STATS命令查看详细内存统计
  • 使用Redis监控工具,如RedisInsight、Prometheus+Grafana
  • 定期分析Redis的内存使用趋势