ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Redis 核心实践:从安装部署到缓存治理与高可用架构全解析

Redis 核心实践:从安装部署到缓存治理与高可用架构全解析 只要你的项目里用到过 Redis这几件事你大概率都经历过刚装好不知道从哪儿看数据用 RDM 连上去发现 key 全是乱码部署主从复制时端口和密码没配对日志刷了一屏又一屏缓存一预热线上接口反而被击穿数据库直接报警。这些不是某个团队的独特问题而是 Redis 使用路上几乎人人都会踩的坑。这篇内容我按自己的实际使用经验重新梳理了一遍从安装部署、数据类型、客户端接入到缓存治理、主从哨兵集群和面试高频考点尽量把“为什么这么做”也讲清楚而不只是丢给你一堆命令。Redis 本身就是一个基于内存的键值数据库核心价值是“把热数据放到离应用更近的内存里”靠单线程模型和 IO 多路复用把单次操作压到亚毫秒级。它能做缓存、分布式锁、排行榜、限流、会话共享也能兼职做简单消息队列。适合正在做后端开发、系统设计、或者准备面试的同学参考也适合那种 Redis 一直“能用但说不透”的人把底层逻辑一次补齐。1. Redis 凭什么这么快一句话理解它的核心模型1.1 它到底解决了什么问题在没有 Redis 之前后端服务读数据基本就是查数据库MySQL 这类磁盘数据库的单行查询通常需要 5 到 10 毫秒如果是复杂查询或者慢 SQL几十毫秒也很正常。当请求量上来之后数据库连接数和磁盘 IO 会先撑不住于是大家开始想“能不能把经常读的数据放在内存里”。Redis 解决的就是这个问题它把所有数据都存在内存中读写都是内存操作官方数据是单线程也能跑到 10 万级 QPS而一台 MySQL 在合理配置下往往只有几千 QPS。这个定位决定了适合用它的场景读多写少、数据结构相对简单、对响应时间要求高的热点数据。比如用户的登录信息、商品详情页的聚合数据、微博热搜榜、实时在线人数。不适合拿它做核心业务的唯一数据源因为内存有限数据丢了可以被重建它更适合当“加速层”而不是“存储层”。1.2 单线程模型和 IO 多路复用很多人第一次听到“Redis 是单线程”会困惑单线程怎么还能这么快其实这里的单线程指的是网络 IO 和键值读写命令的执行是单线程的Redis 6.0 开始网络层引入了多线程处理 IO但命令执行线程只有一个。它快不是因为单线程而是因为避免了多线程上下文切换和锁竞争。真正支撑高并发的是 IO 多路复用机制也就是一个线程可以同时处理成千上万个 socket 连接不需要每个请求都创建一个新线程。这里有个非常关键的实践经验不要在线上执行KEYS *。这个命令会阻塞整个 Redis 主线程遍历所有 key数据量一大线上直接卡顿我在本地库百万 key 上试过一次阻塞了将近 8 秒放线上就是事故。替代方案是SCAN它是游标式的增量遍历每次返回一部分数据不会长时间阻塞。这是一个“原理理解不到位就会出事”的典型例子。1.3 先立个前提Redis 不是万能的我见过不少人把 Redis 当万能存储用什么数据都往里面塞最后内存暴涨、淘汰策略乱套。必须明确Redis 是内存数据库价格的单位是 GB磁盘数据库是 TB所以它只适合存“值得用内存换性能”的数据。另外它的事务和持久化都有取舍ACID 里的一致性做不到数据库那样严格主从复制也可能有秒级延迟。理解这些边界不是限制你用它而是为了在架构选型时判断“Redis 在这个位置合不合适”这个判断能力才是系统设计面试里真正考的东西。2. 装好 Redis 是第一步三条部署路线和两个可视化工具2.1 Windows 本地部署最省事的开发环境方案我最早学 Redis 就是在 Windows 上那时候官方其实不提供 Windows 版本大家只能找微软维护的老版本或者用内存镜像的方式跑 Linux 子系统。现在方便多了Windows 上想快速跑起来至少有三种方式第一种是直接下载 Memurai 或 tporadowski/redis 这类 Windows 移植版解压后运行redis-server.exe即可第二种是用 WSL 跑 Linux 版本第三种是装 Docker Desktop 后跑容器。Windows 版有个实用细节想让 Redis 作为服务开机自启可以在解压目录里把redis-server.exe --service-install redis.windows.conf --loglevel verbose这句命令跑一遍之后用redis-server --service-start启动用redis-server --service-stop停止。这样开发机重启后不用再手动敲命令。注意默认配置下 Redis 是绑定 127.0.0.1 的如果你在 WSL 或远程 Windows 虚拟机里跑记得把配置里的bind 0.0.0.0放开同时一定要设置密码否则局域网内任何机器都能连进来。2.2 Linux 编译安装生产环境的标准姿势虽然包管理器如 apt、yum可以直接装但生产环境我更推荐编译安装原因很简单可以自定义版本、自定义编译参数比如打开内存分配器 jemalloc、调整最大内存。编译安装的完整流程并不复杂先装编译依赖再下载源码包解压进入目录执行make和make install然后复制配置文件到/etc/redis/目录用redis-server /etc/redis/redis.conf启动。生产配置里有两个我每次都会主动改的点第一个是daemonize yes让 Redis 后台运行否则你一关终端服务就停了第二个是requirepass必须设置强密码。还有一个冷门但重要的参数是protected-mode默认是 yes如果你没设密码它只允许本机连接一旦你设置了密码但忘了关掉绑定的公网 IP还是会有扫描风险。我的习惯是生产环境bind只写内网 IPprotected-mode yes然后用防火墙只放行应用服务器到 Redis 的端口。2.3 Docker 部署五分钟拉起主从复制如果是微服务架构或者测试环境Docker 是效率最高的方式。一条命令能完成单机部署docker run -d --name redis -p 6379:6379 redis:7.2 redis-server --appendonly yes --requirepass yourpassword。比手工编译省太多事了而且版本回滚很容易镜像标签切一下就能换版本。更值得一试的是用 Docker Compose 直接编排一个一主两从的复制集群。下面这个配置我实际测试过三个容器主节点写两个从节点同步应用连接主节点或者读分离连从节点都行services: redis-master: image: redis:7.2 container_name: redis-master command: [redis-server, --appendonly, yes, --requirepass, masterpass] ports: - 6379:6379 redis-slave-1: image: redis:7.2 container_name: redis-slave-1 command: [redis-server, --slaveof, redis-master, 6379, --masterauth, masterpass, --requirepass, slavepass] depends_on: - redis-master redis-slave-2: image: redis:7.2 container_name: redis-slave-2 command: [redis-server, --slaveof, redis-master, 6379, --masterauth, masterpass, --requirepass, slavepass] depends_on: - redis-master这里有个特别容易踩的坑从节点同步主节点数据时如果主节点设了密码从节点必须通过--masterauth提供主节点的密码否则从节点的日志会不断刷MASTER aborted replication with an error: NOAUTH Authentication required。而且你给从节点自己设的--requirepass和--masterauth是两个不同的概念前者控制外部客户端连接从节点后者控制从节点连接主节点。我见过不少人只设了前者结果主从一直连不上日志还没仔细看白白排查了半天。启动之后可以用redis-cli -a masterpass info replication检查如果看到role:master和两个slave0、slave1状态为 online说明复制已经建立。2.4 可视化客户端RDM 和 Another Redis Desktop Manager命令行用多了之后可视化工具能让你快速确认数据结构长什么样。目前主流的有两个Redis Desktop Manager简称 RDM和 Another Redis Desktop Manager简称 ARDM。RDM 是早期最流行的桌面客户端界面成熟支持 Redis Cluster、SSH 隧道、从节点只读模式显示ARDM 是完全免费开源的跨平台支持 Windows、macOS 和 Linux在大 key 扫描和内存分析上做得不错。我的建议是日常开发调试用 ARDM因为它免费、轻量、更新活跃如果需要跑性能压测或者做深入的键空间分析可以用 RDM 或者直接用redis-cli。顺便说一句连不上 Redis 时别先怀疑客户端先用redis-cli -h ip -p 6379 -a password ping测通网络和密码如果是云服务器还要检查安全组是否放行了 6379 端口。很多“连接失败”不是因为工具坏了而是云服务器防火墙没放开。3. 五大数据类型命令、用途和面试怎么答3.1 String最朴素也最容易玩出问题的类型String 是 Redis 里最基础的类型value 最大 512MB可以存字符串、整数、浮点数也能存二进制数据。常用命令就那几个SET、GET、MSET、MGET、INCR、DECR、EXPIRE。业务里最典型的是缓存数据库查询结果、计数器和分布式 ID 生成。这里必须提醒一个容易被问倒的细节INCR的原子性依赖 Redis 单线程模型所以并发下不会出现计数加错的问题。但如果你先GET出来在应用层加一再SET回去中间就有竞态窗口高并发下一定不准。有一次朋友排查线上接口的访问统计发现数据总是偏少问题就出在应用层做了先读后写而不是直接用INCR。正确写法是让 Redis 自己完成递增不要用“读出-修改-写回”的模式。还有一个关于SET扩展参数的经验SET key value NX EX 10表示 key 不存在时才设置同时设置 10 秒过期。这个能力是后面分布式锁的基础面试里经常会被问到所以最好记成“NX解决存在性判断EX解决自动释放”两条合在一起才能实现带超时的锁。3.2 Hash对象存储的省内存方案Hash 在业务里对应 Java 的 Map、Python 的 dict适合存对象字段。比如用户信息里有昵称、头像、手机号你用 String 拼个 JSON 也完全可以但用户只改头衔的时候就要整个 JSON 读出来改完再写回去很浪费。用 HashHSET user:1001 name tomy、HSET user:1001 age 25只更新头像字段时只需HSET user:1001 avatar new_url不需要动其他字段。命令还有HGETALL、HINCRBY、HDEL。Hash 另一个好处是省内存这个可能很多新手不知道。Redis 内部对字段少、值小的 Hash 做了紧凑编码ziplist/listpack比同样数据用 String 存储要省不少字节。但要注意一点Hash 的字段太多之后会升级成哈希表结构内存占用会上升。所以不要把上万个字段全塞进一个 Hash合理的做法是让一个 Hash 对应一条业务记录。3.3 List消息队列的低配替代List 底层是双向链表新版是 quicklist支持从左边 push 往右 pop也支持索引范围读取。命令是LPUSH、RPUSH、LPOP、RPOP、LRANGE、LLEN。业务上最常见的三个用法最新公告或动态列表、简单消息队列、生产者-消费者模型。如果想用它做可靠队列要记住一个关键点LPOP是弹出即删除消费者拿到消息后万一处理失败消息就丢了。可靠一点的办法是BRPOPLPUSH它把消息从主队列的尾部弹出同时写入备用队列业务处理成功后再从备用队列删除如果失败还能从备用队列捞回来重试。这个操作是原子的比先POP再手动写回要安全很多。还有BLPOP和BRPOP是阻塞版本适合需要“没有消息就先等着”的场景。但我也要泼一盆冷水如果消息量很大、需要消费确认、需要分区、需要严格的消息顺序和回溯List 不适合直接用专业消息队列。Redis List 的优势是零额外组件项目早期或者消息量可控时非常顺手这也是它被称为“低配 MQ”的原因。3.4 Set去重和集合运算Set 的特点是元素唯一、无序内部用哈希表实现。常用命令有SADD、SREM、SISMEMBER、SMEMBERS、SCARD更高级的是集合运算SINTER交集、SUNION并集、SDIFF差集。面试里最经典的问题是“如何用 Redis 实现关注关系”你可以把每个用户的关注列表存成一个 Set然后直接SINTER算出共同关注。实际业务场景里把“某个活动已经领取过优惠券的用户”存成 Set判断用户是否领过用SISMEMBER就结束了复杂度是 O(1)。还有一个常见需求是抽奖去重把参与用户SADD进一个活动 Set抽奖后用SPOP随机弹出一个用户天然保证不重复中奖。如果只是随机抽样但不移除用SRANDMEMBER。3.5 ZSet带权重的排序利器ZSet 是 Redis 里我最喜欢的数据类型它在 Set 的基础上加了一个 score 字段每个元素都有排序权重内部用跳跃表 哈希表实现。命令是ZADD、ZRANGE、ZREVRANGE、ZINCRBY、ZSCORE范围操作还有ZRANGEBYSCORE和ZRANGEBYRANK。排行榜几乎就是 ZSet 的代名词用户每增加一积分ZINCRBY leaderboard 10 user_1001要展示 Top 10 就用ZREVRANGE leaderboard 0 9 WITHSCORES。ZSet 还能做延迟任务队列score 存任务计划执行的时间戳消费者轮询ZRANGEBYSCORE task_queue -inf now LIMIT 0 1拿到到期任务后处理再ZREM掉。这个设计比 List 队列更优雅因为它支持“按时间戳排序”这个天然需求。面试里如果提到“有序集合为什么用跳跃表而不用红黑树”你至少要知道跳跃表实现简单、范围查询友好、且和内存高效利用配合得好这个具体细节我会在后面面试章节再展开讲。3.6 键设计和过期策略没人告诉你的隐性规则键设计直接决定后期维护成本我的习惯是“业务:对象:id”比如user:1001:info冒号分隔。这样的好处是一、可读性强看到 key 就知道是什么数据二、在 RDM 里可以按前缀折叠浏览三、用SCAN MATCH user:*能批量处理。千万别把无意义字符串当 key不然过两天连你自己都不知道那是什么。过期策略也是高频考点Redis 默认是惰性删除加定期删除。惰性删除是访问时发现过期才删定期删除是每隔一段事件随机抽一批 key 检查并删除。如果过期 key 没有被访问惰性删除就不会触发定期删除又有抽样概率极端情况下过期 key 会残留占用内存。所以对大批量 key 设置过期时间时最好分批用SCAN配合TTL检查或者干脆按业务前缀批量设置统一过期时间。另一个坑是在高峰期同时给大量 key 设置相同过期时间比如 3600 秒结果到点后同一秒全部过期——这是雪崩的一个常见诱因后面我会详细讲如何打散过期时间。4. 客户端接入与序列化别让乱码毁掉可视化调试4.1 客户端选型Java 和 Python 怎么选Java 生态我最常用的客户端是 Lettuce 和 Redisson。Redis 官方推荐的 Jedis 简单直接支持同步和管道Lettuce 基于 Netty默认支持异步和响应式连接复用性能好Redisson 的杀手锏是内置了分布式锁、限流器、延迟队列这些高级工具代码写得非常省力。我的建议是普通项目同步操作多选 Jedis 或者 Lettuce 都行如果要用分布式锁、Semaphore 这些高级功能直接上 Redisson。Spring Boot 项目默认用的是 Lettuce如果遇到连接不够的问题要调spring.redis.lettuce.pool.max-active而不是傻傻地改数据库连接池。Python 那边就是 redis-py 库老牌稳定。基础用法很简单import redis r redis.Redis(hostlocalhost, port6379, db0, passwordyourpassword) r.set(user:1001:name, tomy) print(r.get(user:1001:name))不过 Python 生产环境里要注意连接池不要每次请求都新建Redis实例最好用redis.ConnectionPool否则高并发下连接数会一直涨最终把 Redis 的文件描述符打满。另一个 Python 特有的坑是get返回的是 bytes 字节串而不是 str如果存的是数字要用int()转一下存 JSON 对象的话要先json.dumps再写入读出来json.loads很多人第一次连项目时就在这上面踩坑。4.2 序列化为什么 RDM 里全是 \xac\xed 开头对于 Java 的 Spring Data Redis默认的 JdkSerializationRedisSerializer 会把对象序列化成二进制后存进去所以你在可视化工具里看到 key 或 value 是一堆\xac\xed\x00\x05t\x00...这样的字节根本没法读。这是“redis 序列化”这个搜索词背后最核心的痛点我见过不止一个新手在这个问题上卡了两三天才想明白。生产环境我更推荐 JSON 序列化选择GenericJackson2JsonRedisSerializer并且将 RedisTemplate 的 keySerializer 和 hashKeySerializer 设置为 StringRedisSerializer这样 key 就保持可读的字符串value 用 JSON。直接把下面这段配置放到项目里基本能解决乱码问题Configuration public class RedisConfig { Bean public RedisTemplateString, Object redisTemplate(RedisConnectionFactory factory) { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(factory); StringRedisSerializer stringSerializer new StringRedisSerializer(); GenericJackson2JsonRedisSerializer jsonSerializer new GenericJackson2JsonRedisSerializer(); template.setKeySerializer(stringSerializer); template.setHashKeySerializer(stringSerializer); template.setValueSerializer(jsonSerializer); template.setHashValueSerializer(jsonSerializer); template.afterPropertiesSet(); return template; } }注意 JSON 序列化的代价是体积偏大、反序列化时需要知道对象类型信息所以如果是对性能极度敏感的服务可以考虑 ProtoStuff 或 Kryo 之类的二进制序列化方案。我的经验是业务开发阶段优先保证可读性和排错效率用 JSON等到出现明显的网络和内存压力再评估切二进制序列化不要一开始就为了“极致性能”牺牲可维护性。4.3 连接池、超时和重试的基本盘客户端接入还有一个隐形坑连接池配置不当。连接池太小大流量一来所有线程都在等连接连接池太大反而把 Redis 服务器的连接数打爆。一般经验是每个实例的连接池max-total控制在 20 到 50 之间max-idle控制在 10 到 20min-idle至少 2 到 5这样可以快速响应流量波动又不至于资源浪费。超时配置也要合理。连接超时建议 2 到 5 秒读超时 3 到 10 秒不要在 Redis 卡住的时候让应用无限等待。如果是缓存场景宁可快速失败回到数据库也不要让请求全部阻塞在 Redis。重试策略要特别谨慎写操作重试可能导致重复执行所以在分布式锁这类场景中重试必须配合幂等设计。我感觉这是很多人忽略的Redis 很快没错但“快”不代表不会出故障客户端的基本盘配置决定了故障时的表现是优雅降级还是雪崩。5. 缓存治理三板斧穿透、击穿、雪崩5.1 缓存穿透把“查不到”的数据也缓存起来缓存穿透指的是请求的数据在缓存和数据库里都不存在比如查询一个不存在的用户 ID缓存没有数据库也没有请求每次都打到数据库。如果有攻击者拿一批不存在的 ID 来刷数据库瞬间就能被打挂。经典的解决方案是“空值缓存”即使数据库没查到数据也把空结果缓存几分钟这样后续相同请求直接命中空值不再穿透数据库。注意要给空值设置较短的过期时间比如 5 分钟避免数据库里真插入了数据后缓存长期是空值。更严格的做法是布隆过滤器。把所有可能存在的 ID 预先写入布隆过滤器查询时先看布隆过滤器如果说不存在就直接返回不再查缓存和数据库。这里有一个必须提醒的点布隆过滤器有误判率返回“存在”不一定真的存在最终还是需要落到数据库但返回“不存在”一定不存在。所以布隆过滤器拦截的是“确定不存在”的那部分请求空值缓存拦截的是“数据库查询后的空结果”两者可以叠加用。布隆过滤器在网络中是有解决方案的Redis 官方模块 RedisBloom 提供了BF.ADD和BF.EXISTS不用自己实现很方便。5.2 缓存击穿热 key 过期瞬间的并发风暴击穿和穿透名字像问题完全不同。击穿是某个热点 key 刚好过期瞬时可能有几万个请求同时去数据库查数据库压力瞬间拉满。它的关键特征是“单个热 key”而不是“一批不存在的 key”。解决办法有三类互斥锁、逻辑过期、永不过期。互斥锁是最容易理解的方案发现缓存没命中先尝试获取分布式锁拿到锁的线程去数据库查并回填缓存其他线程等待一段时间后重读缓存。实现要小心不能用普通的SETNX加锁后忘记释放否则一旦程序异常就死锁了。逻辑过期方案是把缓存值的过期时间作为字段一起存在 value 里当发现逻辑上过期后只有一个线程去刷新其他线程直接先返回旧值。这个方案性能最好因为不存在锁等待但缺点是会短暂返回旧数据。永不过期适合那种“看似实时但可以接受短时间滞后”的数据配合后台任务定时刷新即可是业界很常见的兜底策略。5.3 缓存雪崩批量过期和宕机引发的连锁反应雪崩是缓存层大面积不可用所有请求直接打到数据库数据库也挂然后整个服务链路雪崩。常见诱因有两个一个是大量 key 设置了相同的过期时间到点集体失效另一个是 Redis 实例宕机缓存层全挂。针对“批量过期”解决手段就是过期时间打散不要统一设 3600 秒可以设成3600 random(0, 600)秒。JVM 里实现也很简单加一个随机参数就行。针对“宕机”靠的是高可用架构主从 哨兵保证自动故障转移也就是下面要讲的主从哨兵集群这是治本方案。顺便说一个我从故障里学到的经验雪崩往往不是缓存层单独的问题而是应用层的失败处理方式有问题。如果 Redis 超时后应用直接抛异常数据库压力会瞬间爆炸如果 Redis 超时后应用返回兜底数据或降级响应影响面就小很多。所以缓存治理永远要结合降级、熔断、限流一起设计单纯依赖 Redis 高可用是不够的。5.4 缓存更新先删缓存还是先更数据库缓存更新的代码顺序问题也很经典。常见的错误是先更新数据库再删缓存但删缓存失败的话旧缓存会一直存在。更稳妥的做法是先改数据库再删缓存下一次读请求发现缓存缺失再回填新数据。为什么不是先删缓存再改数据库因为先删缓存后有请求可能把旧数据读出来写回缓存等真正更新数据库完成后缓存里还是旧值不一致的时间窗口更长。还有个更隐蔽的坑更新数据库和删除缓存这两个操作没有原子性任何一步失败都可能造成不一致。这时候可以用本地事务 消息队列或者订阅 Binlog 的方式做最终一致性。如果业务对一致性要求没那么高砸之前先接受“短时间不一致”这个事实然后定期全量刷新缓存兜底。我在实际项目里常用的组合是先更库再删缓存同时给缓存设置一个合理的过期时间作为最终一致性兜底即使中间出了意外过期后也能自然恢复。6. 分布式锁与主从哨兵从单机走向高可用6.1 分布式锁SET NX EX 和它在业务里的两种经典错误分布式锁是 Redis 的一个高频应用核心命令是SET key value NX EX seconds这里NX保证只有 key 不存在时才设置成功从而实现互斥EX设置过期时间防止客户端崩溃后锁永远不会释放。正确用法SET lock:order:10001 uuid_value NX EX 30拿到锁的业务方做完整件事后用 Lua 脚本原子地检查 value 并释放锁if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end这里我强调两点。第一value 必须唯一标识持有者这样才能防止“别人的锁被自己删除”。第二释放锁要用 Lua 脚本保证“判断和删除”是原子操作直接GET之后再DEL存在竞态你的锁已经过期另一个线程拿到了新锁你却把自己判断通过后的DEL删掉了别人的锁。这个错误非常典型如果你把这段 Lua 脚本记下来生产中能少踩一半的锁相关坑。Redisson实现的lock()方法比手动脚本更省心它默认帮你做了看门狗续期不会出现业务执行超过锁过期时间导致锁提前释放的问题。但 Redisson 的续期有代价主节点如果宕机锁数据还没来得及同步到从节点就可能出现锁丢失极端场景下两个客户端同时拿到锁。要严格规避这个风险只能上 Redlock 算法或者引入 ZooKeeper 的强一致方案。面试如果考到这个点你能说清楚“单节点锁的局限”和“Redlock 的价值”而不是只会套命令就已经能和其他候选人拉开差距了。6.2 主从复制读写分离和数据冗余的基础前面已经用 Docker Compose 搭过一主两从了这里再补充两个细节。主从复制的作用是数据冗余和读写分离主节点负责写从节点负责读即使主节点宕机从节点还有一份数据可以顶上。复制方式是异步复制主节点执行完写命令后立即返回不等待从节点确认所以主从之间存在短暂的延迟。正常情况这个延迟在毫秒到秒级但在网络抖动或大数据量同步时可能变大。生产环境里如果你用从节点承担了读流量要去监控复制延迟重点看redis-cli info replication里的master_repl_offset和从节点的slave_repl_offset两者差值就是滞后字节数。某些业务对一致性要求高读到旧数据会出问题那就不应该读从节点或者读从节点时要加一个“延迟时间超过阈值就重新读主节点”的逻辑。另外从节点默认只读不能直接写这是避免数据不一致的重要保护别想着“反正它也是 Redis我顺手写一下试试”这会把主从数据搞乱。6.3 哨兵模式 vs 集群模式别再把它们混为一谈这是面试里最容易被问懵的点也是实际架构选型最容易混的概念。哨兵模式全称是高可用方案它不承担数据分片核心工作是监控主节点状态主节点挂了自动把从节点提升为新主节点客户端通过哨兵发现当前主节点地址。数据量受单机内存限制只是解决了“不让服务中断”的问题。集群模式也叫 Redis Cluster它是数据分片方案把 key 按照 CRC16 算法哈希到 16384 个槽里每个节点负责一部分槽数据量可以横向扩展。Redis Cluster 本身也带主从副本每个分片可以有从节点节点故障时会自动切换。选择可以根据数据量和可用性需求来定如果你的 Redis 数据总量不超过单机内存的六到七成、只追求高可用用主从加哨兵就够了如果数据量会涨到单机放不下必须用 Cluster 做数据分片。很多公司早期数据量不大硬上 Cluster结果不仅没有简化运维还因为多节点管理和跨槽命令限制增加了复杂度这种“过度设计”我见过不止一次。6.4 日志和故障排查从 redis.conf 到命令行三板斧学 Redis 一定要会看日志。在生产环境日志文件位置通过logfile配置指定通常放在/var/log/redis/redis.log。当出现主从切换问题时日志里会出现instance Failed to connect MASTER、MASTER timeout等通常是网络不通、密码不对或主节点bind限制导致。磁盘问题也常见AOF 持久化时如果磁盘满了日志会刷Cant open the append-only file这时候要优先检查磁盘空间。命令行排查三板斧我整理成一套固定动作先redis-cli ping测基本连通性再redis-cli info server看版本和运行时长最后redis-cli info replication看主从关系。如果是慢查询问题到 redis.conf 中设置slowlog-log-slower-than 10000单位微秒然后用SLOWLOG GET 10查看近 10 条慢命令。很多“卡顿”问题最后都指向一些非常简单的操作比如大 key 的DEL所以当你发现线上突然卡一下先查SLOWLOG再查大 key基本能定位大部分问题。顺便给个建议禁用或谨慎使用KEYS *已经是老生常谈了但生产环境真正稳妥的做法是直接在配置里把危险命令禁用掉比如把RENAME之类的重命名或者用ACL限制权限。7. 高频面试题和避坑速查表最后再帮你收个尾7.1 高频面试题清单基于近几年后端岗位的实际面试情况Redis 相关的问题集中在这几个方向。数据结构方面五种数据类型的使用场景、ZSet 为什么用跳跃表、List 和 Set 的应用区别。持久化方面RDB 和 AOF 的各自优缺点、怎么选、混合持久化的意义。缓存方面穿透、击穿、雪崩的定义和解决方案、缓存一致性怎么做。并发方面分布式锁的实现方式、锁续期怎么做、单节点锁的局限。集群方面主从复制原理、哨兵和 Cluster 的区别、异步复制造成的延迟问题。回答这些问题的关键不是背答案而是有实际经验支撑。比如回答“ZSet 为什么不用红黑树”可以说跳跃表实现简单、支持范围查询更容易红黑树需要复杂的旋转操作而且 Redis 本身只需要排序和范围操作不需要平衡树的严格性。只要你真正操作过的类型和命令足够多这些细节自然能说得出来。7.2 常见问题与排查速查表我根据自己的经验整理了一张速查表覆盖实际问题中反复出现的情况你可以直接存下来遇到问题先对着查一遍。现象可能原因排查命令 / 解决思路客户端报连接超时bind 限制、安全组未放行、密码错误redis-cli -h IP -p 6379 -a pass ping检查安全组从节点一直 sync 失败masterauth 未设置或密码不对看日志NOAUTH补--masterauthkey 全是二进制乱码序列化方式用了 JDK 默认RedisSerializer改用 JSON String key 序列化器热点 key 高并发穿透缓存过期瞬间大量请求打库热 key 逻辑过期 互斥锁回填内存越来越大过期 key 未释放惰性删除 定期删除概率遗漏开启maxmemory-policy allkeys-lru分批扫描某条命令特别慢阻塞服务大 key 操作、KEYS *遍历SLOWLOG GET 10禁用危险命令主从切换后数据丢失异步复制 主节点故障未同步业务应接受短时间不一致开启wait或强一致方案分布式锁误删他人锁释放时没有校验 value用 Lua 脚本原子删除value 唯一这张表里的每一条都来自真实故障场景。比如“内存越来越大”这个事线上 Redis 如果开启了maxmemory且淘汰策略是noeviction写入新数据时会直接报OOM command not allowed when used memory maxmemory此时线上写入就失败了。所以配置maxmemory-policy一定要根据业务选普通缓存用allkeys-lru如果有些 key 不允许被淘汰那就用volatile-lru并给重要 key 单独不设置过期时间。7.3 多年实践下来我最后想说的几句话Redis 给我的最大感受是它看似简单命令也不难但真正用好它需要的是对“内存、单线程、复制、持久化、一致性”这些底层机制的理解。很多人觉得学会了五种数据类型就算会用 Redis 了可一到线上各种问题还是层出不穷乱码、连接池打满、主从不复制、缓存击穿打挂数据库、锁被人删了。这些问题没有一个是靠多背几条命令能解决的都得回到原理上去想。我个人每次排查 Redis 故障的时候都会先问自己一句如果它在最坏情况下挂了我的应用会怎样然后就会发现真正的高手不会把所有希望寄托在 Redis 的高可用上而是会提前做好降级兜底、合理设计过期时间、把危险命令关掉、让缓存更新有最终一致性兜底。这些“防患于未然”的意识比多写十行缓存代码更值钱。如果你刚开始学 Redis建议照着这篇文章把环境装起来把五种数据类型各存一遍然后用 RDM 看一眼它们的存储形态再做一次主从复制故意把masterauth写错看日志里到底报了什么最后用一个本地接口模拟一次缓存击穿观察数据库连接数是怎么飙上去的。把这些场景都亲手跑过一遍你对 Redis 的理解会比读十本书都深刻。这个内容后面还可以扩展的方向是深入研究 Redis 7 的 Redis Function、多线程 IO 的调优参数、以及RESP3协议的变化这些属于进阶话题了等你把基础和踩坑经验都补齐了再碰也不迟。
返回列表