
刚帮两个同事排查完Ubuntu上Redis的故障一个是阿里云ECS一个是自己电脑上的VMware虚拟机。问题一个比一个典型源码编译make报错日志里什么都没有服务就停了远程死活连不上重启机器Redis直接“失踪”。单独搜这些问题答案散落在各个帖子每次都要重新拼一遍。这篇我把这些年踩过的坑完整梳理出来——不针对单个报错而是沿着Ubuntu上Redis从安装、启动、连接、运行到维护这条完整链路把最常见的故障节点逐个拆开给出可复现的排查方法和解决步骤。核心关键词就三个Ubuntu、Redis、常见故障及解决方法。适合刚接触Redis部署的人也适合那些已经跑起来但隔三差五出点小毛病的运维朋友。1. 安装阶段make失败和装完缺胳膊少腿1.1 make报错多半不是Redis的锅很多人一搜“redis安装教程”上来就让你源码编译然后就在make这一步卡住。报错五花八门test.c:1:10: fatal error: stdio.h: No such file or directory或者zmalloc.h:50:31: fatal error: jemalloc/jemalloc.h: No such file or directory第一反应是Redis源码有问题其实不是。Ubuntu最小化安装或云镜像不带完整编译工具链stdio.h找不到代表连基础C库头文件都没有。解决方法很简单先把基础依赖补齐sudo apt update sudo apt install -y build-essential pkg-configbuild-essential包含了gcc、g、make这些核心工具。装完再执行make大部分情况能过。jemalloc那个报错常见于某些旧版本Redis或特定架构下可以绕开make MALLOClibc用libc自带的内存分配器替代jemalloc。性能上差异在日常场景基本感知不到Redis在benchmark里用jemalloc略好但开发环境、内部系统完全够用。官方文档也允许这个参数不必有心理负担。1.2 make成功但redis-server不存在的目录乌龙还有个高频问题make成功了输入redis-server却提示command not found。原因多数是源码目录下它真的存在只是你没在PATH里./src/redis-server或者你make install没执行。make install才是把二进制复制到/usr/local/bin这一步经常被人漏掉。如果执行了make install还找不到命令检查/usr/local/bin是否在PATH里或者干脆用绝对路径。我遇到过更离谱的有人下载了源码包解压后直接在deps/hiredis子目录里乱敲make然后说“Redis编译不过”。Redis的源码必须从根目录执行make不要进子目录。这也算经验教训遇到安装问题先确认你站在哪个目录。1.3 用apt装还是源码装想清楚再选我个人的建议是分场景场景推荐方式理由生产/正式环境apt或官方二进制包便于升级、依赖干净需要特殊编译参数源码编译可自定义jemalloc、debug等临时测试学习apt一分钟搞定apt安装简单但Ubuntu仓库里的Redis版本通常偏保守2024年时主流版本在6.0.x左右。如果你需要新版本特性比如Redis 7的functions、acl改进就得走源码或官方redis.io提供的二进制。源码编译不是洪水猛兽装好build-essential之后也就是三分钟的事。2. 服务起不来前台正常、后台秒挂的诡异现象2.1 明明启动成功进程却消失了这个故障特别迷惑人在终端执行redis-server一堆启动日志打出来显示Ready to accept connections。然后你关掉终端或者用redis-server 放到后台过一会儿进程就没了。根本原因写在Redis自己的日志里。默认配置下Redis以非守护进程方式运行前台模式下CtrlC或者关闭终端SIGINT/SIGTERM直接把进程带走。你用放后台终端一关会话结束后台任务也跟着收尸。这不是Redis独有是所有Unix进程的会话机制问题。正确做法有三个# 方式一改配置 sudo vim /etc/redis/redis.conf # daemonize yes # Redis 7.x之后改为 supervised后面讲 # 方式二启动时指定 redis-server --daemonize yes # 方式三最推荐用systemd sudo systemctl start redis2.2 supervised参数和systemd单元文件的坑Redis 6.0默认配置里daemonize已经不是主流推荐方式官方更希望你在systemd环境里用supervised指令supervised systemd daemonize no配置了supervised systemd之后Redis会通知systemd自己已经就绪并让systemd管理进程生命周期。这样systemctl start redis和systemctl enable redis能够正常工作。但是很多人在Ubuntu上遇到systemd服务起来后立刻失败的情况。用journalctl -u redis一看报错往往是Cant open the log file: Permission denied或者# Warning: /var/lib/redis is not writable这是目录权限问题。apt安装的Redis会自动创建redis用户和/var/lib/redis目录但源码编译后你手动创建目录可能忘了chownsudo mkdir -p /var/lib/redis sudo chown redis:redis /var/lib/redis sudo mkdir -p /var/log/redis sudo chown redis:redis /var/log/redis如果不想自己写systemd单元文件优先用apt安装它全给你配好了。源码装的话官方已经提供utils/systemd-redis_server.service之类的模板照着改路径即可。2.3 日志没写对地方等于没日志不少“服务突然挂掉”的排查卡在根本没日志。默认loglevel是 notice输出到stdout一旦你设置了daemonize yesstdout被丢弃日志去哪了变成空。建议把日志固定到文件调试期直接调到debug或verboseloglevel notice logfile /var/log/redis/redis-server.log注意目录要让运行用户可写。很多老手一上来就tail -f /var/log/redis-server.log结果文件不存在——因为Redis压根没创建。用-作为logfile会把日志打到stdout这个符号容易被忽略。提示排查任何Redis故障第一步永远是从日志开始。日志不落盘的话先修复日志配置再谈排查。3. 连接被拒本机能连、远程拒绝的联排问题3.1 bind 127.0.0.1、protected-mode和密码三座山从其他机器连不上Redis是最经典的“常见故障”。绝大多数人卡在这几处bind 127.0.0.1 protected-mode yes # requirepass foobar这三项是一套组合锁。bind 127.0.0.1表示只监听本机回环地址外部网卡一律不服务。protected-mode yes是个安全兜底如果你没设置密码又没显式bind到内网地址Redis只允许本机连接。requirepass没设置等于大门敞开但被保护模式挡住。修改方法bind 0.0.0.0 protected-mode no requirepass your-strong-passwordbind 0.0.0.0监听所有网卡。生产环境建议精确bind到内网IP不要全开。每次改完配置要重启Redis才能生效sudo systemctl restart redis3.2 改了配置却不生效你可能启动了一个“裸”Redis这里有个巨坑我见过好几次配置文件改了bind和requirepasssystemctl restart redis从远程用Redis Desktop Manager连接还是超时或被拒绝。于是开始怀疑防火墙查了半天没结果。最后一看进程ps aux | grep redis-server输出redis-server *:6379注意这个*:6379意味着Redis没有加载你的配置文件。运行的是默认配置实例。systemd服务配置的ExecStart里写的是redis-server /etc/redis/redis.conf但你可能曾经手动跑过redis-server它占用了6379端口systemd服务起了但端口被占你连的其实是那个没有密码、没有配置的裸实例。排查方法redis-cli -p 6379 info server | grep config_file如果显示config_file:为空说明当前运行的进程确实没加载任何配置文件。杀掉它再用正确方式启动。3.3 Ubuntu防火墙和云平台安全组的叠加效应Ubuntu默认没开ufw防火墙但很多人会开。检查一下sudo ufw status如果状态active需要放行6379端口sudo ufw allow 6379/tcp但更隐蔽的是云服务器的安全组。阿里云、腾讯云这些平台安全组规则没放行6379即使服务器内部的ufw全开也没用。请求在云网络层就被拦了。这个排查顺序建议先看云控制台安全组再看服务器ufw最后看Redis自身bind和protected-mode。按照这个顺序走99%的“连不上”问题十分钟内能定位。4. 运行期故障内存、持久化和卡顿排查4.1 设置了maxmemoryRedis还是被系统OOM杀掉这属于“看似配置了却依然出事”的典型。很多人设置了maxmemory 512mb以为内存封顶512MB结果过几天Redis进程直接消失dmesg一看OOM killer把redis杀了。原因在于maxmemory控制的是Redis自身数据占用的内存不包括进程开销、碎片和复制缓冲区。比如主从复制场景下master需要为每个slave维护输出缓冲区还有客户端输入缓冲区当有慢消费客户端时这边数据堆积起来也能吃掉几百MB。这些内存统统不受maxmemory约束。所以512MB的maxmemory配合高写入流量总内存轻松涨到1GB以上触发系统OOM。解决方案分两层设置系统层面overcommit降低被杀概率sudo sysctl vm.overcommit_memory1写入/etc/sysctl.conf持久化。Redis官网也建议这个值设为1因为RDB持久化时fork子进程需要写时复制内存overcommit_memory0时可能fork失败。给保留内存余量。maxmemory建议设置为物理内存的50%-60%同时监控RSSredis-cli info memory | grep used_memory_rssused_memory_rss才是实际占用的物理内存如果它逼近物理内存上限说明maxmemory设置得太高需要降。一般让used_memory_rss保持在物理内存的70%以下比较稳。4.2 磁盘空间没满RDB持久化却一直失败另一个常见故障日志里频繁出现Background saving terminated by signal 2或者Failed opening the RDB file temp-xxx.rdb (in directory ...) for saving: No space left on device但是df -h一看磁盘还有几十GB空闲。这就有意思了。问题通常出在磁盘.inode耗尽。Ubuntu的临时目录或Redis数据目录所在分区inode用完了。查看方法df -i /var/lib/redis如果IUse%到100%即使有剩余空间也无法创建新文件。清理一下临时文件或者把Redis的dir换到inode充足的分区。还有一种情况Redis配置的dir目录不存在。比如你手动指定dir /data/redis但/data/redis从来没创建过Redis启动时不会报错它只在持久化时才去创建临时文件到了持久化周期才疯狂报错。提前确认mkdir -p /data/redis chown redis:redis /data/redis4.3 慢查询和延迟排查不是所有卡顿都是慢命令线上Redis偶发卡顿客户端报超时比如热词里那个redis command timed out; nested exception is io.lettuce.core.rediscommandtim...英文都给你截断了。用慢日志看redis-cli slowlog get 50显示耗时最长的命令。但这里有个关键点slowlog只记录命令执行时间不包括网络排队和客户端等待。如果slowlog里没有明显慢命令客户端却感觉卡顿问题可能在别处big key的序列化传输。一个几MB的stringGET命令本身可能只花几毫秒但网络传输占满带宽拖垮整个客户端连接。用redis-cli --bigkeys扫描。fork造成的停顿。RDB持久化触发fork时如果内存占用大fork会阻塞主进程几毫秒到几百毫秒。日志里能看到fork耗时。swap。如果Redis内存被交换到磁盘任何操作都可能卡顿。检查cat /proc/$(pgrep redis-server)/status | grep SwapSwap数值大说明内存不够用了加内存或者降maxmemory。排查卡顿的正确思路先slowlog排除慢命令再检查big key再确认swap和fork最后看系统网络。一步一步来不要一卡就往“Redis性能不行”上想。4.4 数据结构误用Redis变慢的隐性杀手有些故障不报错只是越来越慢。常见案例把大集合进行O(n)操作或者一个list存了上百万字符执行LRANGE全量拉取。这些命令执行期间会阻塞Redis单线程其他请求全部排队。排查技巧redis-cli --bigkeys它会统计各类型key的max size。另一个命令看运行时状态redis-cli info stats | grep instantaneous配合客户端超时时间可以判断阻塞持续周期。还有一个容易忽略的过期key的惰性删除。大量key在同一秒过期Redis会在主线程尝试集中清理造成瞬间卡顿。解决办法是过期时间加随机抖动expire_time base_ttl random.randint(0, 300)Redis 6.0还提供了lazyfree-lazy-expire yes把过期key回收放到后台线程能在一定程度上缓解。5. 运维工具和日常维护中的高频坑5.1 redis-cli中文乱码和--raw参数用redis-cli查一个value是中文的key返回结果是\xe6\xb5\x8b\xe8\xaf\x95这种转义序列初学者以为数据存坏了。其实Redis返回的是二进制安全内容redis-cli默认按转义字符串显示。加--rawredis-cli --raw get test-key就能看到原始中文。如果是批量导入导出这个参数也常用。redis-cli --raw配合管道处理数据是Shell脚本操作Redis的必备姿势。5.2 可视化客户端连接失败的隐蔽原因热词里出现了好几个Redis可视化工具。Another Redis Desktop Manager、Redis Desktop Manager连接不上除了前面说的bind、密码、防火墙问题还有两类隐蔽原因第一类是SSH隧道配置。很多云服务器只开了22端口用户通过SSH隧道连Redis。工具里配置SSH Tunnel时填了SSH密码却忘了工具连接的是隧道的local port或者误把SSH的host填成数据库host导致连接失败。这类问题从命令行验证最直接ssh -L 6379:localhost:6379 userserver然后本地连接127.0.0.1:6379能连上说明工具配置问题连不上说明Redis本身问题。第二类是版本兼容性。一些旧版可视化工具对Redis 6的ACL和新密码机制支持不好。Redis配置了ACL用户但工具只支持requirepass方式连接。碰到这类情况升级工具版本或者临时用默认用户测试。5.3 Ubuntu重启后Redis没自动启动的真相这个问题在“ubuntu系统重装”和“ubuntu的html编辑器”这些热搜旁边看着不起眼但实际很常见。很多人配置了Redis也用了systemd但重启后Redis不见了。检查sudo systemctl is-enabled redis如果输出disabled设置开机自启sudo systemctl enable redis还要注意源码编译安装的人自己写的systemd服务文件可能因为路径不对导致enable失败。而且Ubuntu某些云镜像默认/tmp是tmpfs如果你把Redis的dir或logfile放在/tmp下重启后数据全部消失。Redis的数据目录、日志目录绝不要放/tmp。另外用docker安装redis主从这类方案热搜词里有重启后容器不自动启动需要设置restart策略docker run -d --restart unless-stopped --name redis -p 6379:6379 redis:7unless-stopped在宿主机重启后会自动拉起容器除非你手动stop过它。6. 进阶场景Docker、主从和分布式锁背后的隐患6.1 Docker跑Redis目录权限和网络模式别踩雷Docker安装Redis看似简单实际坑不少。最常见的是数据卷权限问题docker run -d -v /data/redis:/data redis:7host上的/data/redis权限不对容器内redis用户无法写入持久化直接失败。解决mkdir -p /data/redis chown 999:999 /data/redisRedis官方镜像里redis用户UID是999。这一步不做好日志里会出现“Cant open the appendonly dir: Permission denied”。网络模式也要注意。很多人用-p 6379:6379映射端口然后从外部连接失败。如果Redis配置文件里设置了bind 127.0.0.1容器内的Redis只监听自己的回环地址外部流量进不来。Docker映射端口时Redis配置需要监听0.0.0.0。也就是容器内要能接受外部连接这和你本地跑Redis的逻辑不同容易方向搞反。6.2 主从复制延迟和脑裂场景Docker安装redis主从玩的是docker run -d --name redis-slave redis:7 redis-server --slaveof master-ip 6379看似起来了但slave同步滞后或者发生切换后数据不一致。这里要分清复制延迟和主从切换两个维度。复制延迟排查redis-cli info replication关注master_repl_offsetmaster写入偏移量和slave_repl_offsetslave复制偏移量两者差距大说明网络带宽不足或同步命令太大。第二种常见原因是slave开启了AOF且appendfsync everysec磁盘写速跟不上。脑裂问题更隐蔽主从架构里master由于网络分区被隔离客户端还在向它写入新选举的slave变主分区恢复后旧主恢复但它的数据和新主不一致。Redis Sentinel本身不会解决数据冲突它只保证可用性。要缓解这个问题需要设置最小写入节点数min-replicas-to-write 1 min-replicas-max-lag 10这意味着master至少要有1个健康从节点才接受写入。分区后master失去所有从节点拒绝写入避免脑裂期间产生新数据。当然牺牲了一点可用性换一致性业务要能接受。6.3 Redis分布式锁在故障下会怎么失效热搜词里有“redis分布式锁”这话题跟故障排查强相关。很多人用SETNX实现分布式锁时间到了自动过期。常见故障场景SET lock_key owner_token NX PX 30000如果锁的持有者处理时间超过30秒锁过期另一个线程拿到锁两个线程同时执行临界区。这不算Redis故障是业务设计问题。更隐蔽的是主从切换导致锁丢失线程A在master上拿到锁master还没同步到slave就宕机了slave提升为master线程B在旧slave新master上拿到同一个key的锁两个线程都认为自己持有锁。唯一可靠的规避方案是Redlock算法或者用Etcd、ZooKeeper这类强一致组件做分布式锁。Redis官方对Redlock的适用场景本身有争议简单理解Redis分布式锁适合能容忍极小概率失效的业务不能容忍的双写场景不要用它。另外有个实操建议value里存一个唯一随机token释放锁时用Lua脚本校验token再删除避免误删别人的锁if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end这是很多人忽略的细节不写token的分布式锁在延迟抖动的环境下很容易出事故。排查Redis故障这么多年最大的体会是Redis自身代码带来的故障概率极低绝大多数问题出在部署方式、配置细节和外部环境这三层。安装阶段先把依赖和目录搞清楚运行阶段先看日志再看指标连接问题按防火墙、Redis监听、客户端的顺序排查。只要不凭感觉瞎改配置按链路一步步验证大部分问题都能在半小时内定位。我建议每台服务器上都预先写一个checklist日志路径、配置文件路径、systemd状态、端口监听情况、内存RSS、磁盘inode。故障来的时候照着查一遍比临时百度高效太多。