外观
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的内存使用趋势
