☰
Redis实战笔记2026:部署、数据类型与生产排障全攻略
2026/9/28 23:18:46 网站建设 项目流程

不用怀疑标题,这套Redis笔记确实是我压箱底的东西。从2018年第一次在生产环境部署Redis到现在,中间踩过的坑、查过的源码、整理过的应急预案,零零散散记了七八年。2026年这版不是简单换个年份,我把过时的命令、废弃的配置项、已经改版的客户端工具全部重新过了一遍,同时补齐了阿里云环境下的RDS托管、云服务器部署、Docker容器化、Maven仓库配置等实操内容,顺手把面试高频题和线上故障排查方法整理成了速查表。内容不追新,但一定够用,适合正在学Redis的人、马上要背面试题的人,以及想把生产环境里的缓存问题彻底搞清楚的人。

1. 为什么Redis值得花时间吃透,以及这套笔记的核心思路

1.1 Redis到底解决了什么问题,我说点实际的

Redis是个内存型数据库,处理的是“热数据”的读写问题。很多人在初学阶段把它当成一个“高级点的HashMap”,这没错,但只看到了表面。如果你把应用想象成一家餐厅,MySQL是后厨的大仓库,所有食材、半成品都存在里面,但每次招待客人都要跑大仓库翻一遍,速度跟不上。Redis就是前台那张登记台,客流高峰前,你把这些客人最常用的菜品信息、库存余量、会员余额摆到前台,服务员一抬手就能拿到,不用再往后厨跑。

但Redis能做的事远不止缓存。它支持字符串、哈希、列表、集合、有序集合这些基础类型,还有Bitmap、HyperLogLog、Geo这几种高级结构,能做分布式锁、延迟队列、排行榜、UV统计、附近的人。再加上Redis Cluster、哨兵、主从复制这套高可用体系,它基本成了一个独立的基础设施,而不是某个应用里的附属组件。

这套笔记不是为了让你记住几条命令,而是帮你建立一套完整的使用框架:什么场景该用哪种结构、部署的时候选什么形态、出了问题怎么排查。我见过太多人把Redis用成“缓存+分布式锁”,其实还有很多玩法没发挥出来。2026版我在内容上做了几处调整:删掉了已经过时的配置和旧版客户端写法的示例,补上了Docker、RDS、云服务器这三种部署形态的对比,增加了大量线上故障的真实案例。

1.2 2026版重写了哪些内容,适合谁看

这套笔记的主体分四块:环境搭建、数据类型、工程实战、运维排障。每一块都是围绕“实际能用”这个标准来写的。环境搭建部分覆盖了Windows本机、Linux服务器、Docker容器、阿里云RDS四种部署方式;数据类型部分用真实的业务场景串讲,而不是罗列命令;工程实战部分包含Java接入方式、序列化方案、Maven阿里云仓库配置、分布式锁和缓存治理;运维排障部分包括日志分析、慢查询定位、连接数排查和常见问题速查表。

不同人拿到的价值不一样。如果你是个完全没接触过Redis的新手,按顺序读就行,前面环境搭建部分足够把你带到能跑起来的程度,后面案例部分相当于提前帮你趟一遍雷。如果你有两年左右工作经验,重点看第三部分的缓存治理和第四部分的排障思路,这两块是最靠经验积累的,也是面试最容易深挖的地方。如果你正在准备面试,最后一章整理过的高频题和答案方向可以直接拿来复习。

2. 环境准备:从零装好Redis,并配上一套顺手的可视化工具

2.1 Windows本机安装,别在这上面浪费太多时间

很多教程喜欢在Windows上用官方MSI安装包,但我更推荐下载ZIP压缩包解压即用,理由只有一个:干净。官方Windows安装包会把Redis注册成Windows服务,还会改注册表,卸载起来很麻烦。ZIP包下载下来解压到一个固定目录,比如D:\redis,接下来就完事了。

# 启动服务端 D:\redis\redis-server.exe D:\redis\redis.windows.conf # 另开一个窗口,启动命令行客户端 D:\redis\redis-cli.exe -h 127.0.0.1 -p 6379

启动之后先在客户端里输入ping,返回PONG说明通了。Windows版本有几个注意点:一是这个版本本质是微软维护的移植版,版本号通常落后于Linux官方版本,只适合本地学习,别用在生产环境;二是配置文件里默认protected-mode yes,如果后面想从其他机器连接,只改bind和protected-mode还不够,还要配置requirepass,否则按默认配置你是连不上的。

本地学习阶段有个常见误区:非要在Windows上把Redis Cluster跑起来。我劝你别折腾,Windows版对Cluster的支持不够好,报错都不太一样。本地只练单机命令就够了,分布式相关的内容上Docker或者云服务器,后面第三部分会讲到。

2.2 Linux安装并交给systemd托管,这才是标准姿势

Linux下安装Redis其实简单到让人意外,但很多人栽在一个细节上:直接用redis-server &把进程丢后台,完事。这种用法的问题在于,进程和终端会话绑在一起,一旦SSH断开,Redis就可能跟着没了。正确做法是把Redis交给systemd托管,让系统负责拉起和守护。

以CentOS类的系统为例:

# 方式一:包管理器安装 yum install redis # 方式二:源码编译安装(可自定义版本) 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

老一点的系统可能会遇到yum install redis装不上或者报源失效,我之前在低版本系统上就吃过这个亏。解决办法是先换一套可用的软件源,排在里面的阿里云镜像源节点在国内访问速度很好,非阿里云ECS也可以直接用。换源之后再安装,就不会报Error: Failed to download metadata之类的错误了。

装完之后,改Redis配置并创建systemd服务。关键配置项先用一组自己验过的:

# redis.conf 核心配置 bind 0.0.0.0 protected-mode yes port 6379 requirepass your_strong_password daemonize no logfile /var/log/redis/redis.log dir /var/lib/redis maxmemory 2gb maxmemory-policy allkeys-lru appendonly yes appendfsync everysec

这里有个原则很多人不清楚:配置里不要写daemonize yes。因为一旦交给systemd托管,systemd期望前台进程被管理,daemonize yes会让systemd误判服务状态,出现“明明Redis活着,但systemctl status显示失败”的诡异情况。Redis官方很早就明确推荐用supervised systemd+ systemd管理,而不是自己daemonize。

# /etc/systemd/system/redis.service [Unit] Description=Redis Server After=network.target [Service] ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf ExecStop=/usr/local/bin/redis-cli shutdown Restart=always User=redis Group=redis [Install] WantedBy=multi-user.target

写完执行systemctl daemon-reload,然后systemctl enable --now redis。启动后用redis-cli -a 'your_password' ping验证。平时看日志、重启、看状态,都走systemctl,不要手动kill进程。

2.3 用Docker跑主从复制,一只文件帮你搭完整套高可用

本地学习阶段,我强烈建议用Docker跑一个最小化的主从复制环境。好处是干净、可重复、拆了重建只需一条命令,不会把你自己的系统环境搞乱。

先创建个目录放编排文件,内容如下:

# docker-compose.yml version: "3.8" services: redis-master: image: redis:7.2-alpine container_name: redis-master command: redis-server --appendonly yes --requirepass masterpass ports: - "6379:6379" volumes: - ./master-data:/data redis-slave: image: redis:7.2-alpine container_name: redis-slave command: redis-server --slaveof redis-master 6379 --masterauth masterpass --requirepass slavepass ports: - "6380:6379" volumes: - ./slave-data:/data depends_on: - redis-master

在主库里写数据,去从库能看到:

docker exec -it redis-master redis-cli -a masterpass set foo bar docker exec -it redis-slave redis-cli -a slavepass get foo

返回bar,说明主从同步生效了。这里有个细节容易踩坑:Redis 5.0之前配置项叫slaveof,从5.0开始陆续引入replicaof作为替代,到7.x已经全部替换为replicaof。网上大量旧教程还在用slaveof,老版本用着没问题,换到新版Redis就报参数不识别了。我上面用的--slaveof其实是为了兼容说明,新版本建议直接用--replicaof redis-master 6379。

主从复制是Redis高可用的地基,哨兵和Cluster都是在这个机制上扩展的。理解这一层的同步流程,后面排查“主库切了之后从库数据不一致”这类问题会轻松很多。

2.4 可视化客户端怎么选:Redis Desktop Manager还是Another Redis Desktop Manager

命令行再熟练,日常开发调试我还是喜欢配一个可视化客户端,看数据、看TTL、看key分布都直观得多。市面上一堆GUI工具,我用过的几款里,被问得最多的就是这两个:Redis Desktop Manager(RDM)和Another Redis Desktop Manager(Another RDM)。

两者核心区别,我做了一张对照表方便大家选型:

维度Redis Desktop Manager(RDM)Another Redis Desktop Manager
开源情况早期开源,后续商业化开源免费,社区活跃
界面语言英文为主支持中文
重命名key、批量删除支持支持,操作更顺手
连接类型TCP/SSH等常规方式支持TCP/SSH/集群等多种方式
适合人群老牌用户、习惯英文界面中文用户、希望长期免费用的团队

我的个人建议是:如果只是本地连一个Redis,两者随便选,顺手最重要。如果公司要求不能装商业软件,那就直接Another RDM。有一点提醒一下,可视化工具再方便,生产环境排查问题的时候,命令行依然是底线技能。很多工具看一眼“连接不上”“读取超时”就给你一堆红色报错,但实际上有用的信息在redis-cli info输出里。工具能辅助效率,不能替代基本功。

3. 把Redis放到阿里云上:RDS、云服务器与Docker的落地选择

3.1 云上Redis的三种用法,先搞清楚自己该选哪种

你的项目上了阿里云之后,Redis怎么部署,是自动容易忽略但影响很大的问题。主要选项就三个:用云数据库Redis版(RDS)、在ECS上自己装、在ECS上用Docker跑。三者的优缺点和适用场景差得很多,看这张表:

维度云数据库Redis版(RDS)ECS自建ECS+Docker
部署成本有实例费,围绕规格梯次定价只用付ECS费用只用付ECS费用
运维负担云厂商负责主从切换、备份、监控自己装、自己备份、自己处理故障介于两者之间,容器化降低环境差异
高可用能力自带高可用架构,故障自动切换需要自己搭哨兵/Cluster可以用Docker编排,但复杂
定制能力受云厂商控制台限制完全可控可控性高,维护成本中等
弹性扩展可在控制台扩规格,但变更需谨慎手动迁移或加集群节点相对灵活

选型逻辑其实很朴素。没有专职DBA的团队、预算允许的场景,直接用RDS最省心。我自己在业务项目里就常用RDS,因为它的主从切换、自动备份、监控报警这些能力是成熟的,不需要自己半夜爬起来处理故障。反过来说,如果你们对Redis版本有强需求、要改内核参数、或者Redis数据量极大需要靠实例规格之外的定制,那就ECS自建。Docker更适合中等规模、团队有容器化经验、希望快速在测试环境复刻一套Redis的场景。

另外要注意:RDS和自建Redis在参数上有些差别。RDS为了保障托管实例稳定,部分参数是锁定的,比如config set这类动态命令在很多版本里不开放。你在本地玩顺手了config set,上了RDS会发现不好使,这时候别慌,去控制台的参数组里改,改完一般要重启实例才生效。

3.2 RDS实操经验:白名单、参数组、连接数规划

如果你最终选了RDS,有三件套要配好:白名单、参数组、连接池。缺一个,后面都会变成线上事故。

白名单是很多新手第一个坑。第一次创建实例,默认白名单通常只放行内网地址,本地电脑连不上。有人图省事,直接把白名单设成0.0.0.0/0,这等于把数据库裸露在公网上,扫描工具一打一个准。正规做法是在RDS控制台的“白名单”里,只添加你应用所在的ECS内网IP或者安全组ID。如果你本地要调试,临时加一下自己当前公网IP,调完立刻删掉。

参数组里重点关注三个参数。第一个是maxmemory-policy,默认是noeviction,意思是内存满了新写入直接报错,这会导致线上莫名出现OOM command not allowed when used memory > 'maxmemory'。如果你把它改成allkeys-lru,Redis会在内存满时自动淘汰最少使用的key,适合做纯缓存场景。第二个是timeout,空闲连接多久断,建议设一个非零值,比如300秒,避免一堆空闲连接占着不释放。第三个是appendfsync,如果对数据安全要求高,可以设always,但会明显影响写入性能;常规业务用everysec就够了。

连接数规划是个很容易拍脑袋的事。RDS实例规格不同,最大连接数也不同,超出之后会直接拒绝新连接。预估时别只算平均QPS,要看业务峰值的那十分钟。我之前有个项目,活动一开始QPS翻了三倍,连接数瞬间打满,应用侧全部超时,排查到最后就是连接池配置偏小加上RDS规格偏低。那两个数字要匹配:应用层连接池上限加起来,不要超过RDS最大连接数的80%,留点余量给运维排查工具和临时任务。

3.3 工程配套:Maven配置阿里云仓库,顺手接上云SDK

搞定了Redis本身,接着就要在工程里接它。Java后端几乎是标配地会遇到Maven依赖下载慢的问题,尤其是新环境第一次构建,拉几百个jar包能把人急死。解决办法是在Maven的settings.xml里配置阿里云仓库,把中央仓库指向国内节点,下载速度会快非常多。

<mirrors> <mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror> </mirrors>

这个配置写在${MAVEN_HOME}/conf/settings.xml里,对所有工程生效;也可以写在项目内~/.m2/settings.xml。配完之后,Maven下依赖会优先走这个镜像源。如果你还用到了阿里云的短信服务、OSS对象存储、百炼平台的API等等,对应的SDK也是走Maven依赖引入,这时候镜像仓库就更有用了,因为这些SDK及其传递依赖往往有一大堆。

接Redis的话,Spring Boot项目里简单直接:

spring: data: redis: host: r-xxx.redis.aliyuncs.com # RDS实例的内网连接地址 port: 6379 password: ${REDIS_PASSWORD} timeout: 3s lettuce: pool: max-active: 200 max-idle: 50 min-idle: 10

Lettuce是Spring Boot默认的Redis客户端,基于Netty,连接复用做得不错。但注意一个坑:Lettuce在遇到网络异常时,连接状态恢复有时不那么及时,如果线上偶发“Redis command timed out”,除了看网络,还可以考虑换成Jedis。Jedis虽然线程不安全但够简单,配合连接池用,排查心智负担小很多。我自己线上项目两种都用过,小规模应用Jedis反而更直白,Lettuce的优点在连接数极高的场景才能体现出来。

3.4 HTTPS证书续期、网关重启,顺手把Redis连接池检查一遍

热词里有个“阿里云SSL证书免费续期”,这件事本身是云上应用配HTTPS时经常遇到的。我在实际维护中观察到,证书自动续期或手动替换后,Nginx或网关经常会重启,重启的那一瞬间,所有经过网关的请求会重新建立后端连接,其中Redis连接池如果没调好,马上会出现一波连接风暴。

所以我养成了一个习惯:每次做证书续期这类动静较大的变更时,顺手检查三样东西。第一,Redis的连接池最大连接数配置是不是过高,超过了RDS规格上限;第二,连接池的最小空闲连接数是不是维持了太多,空闲连接太多也没意义,浪费资源;第三,代码里有没有每次请求都new一个连接、用完不close,这种写法是泄漏源,时间长了连接数悄悄涨满。

JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(200); config.setMaxIdle(50); config.setMinIdle(5); config.setTestOnBorrow(true); config.setTestOnReturn(true); JedisPool pool = new JedisPool(config, host, port, 3000, password);

setTestOnBorrow(true)会在每次借连接时做一次ping验证,能淘汰坏连接,代价是每次操作多一次网络往返,不过对绝大多数业务可以忽略。这个参数是我调连接池时的底线配置,默认值false虽然性能好一点,但在云环境偶发网络抖动时容易借到坏连接,很坑。

4. 数据类型与序列化:八种核心结构一次讲透

4.1 五种基础类型,到底什么场景用什么结构

很多人背数据类型背得滚瓜烂熟,到真写代码的时候还是全往String里塞。这是没把类型和业务场景建立对应关系。我这里直接摆一张多年使用沉淀下来的对照表:

类型典型业务场景常用命令
String缓存、计数器、分布式ID、验证码、会话tokenSET、GET、INCR、EXPIRE
Hash对象属性存取、购物车、用户资料HSET、HGET、HDEL、HGETALL
List消息队列、最新评论、操作日志LPUSH、RPOP、LRANGE
Set去重、抽奖、好友关注、标签SADD、SISMEMBER、SINTER
ZSet排行榜、延时队列、限流滑动窗口ZADD、ZRANGEBYSCORE、ZREVRANK

举例说,登录验证码用String就够了,SET code:13800138000 123456 EX 300,五分钟后自动失效,不用自己再起个定时任务清理。用户购物车用Hash最合适,一个用户一个key,商品ID是field,数量是value,加减商品就是在hash上做操作,不用把整个购物车序列化成一个JSON再覆盖写回去。

排行榜和延迟队列是ZSet的两大经典玩法。排行榜直接以score存分数,ZREVRANGE取前几名;延迟队列则以时间戳当score,轮询脚本用ZRANGEBYSCORE task:later 0 now把到期任务取出来处理。用ZSet做延迟队列有一个好处:同一个score重复插入不会丢,而且取数据时可以原子地移除,不会出现两个消费者取到同一条任务的问题。

4.2 三种高级类型:Bitmap、HyperLogLog、Geo,用对了都是杀手锏

String之外,还有三种高级结构,它们不是噱头,是专门为了解决特定类型问题设计的。

先讲Bitmap。它本质是String上的位操作,适合存状态开关类的海量数据。我做过的典型场景是“用户签到统计”,一年365天,一个用户一个Bitmap key,第几天签到就把那一位设成1。统计这个用户连续签到多少天,用BITCOUNT配合BITFIELD的GET就能算出来,不需要扫数据库几十万行记录。在线状态也类似,几十万用户在线与否,Bitmap只占几十KB。

再讲HyperLogLog。它的特点是内存占用极小,赢在固定,适合做大规模去重统计,最典型的场景是UV(独立访客)。一天的用户访问量如果做到千万级,存Set会占几百MB,换成HyperLogLog只占12KB左右。代价是统计结果有约0.81%的误差。如果你做的是“大概多少用户进来过”这种量级统计,这个误差完全能接受。

最后是Geo。它基于ZSet实现,专门处理经纬度相关的“附近的人”问题。存入门店坐标:

GEOADD shop:geo 116.397128 39.916527 "北京门店" GEODIST shop:geo "北京门店" "上海门店" km GEORADIUS shop:geo 116.40 39.92 5 km WITHCOORD

这样“查询附近5公里门店”的需求,不用接地图SDK也能先做一版接口出来,点数不多时性能非常好。需要注意的是Geo底层是ZSet,score是经纬度的编码值,不要手动去改这个score,否则坐标就乱了。

4.3 序列化方案怎么选:好看的代码背后,藏着线上事故

Java项目接入Redis时,序列化是最容易埋雷的地方。Spring Data Redis默认用的是JdkSerializationRedisSerializer,直接把对象二进制序列化后丢进Redis。好处是写代码省事,坑也很深:第一,存进去的东西肉眼完全不可读,在Redis客户端里看到一堆\xAC\xED\x00\x05t...,没法排查;第二,一旦某个类字段改了,旧缓存反序列化直接报错;第三,如果项目以后要对接非Java的服务,Jdk序列化数据根本没法读。

我推荐的做法是:key统一用StringRedisSerializer,value用GenericJackson2JsonRedisSerializer或者自带类型信息的序列化器。手动控制格式,存到Redis里是一眼能看懂的JSON,排查问题的时候至少能知道这条数据是什么。

RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); template.afterPropertiesSet();

另一个实际经验是:给不同的业务模块的key加统一前缀,比如order:info:10001、user:profile:13800138000。这么做的好处是,排查线上问题时可以按照前缀批量扫描归类的key,而不是从几百万个杂乱key里大海捞针。还有,用了JSON序列化后,存储空间比Jdk序列化小很多,对内存紧张的Redis实例来说是立竿见影的优化。

5. 分布式锁与缓存治理:生产环境最考验功底的两个方向

5.1 分布式锁的正确打开方式:手写SETNX和Redisson怎么选

分布式锁是Redis使用频率极高的功能,也是网上文章水分最大的话题之一。最原始的实现其实就是一条命令:

SET lock:order:10001 uuid_value NX EX 30

NX保证只有key不存在时才能设置成功,相当于加锁;EX 30是锁的过期时间,防止持有锁的进程挂了之后锁永远不释放。释放锁的时候不能直接DEL,因为可能存在一种情况:线程A的锁已经过期了,线程B拿到了锁,此时线程A的延时任务跑完,一个DEL把B的锁删了。解决办法是删除前先比对value,用Lua保证原子性:

if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end

手写这套方案能应对简单业务,但有个麻烦:锁过期时间设长了,万一持有者真挂了几分钟,其他线程干等;设短了,正常业务还没跑完锁就过期了,其他线程趁虚而入。业务复杂时,我建议直接用Redisson。它内置了“看门狗”机制,默认每10秒检查一次,只要锁持有者还活着就自动续期,彻底解决了“业务没跑完、锁先过期”的死结。Redisson还支持可重入,同一线程可以在持有锁的状态下继续加锁,不会死锁。

RLock lock = redissonClient.getLock("order:" + orderId); if (lock.tryLock(3, 30, TimeUnit.SECONDS)) { try { // 业务逻辑 } finally { lock.unlock(); } }

一句话总结:简单场景手写SETNX+Lua够用,复杂的、长时间的业务锁直接上Redisson,不必自己造轮子。无论如何都要记住,锁的value一定要用唯一ID(比如UUID),绝不能写固定的常量字符串。

5.2 缓存三大问题:穿透、击穿、雪崩,每一类都有对应的解法

线上缓存出问题,九成集中在三个词上:缓存穿透、缓存击穿、缓存雪崩。这三个东西名字像,但成因和解法完全不同。

缓存穿透,指的是“查一个神仙都不存在的key”,缓存和数据库里都没有,于是每次都打到数据库。高并发场景下,攻击者可以伪造一批不存在的ID疯狂请求,数据库直接被拖垮。解决方案有两个层次:第一层是缓存空值,查不到数据库时也把空结果缓存一分钟,再给key额外加个“空标记”;第二层是布隆过滤器,把所有可能存在的ID先放进过滤器,请求来时先用过滤器判断,不存在直接返回。

缓存击穿,说的是“某个热点key正好过期了”,大量请求同一时刻涌去数据库。和穿透的区别是,这个key在数据库里是存在的,只是缓存刚好失效。最简单有效的办法是互斥锁:重建缓存时先拿锁,拿到锁的线程去查库并重建缓存,其他线程先返回旧数据或者短暂等待。也可以用逻辑过期的方法,优点是性能好,缺点是实现起来要处理“旧数据过期但正在重建”的状态,逻辑更复杂。

缓存雪崩,是大量key在同一段时间内集体过期,或者Redis直接宕机。如果是过期时间设置的问题,好办,在基础过期时间上加一个随机值,比如EXPIRE key 3600 + random(0, 600),把这些key的过期时间打散。但真正主因往往是Redis宕机或者网络分区,这种时候缓存全废,流量直接打满数据库。除了依赖云RDS的高可用和主从切换,业务侧还要考虑降级方案,比如本地缓存兜底或者限流。

5.3 缓存一致性:先更新数据库还是先删缓存,别再凭感觉写

缓存治理里最容易被低估的是缓存一致性问题。很多人知道“先更新数据库,再删缓存”,但不清楚为什么要这么做。直接说结论:Cache Aside模式是推荐的,即读的时候先查缓存,没有则查库并回填;写的时候先更新数据库,再删除缓存。

为什么不“先更新缓存再更新数据库”?因为并发环境下会出现不可挽回的旧值。线程A先更新了缓存为“新值”,线程B在数据库更新前读到了这个新值,但线程B因业务失败回滚了数据库,缓存里的“新值”就永远变成假数据了。反过来“先更新DB再删缓存”,只有一种极端竞态:线程A更新DB后,删缓存之前,线程B读到了旧的缓存值,这个窗口期极短,而且等缓存被删后就会回源,数据最终一致。

还要注意更新DB和删缓存这两个动作不是原子事务,删缓存可能失败。所以生产级写法是补一个“延迟双删”:删完缓存后,过几百毫秒再删一次,这样即便写库期间读线程回填了旧缓存,也能被第二次删除清掉。再严谨一点,用MQ异步通知一个消费者去删缓存,删除失败还可以重试。我自己实际项目里用过延迟双删,效果够用,但延迟时间要根据业务耗时来定,务业逻辑重的话几百毫秒不够,需要调高。

6. 日志、慢查询、监控与高频面试题快答

6.1 Redis日志和慢查询到底要怎么看,生产排障必备

线上Redis出问题,第一件事不是猜原因,而是看日志和慢查询。

Redis日志默认在logfile配置指定的路径,我一般把日志级别设成notice,这个级别信息量刚好。平时看日志主要找四类东西:启动失败(配置语法错误、端口占用)、持久化出错(RDB fork失败、AOF写盘失败)、主从同步断连(sync频繁中断)、内存超限告警。

慢查询是定位延迟问题最直接的入口。用命令查询:

# 查最近10条慢查询 slowlog get 10 # 设置阈值为10毫秒,超过的记录 config set slowlog-log-slower-than 10000 config set slowlog-max-len 256

注意slowlog-log-slower-than的单位是微秒,10000等于10毫秒。业务正常的Redis,单命令延迟通常不到1毫秒,一旦出现超过10毫秒的操作,就要重点排查Key是否过大、是否存在阻塞命令(比如KEYS *、SMEMBERS全量返回)、是否触发了AOF重写或RDB保存。线上千万别临时执行KEYS *,大库上这一条命令能把Redis卡住几秒。要扫描key就用官方推荐的SCAN游标遍历。

INFO命令是状态读取宝库。我常态化看三个指标:keyspace_hits和keyspace_misses两个计数器的命中率,低于90%说明缓存价值没发挥出来;connected_clients是否接近maxclients;used_memory和maxmemory的差值。命中率不高的问题比较隐蔽,它不报错但说明很多请求没走缓存,对于热点系统来说是个持续放血的隐患。

6.2 高频Redis面试题和可用的答案方向

面试题这部分我本来不想写,因为网上一搜一大堆,但热词里“redis面试题”搜得确实多,所以整理几个最核心的,附上我认为能过关的回答角度。不用背答案,理解背后的逻辑就够了。

第一个问题:Redis为什么快?四个维度答到位:纯内存操作;IO多路复用,单线程高效处理海量连接;底层数据结构经过优化,比如跳表、压缩列表,对于特定场景性能极好;单线程模型避免了多线程上下文切换和锁竞争。

第二个问题:Redis单线程为什么还能处理高并发?关键在IO多路复用。Redis的网络读写和命令处理都在一个线程里,但通过epoll等机制同时监听大量客户端连接,只有数据就绪的连接才会被处理,避免了阻塞等待。

第三个问题:RDB和AOF怎么选?一句话,RDB是某个时间点的全量快照,恢复快但有丢数据风险;AOF记录每次写操作,数据更安全但文件大、恢复慢。生产环境通常是RDB+AOF混用,RDB做冷备,AOF做精细恢复。

第四个问题:分布式锁怎么实现?先说SET NX EX+Lua防误删,再说Redisson看门狗续期和可重入,再提一嘴主从切换导致锁丢失这种极端情况,能区分出你是有生产经验的人。

第五个问题:Redis集群为什么是16384个槽位?因为槽位数量经过设计,CRC16算法对key计算后对16384取模。槽位太多消息头开销大,太少数据倾斜调整难,16384在可用性和开销之间平衡。实际回答不需要背数字细节,但至少知道槽位是集群分配数据的基础。

第六个问题:内存淘汰策略有哪些?基础八种,按两类记:noeviction是默认,满了直接报错;allkeys-lru全局按LRU淘汰;allkeys-random随机淘汰;volatile-lru只淘汰设置了过期时间的key。对纯缓存场景,allkeys-lru是最常用的。

6.3 线上问题排查速查表:一张表对照着查,省一半时间

下面这些场景都是我在真实环境里遇到过的,整理成表方便快速定位:

现象可能原因排查手段解决方向
连接超时白名单未放行、网络不通、连接数打满检查安全组/白名单;INFO clients看连接数补访问策略;调大连接池
缓存全是垃圾数据key过期时间没设置TTL抽查;看配置文件是否设了默认过期业务代码统一加过期时间
内存涨到60%以上持续不降大量带TTL的key堆积INFO memory、SCAN扫描大key开启淘汰策略;重构缓存key结构
偶发命令延迟突刺大key阻塞、AOF重写、RDB forkslowlog;查看是否在BGSAVE拆分大key;调整持久化策略
重启后数据丢了一部分AOF策略配置不当查appendfsync、恢复文件大小改everysec或always;定期备份
锁偶尔失效主从切换、锁过期时间过短查看锁日志、过期时间设定上Redisson;合理设置过期时间
应用连接数持续增长代码连接泄漏排查是否有未close的Jedis连接使用连接池;规范关闭

这张表是“速查”定位,不是万能药。更深的排查还是要配合慢查询日志和监控数据一起看。但有一个原则我反复讲:线上出问题不要先改配置,先看日志和数据,定位清楚再动手。

最后一章内容到这里正好打住。我整理这套2026版笔记的时候,最深的感受是Redis这类基础组件,看着简单,真正吃透还是靠场景积累。两年前我处理过一个凌晨2点的故障告警,白天排查了半天没头绪,最后发现就是连接池泄漏加过期时间设置不合理两个问题叠加。所以大家看完笔记,一定要自己动手把环境装起来跑一遍,踩一遍坑记得比看十遍文章都牢。如果你照着这套笔记搭好了环境,欢迎在评论区把你在安装、配置、排障中遇到的新问题丢出来,共同补全这个速查表。

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

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

立即咨询