news 2026/1/11 22:40:45

【数据库】【Redis】数据结构全景图:命令、场景与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【数据库】【Redis】数据结构全景图:命令、场景与避坑指南

Redis 数据结构全景表格

数据结构核心命令使用场景
String
(字符串)
SET,GET,INCR,DECR,APPEND,STRLEN,MSET,MGET缓存对象、计数器、分布式锁、Session存储、限速器
Hash
(哈希表)
HSET,HGET,HGETALL,HDEL,HINCRBY,HLEN,HMSET,HMGET存储对象属性(如用户信息)、购物车、配置项、结构化数据存储
List
(列表)
LPUSH,RPUSH,LPOP,RPOP,LRANGE,LLEN,BLPOP,BRPOP消息队列、最新消息排行、任务队列、关注列表、时间线
Set
(集合)
SADD,SREM,SMEMBERS,SISMEMBER,SCARD,SINTER,SUNION,SDIFF标签系统、好友关系、抽奖活动、共同关注、去重集合
Sorted Set
(有序集合)
ZADD,ZREM,ZRANGE,ZREVRANGE,ZRANK,ZSCORE,ZINCRBY,ZCARD排行榜、延时队列、范围查询、带权重的任务调度、热门内容
Bitmap
(位图)
SETBIT,GETBIT,BITCOUNT,BITOP,BITPOS用户签到、活跃用户统计、布隆过滤器、特征标记、日活统计
HyperLogLogPFADD,PFCOUNT,PFMERGE海量数据去重计数(UV统计)、基数估算、大数据量唯一值统计
Geospatial
(地理空间)
GEOADD,GEODIST,GEOPOS,GEORADIUS,GEORADIUSBYMEMBER附近的人/店、地理位置存储、距离计算、基于位置的服务(LBS)
Stream
(流)
XADD,XREAD,XRANGE,XLEN,XGROUP,XREADGROUP,ACK事件溯源、消息队列(支持消费者组)、日志收集、实时数据处理

一、String(字符串)

1.核心命令

# 基础操作SET key value[EX seconds][NX|XX]# NX=不存在才设置(分布式锁),XX=存在才更新GET key DEL key# 数值操作(原子性)INCR key# 原子自增(计数器)INCRBY key100# 增加指定值DECR key SETNX key value# 等价于 SET key value NX# 批量操作MSET key1 val1 key2 val2# 批量设置(原子性)MGET key1 key2# 批量获取# 高级SETEX key60value# 设置并带过期(原子)GETSET key newvalue# 设置新值并返回旧值STRRLEN key# 获取字符串长度

2.使用场景

  • 缓存热点数据:SET user:1001 “{name:‘tom’,age:25}” EX 3600
  • 分布式锁:SET lock:order:1001 “uuid” NX EX 30
  • 计数器:INCR page:view:home(原子,无竞态)
  • 全局唯一 ID:INCR global:order:id

3.注意点

  • 值最大 512MB,避免存储大对象(BigKey 问题)
  • 数值范围:有符号 64 位整数(-2^63 ~ 2^63-1)
  • SETNX 非原子:SETNX + EXPIRE 分两步,可能死锁,用 SET key value NX EX 30 替代
  • **避免 KEYS **:生产环境禁用,用 SCAN 0 MATCH user:COUNT 100 替代

二、List(列表)

1.核心命令

# 左右插入(栈/队列)LPUSH list:a value1 value2# 从左侧插入(栈:后进先出)RPUSH list:a value3# 从右侧插入(队列:先进先出)# 弹出LPOP list:a# 左侧弹出RPOP list:a# 右侧弹出BLPOP list:a10# 阻塞弹出(10秒超时),用于消息队列# 范围查询LRANGE list:a0-1# 获取全部元素LRANGE list:a09# 获取前10个(注意:O(n),大数据量慎用)# 长度LLEN list:a# 指定位置操作LINDEX list:a0# 获取索引0元素LSET list:a0newval# 设置索引0值LINSERT list:a BEFORE value2 value1.5# 插入LREM list:a2value# 删除前2个value

2.使用场景

  • 消息队列:LPUSH + RPOP(生产者左插,消费者右弹)
  • 最新列表:LPUSH news:latest “news1” + LTRIM news:latest 0 99(保留最新100条)
  • 任务队列:BRPOP 阻塞消费
  • 消息广播:多个消费者订阅同一 List(无 ACK 机制,消息可能被重复消费)

3.注意点

  • LRANGE 0 -1 慎用:大 List 会阻塞 Redis(时间复杂度 O(n))
  • BigKey 问题:List 元素超过 5000 个视为 BigKey,需拆分
  • 内存消耗:每个元素独立分配内存,小元素用 Hash 替代更省
  • 无 ACK:消息弹出即删除,消费者崩溃会丢消息(Stream 可解决)

三、Set(集合)

1.核心命令

# 增删SADD set:a member1 member2 SREM set:a member1# 查询SMEMBERS set:a# 获取所有成员(O(n),大 Set 慎用)SCARD set:a# 获取成员数量SISMEMBER set:a member1# 判断成员是否存在(O(1))# 随机操作SRANDMEMBER set:a3# 随机获取3个成员(不删除)SPOP set:a# 随机弹出1个成员(删除)# 集合运算SINTER set:a set:b# 交集(共同好友)SUNION set:a set:b# 并集SDIFF set:a set:b# 差集(a 有但 b 没有)

2.使用场景

  • 标签系统:SADD user:1001:tags “vip” “active” “buyer”
  • 共同好友:SINTER user:1001:friends user:1002:friends
  • 抽奖/随机:SRANDMEMBER 随机选取
  • 去重:SADD 自动去重

3.注意点

  • SMEMBERS 大 Set 危险:10 万成员的 Set 执行 SMEMBERS 会阻塞 1 秒以上
  • 用 SSCAN 替代:SSCAN set:a 0 MATCH * COUNT 100
  • 内存优化:整数集合(intset)编码可节省内存(所有成员为整数时自动启用)
  • 交并集复杂度:SINTER/SUNION 时间复杂度 O(N*M),大集合运算在客户端做

四、Sorted Set(ZSet,有序集合)

1.核心命令

# 添加(score 为 double 类型)ZADD zset:a100"player1"200"player2"150"player3"# 范围查询(按 score 排序)ZRANGE zset:a0-1 WITHSCORES# 升序获取全部ZREVRANGE zset:a09WITHSCORES# 降序获取 Top10# 按 score 范围查询ZRANGEBYSCORE zset:a100200WITHSCORES LIMIT010# 成员操作ZSCORE zset:a"player1"# 获取成员 scoreZINCRBY zset:a50"player1"# score 增加 50ZCARD zset:a# 成员数量# 排名ZRANK zset:a"player1"# 升序排名(从0开始)ZREVRANK zset:a"player1"# 降序排名(Top1 返回 0)# 删除ZREM zset:a"player1"ZREMRANGEBYRANK zset:a02# 删除排名 0-2 的成员ZREMRANGEBYSCORE zset:a0100# 删除 score 0-100 的成员

2.使用场景

  • 排行榜:游戏积分、热销榜
  • 延迟队列:ZADD delay_queue “task1” + 定时扫描
  • 范围查询:成绩区间、价格区间
  • 带权重的队列:优先级任务
// 延迟队列实现// 生产者ZADD delay_queue1704067200"task:email:1001"// 2024-01-01 执行// 消费者(定时任务)while(true){Set<String>tasks=ZRANGEBYSCORE delay_queue0NOW()LIMIT010;for(Stringtask:tasks){// 处理任务processTask(task);ZREM delay_queue task;// 移除}Thread.sleep(100);}

3.注意点

  • 跳表实现:ZSet 底层是跳表 + Hash,内存占用较高(每个元素约 100 字节)
  • BigKey 阈值:成员数 > 5000 为 BigKey
  • ZREVRANGE 0 -1 慎用:O(n),可能阻塞
  • Score 精度:double 类型,大范围整数可能丢失精度
  • 相同 Score:按字典序排序,不保证插入顺序

五、Hash(哈希表)

1.核心命令

# 单字段操作HSET user:1001 name"tom"age25HGET user:1001 name HDEL user:1001 age# 批量操作HMSET user:1001 name"tom"age25email"tom@example.com"HMGET user:1001 name age# 查询HGETALL user:1001# 获取所有字段(O(n))HKEYS user:1001# 获取所有 keyHVALS user:1001# 获取所有 valueHLEN user:1001# 字段数量# 存在判断HEXISTS user:1001 name# 数值操作HINCRBY user:1001 age1# age 原子加 1

2.使用场景

  • 对象存储:HSET user:1001 field value(比 String 序列化更灵活)
  • 配置项:HSET config:app max_connections 100
  • 购物车:HSET cart:1001 product:123 2 product:456 1(商品ID:数量)

3.注意点

  • 小对象优化:Hash 的 ziplist 编码(字段 < 512 且每个值 < 64 字节)可节省内存
  • HGETALL 大 Hash:可能导致 Redis 阻塞(O(n)),用 HSCAN 替代
  • 内存占用:字段名重复存储(每个 Hash 独立),小对象适合,大对象用 String 压缩更优
  • 不支持二级结构:Hash 的值只能是 String,不能嵌套 List/Set

六、Bitmap(位图)

1.核心命令

# 设置位SETBIT active:2024010110011# 用户1001 在 2024-01-01 活跃# 获取位GETBIT active:202401011001# 返回 0/1# 统计位数BITCOUNT active:20240101# 统计 1 的个数(日活)# 位运算BITOP AND result:3days active:20210101 active:20210102 active:20210103# 3日留存用户BITOP OR result:active active:20210101 active:20210102# 两日总活跃

2.使用场景

  • 日活统计:SETBIT + BITCOUNT,内存占用极低(1 亿用户 = 12.5MB)
  • 用户签到:SETBIT sign:1001:202401 0 1(0 表示第 1 天)
  • 权限系统:SETBIT permissions:admin 1001 1(用户1001有管理员权限)
  • 去重:SETBIT seen:page 1001 1(标记用户1001访问过页面)

3.注意点

  • 位数限制:2^32 位(约 42.9 亿),足够大多数场景
  • SETBIT 返回旧值:注意返回值是之前的位值(0 或 1)
  • BITCOUNT 范围:可指定字节范围 BITBIT key start end
  • 线程安全:位操作是原子的,无需担心并发
  • **内存:Bitmap 是 String 实现:最大 512MB,存储约 40 亿位

七、HyperLogLog(基数统计)

1.核心命令

# 添加元素PFADD page:uv:20240101"user1""user2""user3"# 统计基数(去重后数量)PFCOUNT page:uv:20240101# 返回估算值# 合并多个 HLLPFMERGE result:uv page:uv:20240101 page:uv:20240102# 合并两日 UV

2.使用场景

  • UV 统计:页面独立访客(误差率 0.81%)
  • 去重计数:统计搜索关键词去重数
  • 日活/月活:快速估算 DAU/MAU

3.注意点

  • 误差率:标准误差 0.81%,不适合需要精确计数的场景(如金融)
  • 固定内存:每个 HLL 占用 固定 12KB,与元素数量无关
  • 无法获取原始数据:只能统计数量,无法返回具体元素
  • PFADD 可重复:重复添加同一元素不影响计数

八、Geospatial(地理位置)

1.核心命令

# 添加坐标GEOADD cities116.4039.90"Beijing"121.4731.23"Shanghai"# 查询坐标GEOPOS cities"Beijing"# 计算距离(默认米)GEODIST cities"Beijing""Shanghai"km# 返回公里数# 查询半径内成员GEORADIUS cities116.4039.90100km# 北京100km内的城市GEORADIUSBYMEMBER cities"Beijing"100km# 以成员为中心查询# 获取哈希GEOHASH cities"Beijing"# 返回52位geohash字符串

2.使用场景

  • 附近的人:查询 5km 内的用户
  • 门店搜索:查找附近的餐馆、加油站
  • 轨迹记录:存储用户移动轨迹

3.注意点

  • 距离单位:默认为米,支持 km/m/mi/ft
  • 坐标精度:有效经度 -180~180,纬度 -85~85
  • 数据类型:底层是 ZSet,可用 ZSet 命令操作(但会破坏地理结构)
  • 误差:地球建模为球体,有 0.5% 误差
  • 性能:GEORADIUS 复杂度 O(N+log(m)),N 是半径内数量,m 是总成员数

九、Stream(消息流,Redis 5.0+)

1.核心命令

# 添加消息XADD stream:orders * user_id1001amount99.00# * 表示自动生成 ID(时间戳-序列号)# 读取消息XRANGE stream:orders - +# 读取所有消息XRANGE stream:orders1704067200000-01704153600000-0# 按时间范围# 阻塞读取(消费者)XREAD COUNT10BLOCK1000STREAMS stream:orders $# $ 表示只读取新消息,阻塞1000ms# 消费者组XGROUP CREATE stream:orders order_group $ MKSTREAM# 创建消费者组# 消费者读取(ACK 机制)XREADGROUP GROUP order_group consumer1 COUNT1BLOCK1000STREAMS stream:orders># 确认消息(ACK)XACK stream:orders order_group1704067200000-0# 未 ACK 消息查询XPENDING stream:orders order_group# 消息删除XDEL stream:orders1704067200000-0# 标记删除(非真正删除)

2.使用场景

  • 可靠消息队列:替代 List,支持 ACK 和重试
  • 事件溯源:存储用户行为事件流
  • 流计算:实时统计订单金额

3.注意点

  • ID 格式:毫秒时间戳-序列号(如 1704067200000-0),保证递增
  • 消费者组:多个消费者共享消息(负载均衡),需 ACK 确认
  • 消息积压:未 ACK 的消息会保留,可能导致内存膨胀
  • 不支持删除中间消息:只能删除两端(XRANGE 后惰性删除)
  • Stream 不是 Kafka:不支持分区副本,单机容量有限

十、通用注意事项(所有数据结构)

1. BigKey 问题

# 识别 BigKeyredis-cli --bigkeys# 阈值String: value>10KB List/Set/ZSet/Hash: 元素数>5000
  • 危害:删除、查询操作会阻塞 Redis(时间复杂度 O(n))
  • 解决:拆分、压缩、异步删除(UNLINK 命令)

2. 慢查询

CONFIG SET slowlog-log-slower-than10000# 超过 10ms 记录SLOWLOG GET10# 查看慢查询
  • 常见慢操作:KEYS *、FLUSHALL、LRANGE 0 -1、HGETALL 大 Hash

3. 过期策略

# 设置过期EXPIRE key60# 60秒后过期SETEX key60value# 原子设置+过期PERSIST key# 移除过期# 淘汰策略(maxmemory-policy)noeviction# 不淘汰,返回错误(默认)allkeys-lru# 所有 key LRU 淘汰volatile-lru# 仅过期 key LRU 淘汰allkeys-random# 随机淘汰volatile-ttl# 淘汰 TTL 短的

4. 事务

# MULTI/EXEC 打包命令(非原子性,仅排队)MULTI SET a1SET b2EXEC# 一起执行,但中间可能被其他命令插入# WATCH 实现乐观锁WATCH balance MULTI DECRBY balance100INCRBY points10EXEC# 如果 balance 被修改,EXEC 返回 nil(回滚)

注意:Redis 事务不支持回滚,命令语法错误会全部失败,运行时错误会继续执行后续命令

5. Lua 脚本(原子操作)

-- 原子执行,保证多个命令不被打断 EVAL"if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"1lock:order:1001"uuid"

6. 数据一致性

  • 缓存与 DB 不一致:更新 DB 后删除缓存(非更新缓存),或 Canal 监听 Binlog
  • 主从延迟:从库读取可能读到旧数据,重要读走主库

7. 客户端

  • 连接池:Jedis 需配置连接池,Lettuce 基于 Netty 自动管理
  • 管道(Pipeline):批量操作减少 RTT
Pipeline pipeline=jedis.pipelined();for(int i=0;i<1000;i++){pipeline.set("key"+ i,"value"+ i);}pipeline.sync();// 一次性发送

十一、数据结构选型决策树

存储单个值? ├── 是 → String(缓存、计数器) │ 存储对象(多个字段)? ├── 是 → Hash(用户资料、配置) │ 需要队列/栈? ├── 是 → List(消息队列、最新列表) │ 需要去重集合? ├── 是 → Set(标签、抽奖) │ 需要排序/排行榜? ├── 是 → ZSet(积分排行) │ 需要判断存在性且数据量大? ├── 是 → Bitmap(日活、签到) │ 需要估算去重数? ├── 是 → HyperLogLog(UV) │ 需要地理位置? ├── 是 → Geospatial(附近的人) │ 需要可靠消息队列? └── 是 → Stream(带 ACK 的 MQ)

十二、总结

Redis 的数据结构是"为场景而生":String 负责缓存计数,Hash 存储对象,List 实现队列,Set 去重,ZSet 排序,Bitmap 极致空间,HyperLogLog 估算去重,Geospatial 处理位置,Stream 支撑可靠消息。核心注意点是:避免 BigKey、警惕慢查询、理解内存编码、合理设置过期

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/12 2:37:38

30、编程与脚本编写指南

编程与脚本编写指南 1. 程序编译与安装 在大型项目中,程序编译非常重要。例如,Linux内核(一个不断修改和改进的程序)包含数百万行代码。 对于封装良好的源代码,通常会有一个名为 install 的特殊 make 目标。这个目标会将最终产品安装到系统目录中以供使用,通常这个…

作者头像 李华
网站建设 2025/12/31 15:56:10

33、Shell脚本中的控制操作符与交互式输入技巧

Shell脚本中的控制操作符与交互式输入技巧 1. 控制操作符:另一种分支方式 在Shell脚本编程里,控制操作符 && 和 || 为我们提供了一种独特的分支处理方式。理解它们的行为至关重要,下面是它们各自的工作原理: - command1 && command2 :先执行 co…

作者头像 李华
网站建设 2026/1/10 3:27:36

vue和springboot框架开发的协同过滤算法的电影推荐系统 电影评价管理系统_ 影评解说系统z9p6gctw

文章目录具体实现截图主要技术与实现手段关于我本系统开发思路java类核心代码部分展示结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;具体实现截图 同行可拿货,招校园代理 vueSpringboot电影评价管理系统_ 影评解说系统 框架开…

作者头像 李华
网站建设 2026/1/1 9:23:22

vscode 连接失败

客户端改ip了&#xff0c;vscode连接不是了&#xff0c;报错信息&#xff1a;Add correct host key in C:\\Users\\Administrator/.ssh/known_hosts to get rid of this message. > Offending ED25519 key in C:\\Users\\Administrator/.ssh/known_hosts:34 > Host key f…

作者头像 李华
网站建设 2026/1/6 11:35:12

【Linux系统】初探虚拟地址空间

一、内存空间布局很久之前&#xff0c;我们浅浅谈过内存的空间布局&#xff1a;在这里插入图片描述其中&#xff0c;初始化数据和未初始化数据指的是全局或静态变量。程序的局部变量开辟在栈区&#xff0c;malloc/new等申请的空间是在堆区。堆区和栈区&#xff0c;是相对而生长…

作者头像 李华