☰
从数据结构到高可用:Redis核心知识点全解析
2026/10/10 4:08:04 网站建设 项目流程

在日常后端开发和系统架构里,Redis 几乎是绕不开的一块。无论是做缓存、分布式锁、排行榜还是消息队列,它都有一席之地。面试时它是高频考察点,实战中它也是排查性能问题最先怀疑的对象之一。这篇文章就把 Redis 的核心知识点重新梳理一遍,从数据结构到底层编码、从持久化到集群高可用,再到实际使用中经常踩的坑和排查思路,尽量把每个关键点背后的逻辑讲透,顺便带上一些我实际用下来的体会和教训。内容既适合准备面试的人从头系统过一遍,也适合有经验的开发者查漏补缺,看看某些细节是不是自己当初理解得不够到位。

1. 先搞清楚 Redis 到底解决了什么问题

1.1 为什么我们需要一个内存数据库

传统的关系型数据库把数据放在磁盘上,每次读写都涉及磁盘 IO,虽然有了索引和各种缓存机制,但面对高并发、低延迟的场景,磁盘的速度仍然是一个瓶颈。Redis 的核心定位就是把它变成内存操作,数据直接放在内存里,读写速度快好几个数量级。很多业务场景,比如热点数据缓存、秒杀计数、在线状态维护,根本不需要每次都去查数据库,先打到 Redis 上,能扛住绝大部分请求,数据库的压力自然就小多了。

我见过不少项目把 MySQL 当作万能存储,热门商品详情、用户会话、验证码,全往表里塞。等到流量一大,慢查询一堆,DBA 天天抓狂。后来引入 Redis 做缓存层,同样的服务,数据库负载降了百分之七八十。这就是 Redis 最朴素的价值——把频繁读的数据放到更快的地方。

但要注意一点,Redis 不是用来取代数据库的。它更像是一个加速层或者辅助层。数据最终的一致性、持久化保证,仍然需要依靠传统数据库。把 Redis 当主存储使用的项目,在宕机、数据丢失时往往要吃大亏。

1.2 Redis 的数据模型与通用命令

Redis 的数据模型和其他数据库差异很大。它本质上是一个键值对存储,键都是字符串,值有五种基本类型,再加上后来的四种扩展类型。操作的时候也是通过一套简洁的命令来完成,不需要写 SQL。

常用的通用命令并不多,却都很实用:

  • KEYS pattern:按模式匹配键名。这个命令生产环境要非常谨慎,因为它会阻塞 Redis 主线程,数据量大时极其危险。
  • EXPIRE key seconds:设置过期时间,是缓存策略的核心命令。
  • TTL key:查看剩余存活时间,-1 表示永不过期,-2 表示键不存在。
  • DEL key:删除键,可以一次删多个。
  • TYPE key:查看键的类型。
  • EXISTS key:判断键是否存在。

这些命令看起来简单,但组合起来就是业务里的各种玩法。给缓存设置合理的过期时间,能够防止缓存雪崩;在抢购场景用原子命令配合过期时间,能够防止超卖。理解每个命令背后的语义,比死记硬背命令列表更有用。

2. 五种基本数据结构的内部原理与实战场景

2.1 字符串:不只是简单的 key-value

字符串类型是 Redis 里最基础、最通用的数据类型。它既可以存字符串,也可以存数字,还能存二进制数据,比如图片内容或者序列化后的对象。底层实现不是 C 语言原生的char*,而是一种叫做 SDS(Simple Dynamic String,简单动态字符串)的结构。

SDS 的优势在于三点。第一,它记录了字符串的长度,获取长度的时间复杂度是 O(1),不用像 C 字符串那样遍历计数。第二,它在修改字符串时会自动检查空间并扩容,不会出现缓冲区溢出。第三,它在内存中存储了长度和空闲空间,二进制安全,可以存储包含\0字符的数据。

实际使用字符串的时候,有几个命令特别值得注意:

  • SET key value [EX seconds | PX milliseconds] [NX | XX]:可选的过期时间和条件设置。NX表示键不存在时才设置,这是实现分布式锁的基础。
  • INCR key/DECR key:原子自增自减。高并发场景下做计数器,比如文章浏览量、库存扣减,用它就不会有并发安全问题。
  • SETEX key seconds value:设置值的同时设置过期时间,适合验证码这类场景。
  • MSET/MGET:批量设置和获取,减少网络 RTT 开销。

我之前做过一个用户签到功能,用位图实现,本质上也是字符串类型。通过SETBIT和BITCOUNT来记录用户一年的签到情况,一个用户一年只需要 46 个字节,非常节省内存。

2.2 哈希:适合存储对象数据

哈希类型是一个string类型的 field 和 value 的映射表,特别适合存储对象。比如用户信息、商品信息,以用户 ID 作为 key,用户的各种属性作为 field。这样做的优势是,可以单独获取或修改某一个字段,而不需要像字符串序列化那样整存整取。

底层实现有两种编码方式。当 field 和 value 都比较短小,且数量不多时,使用 ziplist(压缩列表)编码,节省内存;当数据量变大后,会自动转换为 hashtable 编码,保证查找效率。这就是 Redis 的内存优化策略——在数据量小的时候用时间换空间,数据量大了再切换到高效的哈希表。

实战中常用的命令:

  • HSET key field value:设置单个字段。
  • HMSET key field1 value1 field2 value2:批量设置字段,新版 Redis 中HSET已支持多 field,可以替代。
  • HGETALL key:获取所有字段和值,需要注意当哈希较大时这个命令会阻塞线程,生产环境要慎用。
  • HINCRBY key field increment:对哈希中的某个字段做原子自增,比如记录用户积分、商品库存。

有一个容易犯的错误,就是把对象的每个属性拆成独立的字符串 key。比如一个用户有昵称、头像、积分,就建三个 key。这样既浪费内存(每个 key 都有额外的元数据开销),又难以管理。正确做法是用哈希一个 key 搞定:user:1001下面挂name、avatar、score三个 field。

2.3 列表:消息队列与时间线的好帮手

列表类型是一个双向链表,可以从左右两边插入和弹出元素。这个特性让它天然适合做简单的消息队列、最新消息列表、时间线等场景。

底层实现上有两种编码:ziplist 和 linkedlist。当列表元素较少且都是小整数或短字符串时,用 ziplist 节省内存;元素多了,或者单个元素很大,就切换为 linkedlist。新版本的 Redis 还引入了 quicklist 作为列表的底层实现,它结合了 ziplist 和双向链表的优点,把数据分成多个节点,每个节点又是一个压缩列表,既节省内存又支持高效的两端操作。

常用命令:

  • LPUSH/RPUSH:从左侧/右侧插入元素。
  • LPOP/RPOP:从左侧/右侧弹出元素。
  • LRANGE key start stop:获取指定范围的元素,做分页查询用。
  • LLEN key:获取列表长度。
  • BLPOP/BRPOP:阻塞式弹出,在列表没有元素时会一直等待,直到超时或有新元素进入。

用列表做消息队列有一个很经典的坑,就是LPOP加上轮询的方式非常浪费资源。正确的姿势是用BLPOP阻塞等待,这样消费者不会空转消耗 CPU。不过严格来说,Redis 的列表消息队列并不保证消息不丢失,如果一个消费者LPOP成功但还没来得及处理就宕机了,这个消息就丢了。这也是为什么很多团队后来转向了专业的消息队列中间件,或者用 Redis 的 Stream 类型来弥补。

2.4 集合:去重、标签与随机抽奖

集合类型是一个无序的字符串集合,它的最大特性是元素唯一。底层实现是哈希表或者整数集合,当元素都是整数且数量不多时用整数集合,非常节省内存。

典型应用场景:

  • 去重:比如一个用户点赞过的文章 ID,集合保证了不会重复点赞。
  • 标签系统:给一篇文章打上多个标签,或者给用户打上兴趣标签,用集合存储后可以做交集、并集、差集运算。
  • 随机抽奖:SRANDMEMBER或SPOP可以随机取出元素。

常用命令:

  • SADD key member [member ...]:添加元素。
  • SREM key member [member ...]:移除元素。
  • SISMEMBER key member:判断元素是否存在,时间复杂度 O(1)。
  • SINTER/SUNION/SDIFF:交集、并集、差集运算。
  • SCARD key:获取集合元素数量。

集合的差集、交集操作在社交场景里特别实用。比如推荐可能认识的人,算法就是:取你的好友集合和目标用户的好友集合的交集,交集越大,越可能认识。还有一个场景是权限控制,一个用户拥有哪些角色、哪些权限点,都可以用集合存,判断权限时用SISMEMBER非常快。

2.5 有序集合:排行榜的标配

有序集合和集合的区别在于,每个元素都关联了一个分数(score),并且按照分数从小到大排序。它底层使用跳表(skip list)加哈希表实现,既能高效地按分数排序,又能快速查找成员。

这个数据结构几乎就是为了排行榜场景设计的。比如游戏排行榜,成员是玩家 ID,分数是玩家得分。ZINCRBY可以实时加分,ZREVRANGE可以取出分数最高的前 N 名,ZRANK可以查询某个玩家的排名。

常用命令:

  • ZADD key score member:添加元素并设置分数。
  • ZINCRBY key increment member:给某个成员加分。
  • ZRANGE key start stop [WITHSCORES]:按分数从低到高获取指定范围元素。
  • ZREVRANGE key start stop [WITHSCORES]:按分数从高到低获取,适合排行榜。
  • ZSCORE key member:获取某个成员的分数。
  • ZRANK key member/ZREVRANK key member:获取成员的排名。
  • ZREM key member:移除元素。

我之前做过一个直播间在线榜单的需求,用户给主播刷礼物后,礼物值需要实时累计并展示榜单。直接用ZINCRBY,一行命令搞定。在千万级数据量下,跳表的插入和查询性能依然非常稳定。但注意,有序集合的ZRANGE如果 range 范围很大,比如几千上万,也会造成阻塞,分页查才是正确的用法。

3. 持久化机制:数据安全与性能的权衡

3.1 RDB 快照:性能优先,数据冷启动

RDB(Redis DataBase)持久化机制是生成某一时刻的内存快照,保存到磁盘的二进制文件中。它的触发方式分为手动触发和自动触发。

手动触发用SAVE或BGSAVE命令。SAVE是同步的,会阻塞 Redis 主进程,生产环境绝不可以使用;BGSAVE是异步的,会 fork 出一个子进程来生成快照,主进程继续对外服务。自动触发则是通过配置文件中的save指令,比如save 900 1表示 900 秒内至少有 1 个键发生变化就触发一次BGSAVE。

RDB 的优点很明显:文件紧凑,加载速度快,适合做灾备和数据冷启动。主从复制时,从节点首次同步也是通过 RDB 完成的。

但它的问题也很突出:如果 Redis 没有触发 RDB 就异常宕机了,从上一次快照到宕机时刻的数据全部丢失。假设配置save 900 1,最坏情况下会丢 15 分钟的数据。对于大部分业务来说,这个丢失窗口是不可接受的。

3.2 AOF 日志:更可靠,但有代价

AOF(Append Only File)机制是把每次写命令追加到日志文件中,记录的是命令本身,而不是数据结果。这样数据恢复的时候,重新执行一遍命令即可。

AOF 的可靠性取决于同步策略,配置项appendfsync有三个选项:

  • always:每次写入都同步到磁盘,最安全但性能最差。
  • everysec:每秒同步一次,性能与安全兼顾,最多丢一秒的数据。
  • no:由操作系统决定何时同步,性能最好但数据丢失风险最高。

生产环境一般选everysec。这个选项在性能上接近no,安全上只是最多丢一秒数据,是大多数业务的合理选择。

AOF 文件会随着写入不断膨胀,需要定期压缩。Redis 提供了BGREWRITEAOF命令,重写过程中会读取当前内存中的数据,生成最小的命令集合来恢复当前状态,然后替换旧文件。重写期间,新的写命令会同时缓存起来,重写完成后再追加到新文件中,保证数据不丢。

AOF 恢复数据时,是把文件里的命令一条条重新执行,所以启动会比 RDB 慢。文件越大,启动越慢。为了解决这个问题,Redis 4.0 以后引入了混合持久化机制。

3.3 混合持久化与恢复流程

混合持久化结合了 RDB 和 AOF 的优点。aof-use-rdb-preamble配置开启后,AOF 文件重写时,会先把当前内存数据以 RDB 格式写入文件开头,之后再把增量写命令以 AOF 格式追加在后面。这样恢复时,先快速加载 RDB 部分,再重放少量 AOF 增量命令,启动速度大幅提升。

恢复流程是这样的:Redis 启动时,会优先检查 AOF 文件是否存在。如果存在,就加载 AOF 文件;如果不存在,则加载 RDB 文件。如果 AOF 文件损坏,可以用redis-check-aof工具修复,但会丢失损坏位置之后的命令数据。

我自己的习惯是:如果业务对数据丢失容忍度很低(比如购物车、订单状态),必须开启 AOF 并配置everysec;如果只是纯缓存场景,数据丢了可以从数据库重建,那用 RDB 就够了。混合持久化是两全其美的方案,建议直接开启。

这里有个经验之谈:持久化配置要在数据量小的早期就规划好,不要等到线上数据几百 GB 了才想起开 AOF。大数据量下,AOF 文件的写入、重写、磁盘占用,都会变成棘手的问题,到时候再改配置,需要灰度、压测、预案,风险大得多。

4. 高可用架构:主从复制、哨兵与集群

4.1 主从复制的基本逻辑

单台 Redis 节点宕机,整个缓存层就瘫了。主从复制是构建高可用架构的第一步。在主从架构下,一个主节点(master)负责写,多个从节点(slave)负责读。写操作同步到从节点,读流量可以分摊到不同的从节点上,提升读吞吐量。

主从复制的流程:

  1. 从节点向主节点发送SYNC或PSYNC命令,请求同步。
  2. 主节点执行BGSAVE,生成 RDB 快照,同时缓存新写入的命令。
  3. 主节点把 RDB 文件发送给从节点,从节点加载 RDB 恢复数据。
  4. 主节点把缓存期间的写命令推送给从节点,从节点重放命令,最终达到数据一致。

这里有一个重要概念:复制偏移量。主节点每次发送 N 个字节给从节点,就会累加 N 个字节到自己的复制偏移量;从节点每接收 N 个字节,也会累加到自己的偏移量。通过对比偏移量,能判断主从数据是否一致。如果从节点掉线后重连,会用PSYNC命令带上自己的偏移量,主节点只推送断线期间的增量数据即可,不用全量同步。

实际部署时,我建议把所有从节点都设置为只读模式,防止业务误连从节点写入数据导致主从数据不一致。同时开启replica-read-only yes是默认行为,一般不用特别改,但要注意运维层不要把从节点暴露给写请求。

4.2 哨兵:自动故障转移

主从复制解决了数据冗余和读扩展,但主节点宕机后,需要人工切换,这个延迟业务上不能接受。哨兵(Sentinel)机制就是为了解决自动故障转移问题。

哨兵是一个独立的进程,它监控所有 Redis 节点。当主节点宕机时,哨兵集群会通过投票选举出一个新的主节点,并把其他从节点重新指向新的主节点,同时将旧主节点降级为从节点。整个过程对应用层透明,客户端通过哨兵获取当前主节点地址。

哨兵的客观下线机制需要理解一下:单个哨兵发现主节点心跳超时,会先标记为主观下线,然后通过SENTINEL is-master-down-by-addr命令向其他哨兵确认。当超过半数哨兵确认后,才会真正判定主节点客观下线,触发故障转移。所以哨兵至少需要三个节点,否则无法形成多数派决策。

我自己在部署哨兵时踩过一个坑:三个哨兵部署在同一台机器上,结果机器宕机,三个哨兵同时挂掉,故障转移完全无法进行。正确的做法是哨兵分散到不同的物理机或可用区,这样单点故障不会影响哨兵集群的判断。

4.3 Cluster 模式:数据分片与水平扩展

哨兵模式解决了高可用问题,但所有数据仍然存储在同一组主从节点上,内存容量有一个上限。当单节点内存不足以支撑业务时,需要横向扩展,这时候就要用 Cluster 集群模式。

Cluster 模式的核心思想是数据分片。整个键空间被分成 16384 个哈希槽(hash slot),数据通过CRC16(key) % 16384计算出属于哪个槽。每个节点负责一部分槽。比如三主三从的集群,三个主节点分别负责 5461、5462、5461 个槽。

客户端在访问某个 key 时,先计算它属于哪个槽,再找到负责该槽的节点。如果请求发到了错误的节点,集群会返回MOVED错误,并把正确的节点地址告诉客户端。这也是明明用代码连上了 Cluster,却报错MOVED的原因,实际上客户端 SDK 会自动处理重定向,但如果你用的是普通客户端而不是集群客户端,就会遇到这个问题。

Cluster 模式下,数据无法跨节点做事务和管道操作,因为MGET可能涉及多个 key 分布在不同节点上。Redis Cluster 的解决方案是 hash tag,把 key 写成{user1001}.name和{user1001}.age,利用花括号让这两个 key 被分配到同一个槽,从而支持同一事务操作。这是一个很实用的小技巧。

主从复制同样存在于 Cluster 中,每个主节点至少挂一个从节点,当某个主节点宕机,它的从节点会被提升为主节点,保证集群可用性。但如果是主从节点同时宕机,集群就会进入不可用状态,所以 Cluster 模式下从节点数量不能太省。

4.4 高可用方案选型建议

实际架构选型时,我有一套简单的取舍思路:

  • 单机 Redis + RDB/AOF,数据量小、可用性要求一般的业务,先用这种最省事的方案,不需要引入额外组件。
  • 主从复制 + 哨兵,大多数互联网业务的首选。读写分离提升读性能,哨兵自动切换保证可用性,部署简单,运维成本可控。
  • Cluster 集群,数据量超过单机内存承载范围,或者写入 QPS 已经超出单机极限时,用 Cluster 做水平扩展。

数据规模在上百 GB 到几个 TB 的场景,不要一上来就 Cluster。先用主从加哨兵,配合业务自身做数据分片,比如按用户 ID 取模分到不同的缓存集群,效果也很好。Cluster 解决了横向扩展问题,但引入了槽位概念、跨节点操作限制、运维复杂度,代价并不小。

5. 常见问题排查与避坑指南

5.1 缓存穿透、击穿与雪崩的应对策略

这三个问题几乎是 Redis 面试问答中的标配,实际业务中也非常常见。

缓存穿透是指查询一个不存在的数据,缓存和数据库中都没有,导致每次请求都要打到数据库上。高并发下,这些无效请求直接把数据库打崩。解决方案有几种:把空结果也缓存起来,设置短过期时间;用布隆过滤器在缓存层前置过滤,不存在的数据直接返回;更简单的方式是参数校验,非法的 ID 直接拒绝。

缓存击穿是指一个热点 key 在过期的瞬间,大量请求同时打到数据库上。解决思路有两个方向:互斥锁,让第一个请求去重建缓存,其他请求等待;或者逻辑过期,不给 key 设置物理过期时间,而是存一个过期时间戳在 value 里,后台线程异步刷新。

缓存雪崩则是指大量 key 在同一时间过期,导致请求全部打到数据库。解决方法是给过期时间加随机抖动,比如基础过期时间 10 分钟,再随机加 0 到 2 分钟,错开过期高峰;同时维护多个副本 key,过期时间不同,某个副本过期了就走另一个副本。

我自己在项目里最常用的组合是:布隆过滤器拦截穿透 + 互斥锁解决击穿 + 过期时间随机化解决雪崩。三个方案配合下来,数据库的请求量非常平滑。

5.2 大 Key 的识别与处理

大 Key 是运维中的定时炸弹。它通常指单个 key 存储的 value 特别大,比如一个大字符串几十 MB,或者一个集合有几百万个元素。

大 Key 的问题在于:删除它时,会阻塞 Redis 主线程,导致线上服务卡顿;做持久化时,fork 子进程和写入磁盘都受影响;主从复制时,传输大数据块导致网络延迟和从节点滞后。

识别大 Key 的方式有几种,最常用的是redis-cli --bigkeys命令,它会扫描整个实例,统计每种类型中最大的 key。但要注意,这个命令本身也会消耗一定资源,最好在业务低峰期执行。

处理大 Key 的原则就是不要直接阻塞删除。Redis 4.0 之后提供了UNLINK命令,它是异步删除,主线程不阻塞。如果没有UNLINK,可以分批删除,集合类型用SRANDMEMBER取出部分成员再SREM,循环多次删除完。

提示:使用UNLINK删除大 key 时,内存并不是立即释放,而是由后台线程慢慢回收。删除后短时间内观察used_memory可能没有明显下降,这是正常现象,不要误以为删除失败。

5.3 内存淘汰策略的选择

Redis 的内存是有限资源,达到maxmemory上限后会触发内存淘汰策略。配置项maxmemory-policy有多个选项:

  • noeviction:不淘汰任何数据,写请求直接报错,这是默认策略。
  • allkeys-lru:从所有 key 中按 LRU(最近最少使用)算法淘汰。
  • volatile-lru:从设置了过期时间的 key 中按 LRU 淘汰。
  • allkeys-random:从所有 key 中随机淘汰。
  • volatile-random:从设置了过期时间的 key 中随机淘汰。
  • volatile-ttl:从设置了过期时间的 key 中,淘汰剩余存活时间最短的。

生产环境的选择逻辑其实很清晰:纯缓存场景用allkeys-lru,让 Redis 自动把不热的数据清掉,保证缓存命中率;如果缓存中混有用了做持久化存储的业务数据,就选volatile-lru,把内存压力集中在有过期时间的 key 上。

有一个坑要特别提醒:maxmemory设置得比实际数据小很多时,LRU 会频繁淘汰 key,导致缓存命中率暴跌,请求大量打到数据库上。这种情况表面看是 Redis 的问题,实际是容量规划出了问题。上线前压测一下流量和内存增长曲线,比事后调淘汰策略重要得多。

5.4 慢查询与热点 Key 的排查思路

Redis 单线程模型意味着一个慢命令可能拖垮整个实例。slowlog是排查慢查询的有力工具,SLOWLOG GET可以看到最近执行的慢命令记录。执行时间超过slowlog-log-slower-than配置(默认 10000 微秒)的命令会被记录。

常见的慢命令有:KEYS *、HGETALL对超大哈希、ZRANGE取超大范围、SMEMBERS取超大集合。这些命令的时间复杂度和集合大小直接相关,数据量大了必然慢。

遇到慢查询,第一反应应该是确认是不是大 key 操作。可以先看 slowlog 里记录的 key,再用STRLEN、HLEN、LLEN、SCARD、ZCARD等命令查看对应 key 的元素数量。如果确认是大 key,优先用UNLINK异步删除,然后改造业务逻辑,把大 key 拆分成小 key,或者用哈希分片。

热点 key 的排查相对更隐蔽。一个 key 的访问量占整体 QPS 的很大比例,会导致对应节点压力过大。排查方式是在客户端埋点统计 key 的访问频率,或者在 Redis 上用MONITOR命令观察,但MONITOR生产环境非常危险,会让性能下降,要慎用。

处理热点 key 有几个常见思路:多级缓存,本地缓存加远程缓存,让大部分请求直接在应用本地命中;key 副本复制,把热点 key 复制多个副本分布到不同节点,读取时随机选择一个副本;热 key 加随机后缀打散,写入时把同一 key 拆成多个不同 key,读取时全部读出再合并。

我记得有一次线上活动,一个爆款商品详情页直接压垮了一个 Redis 节点,因为这个 key 占据了全实例 60% 的访问流量。当时的处理方式是把这个商品的详情缓存复制了 20 份分散到不同节点,瞬间就把单点压力降了下来。这个方案实现简单,效果立竿见影。

6. 数据安全与运维细节

6.1 客户端连接管理与访问控制

Redis 默认监听6379端口,没有密码保护。如果部署在公网或者不可信网络环境,非常容易被扫描和攻击。第一道防线是网络隔离,Redis 只允许内网访问,通过防火墙或者安全组限制来源 IP;第二道防线是开启认证,在配置文件中设置requirepass,客户端连接时需要提供密码。生产环境这两者必须同时具备,不能只依赖一个。

Redis 6.0 之后引入了 ACL 访问控制,可以为不同用户分配不同的权限。比如只读用户只能执行读命令,管理员用户可以执行所有命令。按用户分配权限后,即使某个应用被入侵,攻击者能执行的命令也受限,降低了影响范围。

连接数管理同样重要。默认maxclients是 10000,过高会导致文件描述符耗尽,过低则影响业务并发。曾遇到过一个项目因为连接数被占满报错,排查后发现是应用层连接池配置过大,每个实例创建了几百条连接,几十个实例加起来把 Redis 打爆了。连接池大小不是越大越好,一般每个实例 10 到 50 条连接足够,具体根据 QPS 压测确定。

6.2 容量规划与监控指标

Redis 实例的内存使用需要提前规划。监控时需要重点关注的指标包括:

  • used_memory:实际使用的内存总量。
  • used_memory_rss:操作系统视角下 Redis 占用的内存,包括碎片。
  • mem_fragmentation_ratio:内存碎片率,正常情况下在 1 到 1.5 之间。如果长期大于 1.5,说明碎片严重,需要重启实例或执行memory purge。
  • hit_rate:缓存命中率,命中率过低说明缓存设计有问题。
  • connected_clients:当前连接数。
  • instantaneous_ops_per_sec:实时 QPS。
  • rejected_connections:因达到最大连接数而被拒绝的连接数。

容量规划的一个实用公式是:内存预算 = 数据预计大小 × 1.5 到 2 的膨胀系数。Redis 存储字符串时,除了 value 本身的内存,key 和字典结构还有额外开销。一个小字符串 key,实际占用内存可能是字符串长度的两三倍。所以估算容量时不能只看数据大小,要按倍数预留。

我之前接过一个项目,上线前预估数据量 20 GB,实际跑了三个月就飙到 40 GB。原因就是 key 设计太粗糙,每个用户有十来个缓存 key,每个 key 名字都很长。后来优化 key 命名,缩短前缀,调整数据类型,内存直接砍半。字符串 key 尽量短,合理利用哈希结构,能从源头上省内存。

6.3 数据备份与恢复演练

数据备份是最后的保险手段。即便有了主从和哨兵,也不代表数据绝对安全。人为误操作、主从数据不一致、恶意删除,都可能造成数据丢失,定期备份到独立存储非常必要。

备份方式就是把 RDB 文件或者 AOF 文件定期复制到备份服务器或对象存储。可以通过脚本定时执行BGSAVE,等 RDB 文件生成完成后,用redis-cli --rdb或者直接复制文件到备份位置。备份文件要保留多个版本,比如保留最近 7 天的,防止备份文件本身损坏或数据被污染。

恢复演练比备份本身更重要。我见过不少团队备份脚本写得很好,但从来没有真正恢复过。等到真的需要恢复数据的时候,发现备份文件不完整、恢复流程有 bug、版本不兼容,整个备份形同虚设。建议每个季度做一次完整的恢复演练,从备份文件中把数据恢复到一台全新的 Redis 实例上,验证数据完整性。

阿里云、腾讯云等云厂商提供的 Redis 服务一般都有自动备份功能,配置简单,但要注意备份保留周期和恢复方式是否满足业务要求。自建 Redis 的话,备份脚本要加报警,备份失败时及时通知,避免长时间无人发现备份中断。

7. 我对 Redis 使用的一些体会

用了这么多年 Redis,最大的体会是它很强大,但也很脆弱。强大在于丰富的数据结构和高效的性能,脆弱在于它是单线程模型,一个命令、一个大 key、一次持久化操作,都可能让整个服务卡顿。所以生产环境的 Redis 一定要当作基础设施来对待,上线前做好规划,运行中做好监控,操作时保持敬畏。

经常有人问,什么时候不建议用 Redis?我的回答是:不要什么事都用 Redis。真正的持久化存储、复杂的事务、跨多键的强一致操作,Redis 并不擅长。把 Redis 当成一个高速缓存和专用数据结构的工具箱,放在合适的位置上,它才能发挥最大的价值。

最后分享一个我在项目里常用的自查清单:新上的缓存 key,是不是都设置了合理的过期时间?有没有可能成为大 key?会不会成为热点?删除策略是什么?容量够不够?监控告警有没有覆盖?每次上线排查一遍这些问题,能省掉很多半夜被报警叫醒的烦恼。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询