资讯详情

深入Redis:五大数据类型底层原理与生产实战全解析

📅 2026/9/13 16:25:50 | 华诺云谱 👁 阅读
深入Redis:五大数据类型底层原理与生产实战全解析
做Redis开发这么久我最深的感受是网上聊Redis的文章一大半都在讲API怎么调但实际线上出问题的时候根本不是API记没记住的问题而是你根本不知道这个命令背后是什么数据结构、这条数据到底该用哪种类型存、这个场景用Redis到底合不合适。这篇文章我不打算给你念文档。我按自己这些年用Redis的实际经验把命令、数据类型、应用场景这三件事串起来讲一遍。里面会涉及底层编码、内存视角、以及一些平时文档里不会写的坑。不管你是刚接触Redis的初学者还是已经用过一段时间但总觉得理解不够系统的开发者这篇文章应该都能帮你把脑子里的Redis知识重新捋一遍。1. 先把Redis装好、连上再聊别的1.1 安装别看教程乱抄版本选型有讲究Redis的安装其实很简单但很多新手容易在第一步就被各种教程带偏装了个莫名其妙的版本。官方推荐的方式是直接去官网下载稳定版源码编译安装。Linux环境下操作就这几条命令wget https://download.redis.io/releases/redis-7.2.4.tar.gz tar xzf redis-7.2.4.tar.gz cd redis-7.2.4 make make install编译完redis-server和redis-cli默认会装到/usr/local/bin下直接就能用。Windows环境的同学注意了Redis官方其实不提供Windows版本。你在网上看到的所谓Windows版Redis大多是微软或者第三方开发者维护的移植版更新节奏通常比官方慢而且性能和稳定性都要打点折扣。如果你只是在本地开发环境用装个移植版问题不大真要上生产老老实实搞一台Linux机器别在Windows上折腾Redis这是过来人的忠告。版本选型方面当前建议直接上7.x版本别去碰5.x、6.x的旧版了。7.x在内存效率上有明显提升比如listpack替代ziplist、底层编码优化等同样一批数据在7.x下能省不少内存。更关键的是7.x的很多新特性在老版本上根本体验不到你如果还在用6.x甚至更老的版本赶紧规划升级。1.2 连接工具别用错命令行和GUI各有各的好装好Redis之后最快验证能不能用的方式就是命令行redis-cli -h 127.0.0.1 -p 6379进去之后敲一个PING返回PONG就说明通了。但cli这工具说实话日常开发用起来效率偏低尤其看一个复杂hash里面所有字段的值时那个输出格式能把你眼睛看花。这时候就需要一个GUI客户端。市面上的Redis桌面管理工具我基本都试过早期流行的是Redis Desktop Manager现在很多人转向Another Redis Desktop Manager因为它免费开源、跨平台颜值和功能也在线。我个人现在主力用的就是它支持连接管理、键值浏览、命令行终端还能直接看内存分析日常排查问题足够用了。提示生产环境的Redis千万别图省事把端口直接暴露到公网。Redis默认没有密码认证暴露出去分分钟被挖矿脚本扫到。要么用防火墙限定来源IP要么配置requirepass设置强密码要么走内网通过跳板机连接这三个至少占一个。2. 五大基本数据类型底层实现才是重点Redis面试题里必考的五大类型——String、Hash、List、Set、ZSet——大多数人只知道个表面API能说出底层结构的人就少了一半能说清楚每种类型在什么场景下选型的人更少。这节我把每个类型掰开揉碎讲清楚。2.1 String最简单的类型最容易被用错String是Redis最基础的类型value最大能存512MB。它的底层实现在早期是SDSSimple Dynamic String简单说就是一个带长度字段的动态字符串结构相比C语言的char*它在获取长度、追加操作、二进制安全等方面都有明显优势。Redis 7.x里演进出了更紧凑的SDS结构内存效率更高。String的常用命令大家都熟SET、GET、MSET、MGET、INCR、DECR、APPEND、STRLEN。但真正值得关注的是带扩展参数的命令比如# 设置键值同时指定过期时间 SET user:10086:token abc123 EX 3600 # 如果键不存在才设置用于分布式锁 SET lock:order:10001 worker-1 NX EX 30 # 获取旧值并写入新值用于计数器清零 GETSET counter 0这两个扩展参数EX和NX配合使用是后面讲分布式锁的核心基础现在先记住。String最容易犯的错是用它存复杂结构化数据。比如一个用户对象有name、age、email三个字段有人图省事直接SET user:1 zhangsan|18|zsqq.com。这种存法每次改其中一个字段都要把整个字符串读出来解析一遍再写回去一旦字段多、并发高性能直接崩。每个字段开一个key的方案也不好key数量爆炸内存浪费严重。正确方案是下面要讲的Hash。另外一点INCR这一族命令是原子操作这一点在并发计数场景价值极大。很多人用Redis做计数器时自己先GET再SET中间被其他请求插一脚数据就错了这属于典型的没搞懂原子性的用法。2.2 Hash存对象就应该是它Hash类型就是一个field-value映射表底层实现有两种编码当数据量小且每个field的value较短时用listpack早期版本是ziplist当数据量大时自动转换为hashtable。Redis会根据数据规模自动在两种编码间切换这个细节透露出一个重要信息小数据量场景Redis在极力节省内存所以你对小对象的存储不要太奢侈。Hash的常用命令HSET user:1 name zhangsan age 18 email zsqq.com HGET user:1 name HGETALL user:1 HINCRBY user:1 age 1 HLEN user:1 HDEL user:1 email刚才那个用户信息存String的问题用Hash就完美解决对象整体是一个key内部字段对应field修改单个字段不需要读写整个对象HINCRBY还能对数值字段做原子自增。Hash在命令设计上的最佳实践是如果业务对象字段固定比如订单、用户、商品这种结构化数据无脑选Hash。有些场景传感器采集的时序数据用Hash存某设备某时间段内的指标数据字段是时间戳、值是指标值这种用法也可以用就是要注意设置过期时间防止数据无限堆积。Hash一个常见的坑是HGETALL在大hash上非常昂贵。一个hash里存了几万个field一次HGETALL把所有数据全部取回来网络传输就可能卡几十毫秒这在热路径上是大忌。只看部分字段就尽量用HMGET按需取别动不动把整个hash倒出来。2.3 List不是只能做消息队列List的底层是quicklist结构本质是一个由多个ziplist新版本为listpack组成的双向链表。这个设计很有意思既保留了双向链表两端插入删除快的特性又通过每个节点内部压缩的方式降低了内存消耗。常用命令# 右侧推入左侧弹出典型队列 RPUSH task:queue task-1 LPOP task:queue # 左侧推入左侧弹出典型栈 LPUSH history:user:1 page-1 LPOP history:user:1 # 阻塞弹出用于可靠队列 BRPOP task:queue 0这里想说一个点List在很多人眼里就是“消息队列”但实际上它作为消息队列有个天然问题——消息弹出后队列里就没了消费者处理失败消息就丢了。如果你只是做异步解耦、允许少量消息丢失的场景ListBRPOP没问题如果每条消息都不能丢得上Redis Stream或者干脆用专业消息队列Kafka、RabbitMQ别硬用List扛。还有一个List用得很多的场景是时间线/最新动态。比如发布一条内容LPUSH user:timeline content-id然后LRANGE user:timeline 0 9就能拿到最新的10条。它的优势在于操作都是O(1)或O(N)N是取出的条数比关系型数据库的ORDER BY id DESC LIMIT 10在一些场景下快得多。2.4 Set去重和聚合运算的神器Set是String类型的无序集合底层实现有两种存储元素全是整数且数量不大时用intset否则用hashtablevalue指向null。Set的最大卖点是支持集合运算交集、并集、差集。常用命令SADD tag:java article-1001 article-1002 SMEMBERS tag:java SISMEMBER tag:java article-1001 SCARD tag:java # 交集、并集、差集 SINTER tag:java tag:redis SUNION tag:java tag:redis SDIFF tag:java tag:redisSet的经典应用场景第一个是用户标签一个文章有哪些标签、一个用户关注了哪些博主这些都是Set存。第二个是做共同好友/共同关注一句SINTER user:1:following user:2:following就能算出来。第三个是抽奖去重SADD lottery:pool userId重复添加自动忽略。这里有一个Set场景下容易忽略的问题用SMEMBERS取全部成员时如果集合很大同样会造成阻塞和网络阻塞。要遍历大集合时优先用SSCAN做增量迭代这一点和后面讲KEYS命令时的建议是一致的。2.5 ZSet排序需求的首选方案ZSet可太重要了它相当于在Set的基础上给每个元素加了一个score字段集合按score从小到大自动排序。底层实现是跳表skiplist加哈希表的组合跳表负责按score排序和范围查询哈希表负责O(1)定位元素。常用命令ZADD leaderboard 100 user-1 90 user-2 ZINCRBY leaderboard 5 user-1 ZRANGE leaderboard 0 -1 # 从小到大的排名 ZREVRANGE leaderboard 0 9 # 从大到小取前10名 ZSCORE leaderboard user-1 ZRANK leaderboard user-1 # 从小到大排名位置 ZRANGEBYSCORE leaderboard 80 100 # 按分数范围取ZSet最典型的场景是排行榜没有之一。直播打赏榜、积分榜、热销商品榜全部可以用ZSet一个key搞定。ZINCRBY原子地给某个用户加分ZREVRANGE取前N名天然就是排行榜接口的完整实现。延迟队列是ZSet一个含金量很高的应用场景。ZSet的score存任务执行时间戳消费者用ZRANGEBYSCORE task:delay 0 now LIMIT 0 10取到期的任务处理完ZREM删除。这个方案的妙处在于同一个ZSet里你可以塞未来不同时间点要执行的任务每次只取已经到期的那一批天然胜过一个List队列的做法。ZSet还有一个容易被忽略的用法是做限流。score用时间戳每来一个请求ZADD rate:user:10086 now now然后统计窗口内有多少个成员ZCOUNT rate:user:10086 windowStart now超过阈值就拒绝窗口过期成员用ZREMRANGEBYSCORE清掉。滑动窗口限流用ZSet实现非常优雅代码量不大但效果稳。3. 命令别只会SET和GET这些实操逻辑你需要搞懂3.1 Key通用命令管理Redis的日常操作除了类型特有命令有一批通用命令几乎每天都要用到但很多人并没有系统地理解它们的使用场景和注意事项。EXPIRE key seconds设置过期时间。注意这里有个细节EXPIRE是以秒为单位的如果你要毫秒级过期要用PEXPIRE。TTL key查看键剩余存活时间-1表示没有设置过期时间永久有效-2表示键不存在。DEL key [key...]删除一个或多个键。UNLINK key异步删除键。这个命令很关键直接删一个大key时会阻塞Redis进程UNLINK是先在字典中把键摘除再在后台线程释放内存不影响主线程响应删除bigkey时请务必习惯用UNLINK。TYPE key查看键的数据类型。OBJECT ENCODING key查看键的底层编码排查内存问题时很有用。EXISTS key判断键是否存在。这里重点说EXPIRE和TTL配合实际业务的用法。比如做接口限流我可以用INCR加EXPIRE两步来做第一次访问时INCR如果返回值是1就顺便EXPIRE key 60后面60秒内都能拿到计数这种方法在计数器场景下很简单实用。注意这里要保证两步的原子性可以用Lua脚本把INCR和EXPIRE串起来。3.2 慎用KEYS、慎用SMEMBERS请用SCAN这是新手和老手之间非常明显的一个分水岭。KEYS user:*这条命令看起来很好用但生产环境如果key数量很大几十万甚至上百万个KEYS命令会阻塞Redis主线程好几秒甚至更久。期间所有客户端请求全部排队等待表现就是服务突然卡顿甚至超时。这是生产环境的事故级隐患。正确的做法是使用SCAN命令。它的工作方式是基于游标的增量迭代每次只返回一小部分key虽然不能保证一次拿全但可以分多次完成遍历每次命令的执行时间都很短不会阻塞主线程。SCAN 0 MATCH user:* COUNT 1000第一次调用传入游标0返回结果包含游标位置和一批key下次把返回的游标传进去继续遍历直到游标回到0表示遍历完成。对于Set类型的大集合对应的是SSCANHash类型大集合对应的是HSCANZSet对应的是ZSCAN。养成用增量遍历的习惯是Redis从业者从入门到进阶的关键一步。3.3 Pipeline和事务减少RTT和保证原子性Redis的命令执行非常快但如果你的代码里一条一条发送命令每条都要走一次网络往返RTT在大批量操作时总耗时会非常难看。Pipeline流水线就是解决这个问题的手段之一客户端把多条命令一次性批量发给Redis服务端服务端依次执行后把结果一次性返回给客户端。网络往返从N次降到1次性能提升非常可观。Python客户端的Pipeline用法大概是这样的import redis r redis.Redis(host127.0.0.1, port6379) pipe r.pipeline() for i in range(10000): pipe.set(fk:{i}, i) pipe.execute()注意Pipeline的作用是批量传输它并不保证原子性。Pipeline里的命令是依次执行的中途如果某条命令出错后面的命令不会回滚这和事务是两码事。要保证原子性需要用MULTI、EXEC、DISCARD和WATCH。MULTI开启事务把多条命令放进队列EXEC一次性执行。这里有个重要区别Redis事务在EXEC之前如果某条命令的语法有错误整个事务会被拒绝如果在执行过程中某条命令失败比如对String类型执行LPUSH前面的命令已经执行了Redis不会做回滚。事务里还有个WATCH命令能做乐观锁读一个key之后WATCH它事务执行前如果那个key被其他客户端改了事务会被拒绝。不过实际业务中Lua脚本往往是更好的选择因为Lua脚本既保证原子性又减少网络往返还能用编程逻辑表达比较复杂的操作后面分布式锁的释放动作就是用Lua脚本实现的。3.4 慢查询日志和MONITOR排查性能问题的手段Redis的性能问题定位有两条路径非常实用慢查询日志和MONITOR命令。慢查询日志记录的是执行时间超过阈值的命令。它不像MySQL的慢查询那样直接写文件而是保存在内存里供查询。相关配置有两个# 慢查询阈值单位微秒默认10000 CONFIG SET slowlog-log-slower-than 5000 # 慢查询日志最多保存条数 CONFIG SET slowlog-max-len 128 # 查看慢查询日志 SLOWLOG GET 10 # 重置慢查询日志 SLOWLOG RESET当线上出现接口变慢第一件事就是看慢查询日志把超过阈值的命令捞出来看哪个key、哪条命令在慢。最常见的元凶就是大key上的KEYS、SMEMBERS、HGETALL这类全量操作也可能是某个大key的DEL操作造成阻塞。找到元凶之后要么改命令要么分拆大key问题基本就能解决。MONITOR命令可以实时打印Redis收到的所有命令用来排查线上数据被谁改的、什么命令在刷key非常直观。但这命令在生产环境开启代价很大在高并发场景下会拖垮性能一般也就排查问题的时候开个几秒钟抓完快照马上关闭。4. 应用场景实战从“会用”到“用得对”4.1 缓存三座大山穿透、击穿、雪崩的应对方式缓存是Redis最基础的应用场景。数据库前面挡一层Redis热点数据直接走内存后端数据库压力小一大截。但缓存用得不好会撞上穿透、击穿、雪崩这三个经典问题。缓存穿透是指查询一个根本不存在的数据缓存里没有数据库里也没有每次请求都穿过缓存打到数据库上数据库压力陡增甚至被打挂。解决方案有三个层次。第一个是缓存空值数据库中查不到也把这个key的value设置成空字符串或者特殊占位符并设置一个较短的过期时间比如30秒后续同样key的请求直接命中缓存不会打到底层数据库。第二个是布隆过滤器把所有可能存在的数据的哈希值放到布隆过滤器里请求来了先判断key是否存在不存在直接返回不用查缓存更不用查数据库。第三个是参数校验不合法的请求参数直接在入口层拦截。缓存击穿是指某个热点key在缓存过期的瞬间大批请求同时去数据库查询数据库瞬间被打爆。和穿透的区别在于击穿的对象是“存在但恰好过期”的热点数据。解决方案也有几个。第一种是互斥锁当缓存中没有数据时让一个线程去数据库加载数据并重建缓存其他线程等待一段时间后重试第二种是逻辑过期缓存数据不设置物理过期时间而是在value里存一个逻辑过期时间后台异步刷新极端情况下允许短暂的数据不一致第三种是对热点数据预热在缓存即将过期前用定时任务主动刷新。缓存雪崩是指大量key在同一段时间内集体过期请求全部落到数据库上和击穿的区别是“多对多”而非“一对一”。应对方案首先是过期时间加随机值比如原来是3600秒现在设置成3600加0到300秒的随机数避免key在同一时刻集体过期其次是多级缓存本地缓存比如Caffeine加Redis再加数据库即使Redis层面大面积失效本地缓存还能扛一部分再次是熔断降级在数据库压力过大时直接返回默认值或提示信息宁可降级也不让数据库挂掉。4.2 分布式锁SET NX EX的细节和Redisson的坑Redis实现分布式锁是面试常考题也是很多系统实际在用的方案。它的核心命令是SET lock:key requestId NX EX timeout其中NX确保只有key不存在时才能设置成功EX确保锁会自动过期避免持有锁的进程宕掉导致死锁。这里有几个关键的细节必须注意。第一value要设置成唯一标识比如UUID。为什么因为释放锁时要检验value是否是自己设置的防止误删别人的锁。假设不校验进程A的锁过期了进程B获取到锁此时A处理完业务去删除锁直接把B的锁删了B的临界区就失去了保护。这个错误后果很严重但网上很多教程都没讲清楚。第二释放锁要用Lua脚本保证“校验删除”的原子性。比如if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end这个脚本的执行过程是原子的不存在“刚校验完value还没来得及删除锁就过期了”的竞态窗口。第三锁的过期时间不能拍脑袋设置。设置太短业务还没执行完锁就自动释放并发进来就有问题了设置太长万一持有锁的进程挂掉别的进程要等很久。通用的做法是启动一个看门狗线程定期给锁续期Redisson框架内置了这个机制默认看门狗每10秒检查一次锁是否存在存在就把过期时间重置为30秒业务进程不退出就不会丢锁。但Redisson也有坑。前几天我在排查一个线上问题服务A持有Redis锁执行一个耗时很长的批处理任务处理到一半GC停顿了十几秒Redisson看门狗线程也一起STWStop The World了锁过期失效服务B趁机获取到同一个锁两个服务同时执行同一段业务逻辑出现了重复下单。解决方法是除了Redisson的看门狗还要在业务侧做幂等控制分布式锁不是银弹它保证的是“在正常情况下只有一个执行者”但GC停顿这种极端情况光靠锁是挡不住的。4.3 计数器与排行榜原子操作加ZSet的黄金组合Redis做计数器的核心优势是INCR、DECR、HINCRBY这些命令的原子性。在秒杀场景下要控制商品库存不能超卖可以先用一个String类型的计数器保存库存每次下单前做一次DECR如果返回负数说明库存不足直接拒绝。这个操作不需要加锁不需要事务单条命令原子完成性能极高。更近一步的用法是结合Hash做多维度的计数。比如统计一个视频的播放量、点赞数、评论数可以用一个Hash来存HINCRBY video:1001 plays 1、HINCRBY video:1001 likes 1。一次操作一个字段互不影响。排行榜就是一个“ZSet的showcase”。拿直播打赏榜举例# 用户给主播打赏10元 ZINCRBY leaderboard:live:room_1001 10 user:888 # 获取当前房间打赏榜前10 ZREVRANGE leaderboard:live:room_1001 0 9 WITHSCORES你不需要自己维护排序逻辑Redis替你排好了。这里有个小技巧如果一张排行榜的数据量很大可以在ZSet里只保留Top N的数据后台定期把末尾的成员裁掉控制key的体积提升后续操作速度。4.4 异步队列和延迟队列从List到Stream的演进用List做异步队列是很多轻量级系统的选择。LPUSH生产消息BRPOP消费消息阻塞等待参数设置为0就是一直等实现一个基本的异步消费链路非常容易。在消息量不大、业务逻辑简单、允许少量失败重试的场景下这个方案完全够用没有引入额外中间件的成本。但前面提到过List方案的痛点是消息消费后即消失一旦消费者处理失败消息就恢复不回来了。所以在Redis 5.0之后官方推出了Stream类型它像一个具备持久化能力的消费队列支持消费者组、消息ACK确认、消息ID自动生成、历史消息回溯等特性。如果你在选型时就是要把队列放在Redis里优先考虑Stream而不是List尤其当消息重要程度较高时。延迟队列用ZSet实现具体做法前面提过score存期望执行时间戳取消息时用ZRANGEBYSCORE取所有小于等于当前时间戳的任务处理完毕ZREM删除。我这里给一个具体的伪代码逻辑生产者 ZADD delay:queue 当前时间戳延迟秒数 消息内容 消费者轮询 now 当前时间戳 tasks ZRANGEBYSCORE delay:queue 0 now LIMIT 0 10 for task in tasks: 处理任务 ZREM delay:queue task这个方案有个需要注意的问题如果同一个score对应多条消息ZSet本身不保证它们的消费顺序。要让一定时间段内的消息按插入顺序执行可以把score设计成“时间戳自增序号”的组合比如Redis的TIME命令取到微秒级时间拼上一个自增序列保证score单调递增。不过实际项目中大多数人不会把这么复杂的逻辑塞进Redis直接用现成的延迟队列中间件更省心。5. 常见问题与排查技巧实录5.1 maxmemory与内存淘汰策略防止Redis被写爆Redis是内存数据库但如果使用者没有规划好内存边界Redis会在不知不觉中被数据撑爆然后操作系统开始回收内存甚至触发OOM把进程杀掉。在配置文件或者运行中用CONFIG SET maxmemory设置一个上限比如CONFIG SET maxmemory 4gb设置了maxmemory之后还要指定内存淘汰策略maxmemory-policy决定内存满了之后怎么办。Redis提供了几种策略noeviction不淘汰任何数据新写入直接报错。allkeys-lru从所有key里按LRU最近最少使用淘汰。volatile-lru从设置了过期时间的key里按LRU淘汰。allkeys-lfu从所有key里按LFU访问频率最低淘汰。volatile-lfu从设置了过期时间的key里按LFU淘汰。allkeys-random随机淘汰任意key。volatile-ttl从设置了过期时间的key里淘汰剩余存活时间最短的。我的经验是缓存场景优先用allkeys-lru热点数据会被留下冷门缓存自动被清理如果业务数据都在Redis里有明确的过期策略可以用volatile-lru保证持久化的key不被意外清掉如果对数据完整性有要求直接上noeviction宁可写入报错也不能偷偷清数据。做Redis内存规划的时候不要懒配置一下maxmemory和maxmemory-policy这个习惯能让你省去很多半夜被叫醒的麻烦。5.2 持久化选型RDB还是AOF这是个问题Redis默认开启了RDB快照持久化快照文件是二进制的dump.rdb。它的特点是恢复快适合做冷备和数据恢复缺点是可能丢失上一次快照之后写入的数据。默认配置下如果Redis进程突然宕机你可能丢几秒甚至几分钟的数据。AOFAppend Only File则把每次写操作都追加到日志文件里数据安全性更高。可以通过appendfsync参数控制同步策略always每条命令都刷盘最安全但性能最低everysec每秒刷盘一次性能和安全性平衡最多丢1秒的数据no交给操作系统决定安全性最差。生产环境我的推荐做法是RDB和AOF同时开启RDB负责快速恢复基线数据AOF负责补足RDB快照之后的数据变更。Redis 7.x里AOF文件可以智能化地重写压缩不会无限膨胀下去。很多云厂商的Redis托管服务也默认同时开启两个持久化这说明双开是一个被广泛认可的方案。持久化还有一个容易被忽视的坑大key对持久化的影响。在进行RDB快照时Redis会fork一个子进程来生成快照文件父进程继续处理命令。如果某个key特别大fork的子进程在写快照的瞬间主进程会尝试复制这个大key对应的内存页导致短暂的阻塞。所以从持久化的角度考虑也不建议在Redis里放超过几十MB的大key。5.3 连接数打满、bigkey、hotkey的排查处理线上Redis出故障最常见的是这几类问题。连接数打满客户端连接数超过maxclients限制后新连接直接拒绝。最常见的原因是应用层连接池配置不合理比如连接池最大数量设得太高或者连接空闲后没有及时释放。排查时可以先看INFO clients里当前连接数和连接来源然后重点检查应用代码里的连接池参数给连接池设置一个合理的maxTotal和maxIdle不要无上限地创建连接。bigkeyvalue长度很大比如一个String有几MB或者一个Hash有几十万field。bigkey的危害有三点删除时阻塞Redis网络传输时占用大量带宽拖慢其他请求持久化时对内存和CPU都有压力。排查方式用Redis内置的--bigkeys参数redis-cli --bigkeys这个命令会遍历整个实例统计出每种类型里最大的key。定位到bigkey之后处理方法通常是拆分把一个大hash拆成多个小hash或者把一个大list拆成多个小list用hash tag等方式保证相关key落在同一分片。hotkey某个key的访问量特别高导致单个Redis分片CPU飙升。排查可以用redis-cli --hotkeys需开启LFU策略或者用MONITOR抓一小段分析。热点key的解决思路有三个加一层本地缓存兜住热点流量把同一个key复制出多个副本key分散读流量比如key后缀加随机数热点数据拆分成多个分片后汇总。5.4 几个容易忽略的Redis配置项最后分享几个经常被忽略但实际价值很大的配置项。rename-command可以禁用危险命令。生产环境建议把KEYS、FLUSHALL、FLUSHDB、CONFIG这几个命令rename成别人猜不到的字符串或者在客户端层面就禁止调用。这样就算应用被脱库攻击者也做不了破坏性操作。notify-keyspace-events可以开启键空间通知。当某个key过期、被修改、被删除时Redis会向订阅的客户端推送事件。这个能力在做缓存失效后的业务补偿、定时任务触发、数据同步等场景很有用但注意开启后会有额外的消息分发开销按需配置。lazyfree-lazy-expire和lazyfree-lazy-eviction这些参数在Redis 4.0以后推出把原本可能在主线程阻塞的删除、过期任务挪到后台线程异步执行。在内存淘汰和过期key清理比较频繁的实例上开启lazyfree能明显降低延迟毛刺。io-threads可以在多核机器上开启多线程IO把网络读写分散到多个IO线程上Redis 6.0之后支持。高并发的读多写少场景收益明显建议在有条件的情况下开启。说说我在实际使用中的几点体会写了这么多再说点个人的经验。Redis这个东西文档和API只能让你“会调用”真正让你“会用”的是两件事第一是清楚每种数据类型的底层结构知道它擅长什么、不擅长什么这样在数据建模时才能准确把握第二是理解生产环境里Redis的边界它不是万能的有些场景用Redis只是“能用”但未必是“最优解”。比如真正严格的事务需求、复杂的关系型查询、超大规模的消息堆积这些场景还是要选择更专业的组件。还有一点很多初学者会忽略就是Redis不只是要“学命令”更要“学运维思维”。同一个Redis实例在不同人的手里能跑出完全不同的效果。你在写代码的时候就得想这个key会不会无限增长这个bigkey会不会阻塞实例这个热点key会不会打爆某个分片把这些视角养成习惯Redis出问题的概率会大幅降低。如果这篇文章能帮你把Redis的知识体系从“背命令”往上拔一层那我就没白写。动手装一个Redis拿自己的业务数据练一练比看十篇文章都有用。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。