
我在维护一个电商中台的时候遇到过一件挺打脸的事线上 Redis 节点 CPU 突然飙到 90%但监控面板里所有缓存命中率都正常。一群人围着数据扩容讨论了大半天最后发现罪魁祸首是日志文件写到一半磁盘满了AOF 重写卡死主线程也跟着被拖住。更讽刺的是触发这次事故的日志内容正好是开发环境调试用的 debug 级别——测试完忘了改回来。从那次之后我把 Redis 的日志配置当成了和内存淘汰策略一样重要的必修课。这篇内容就围绕 Redis 配置日志这个主题展开把日志级别、日志文件、慢查询日志、AOF 持久化日志、容器和主从环境下的日志处理全部按实操思路串一遍。适合刚接触 Redis 的运维、后端开发也适合那些一直“能用就行”但被线上问题折腾过的老手。1. 先把 Redis 日志的四种类型分清楚不是只有 log 文件很多人一提到 Redis 日志第一反应就是redis.conf里的logfile参数。但实际工作中Redis 的日志体系至少包含四类每一类针对的问题和排查方向都不一样。如果搞混了很容易在排查问题时找错对象。1.1 服务运行日志最直观的“心跳监测”服务运行日志就是 Redis 启动、停止、fork 子进程、执行 BGSAVE 或 BGREWRITEAOF 时打印的那部分内容。它记录的是“进程级”的状态变化而不是每一条命令的执行记录。比如这样24121:M 20 Dec 2024 10:23:01.123 * Background saving started by pid 24135 24121:M 20 Dec 2024 10:23:01.456 * DB saved on disk 24121:M 20 Dec 2024 10:23:01.567 * Background saving terminated with success这段日志通常包含时间戳、进程角色M 是 masterS 是 slaveC 是子进程、日志级别标识.表示 notice*表示 verbose#表示 warning然后是具体事件描述。排查启动失败、持久化异常、主从切换等问题时优先看这部分。1.2 慢查询日志性能问题的放大镜慢查询日志不写文件而是存在 Redis 内存里通过SLOWLOG命令查看。它记录的是执行时间超过阈值的命令用于定位那些“拖垮单线程”的操作。这个和文件日志是两套完全独立的体系配置参数也不同。我见过不少刚入门的朋友在redis.conf里找不到慢查询日志的文件路径其实是因为它根本没有文件。1.3 持久化日志AOF/RDB数据安全的“黑匣子”AOFAppend Only File本身也是一种日志但它的职责是记录数据变更用于宕机恢复。AOF 文件里存储的是 Redis 协议格式的写命令比如*3 $3 SET $3 foo $3 bar这类日志更像数据库的 binlog、redo log它和“系统运行日志”不是一个概念。排查数据丢失、恢复异常时看的是 AOF 文件和相关运行日志排查性能问题时看的则是慢查询日志。两套日志配合使用才能完整还原事故现场。1.4 命令执行日志与调试输出debug 模式下才见真章当loglevel设为debug时Redis 会打印大量内部调试信息包括键空间操作、客户端连接细节等。这个级别在生产环境几乎不可用因为输出量极大。但它也不是完全没有价值——在测试环境复现 bug、观察内部状态变化时debug 日志能提供很多“明面上看不到”的线索。搞清楚这四类日志的区别之后再回头看配置就顺理成章了。很多人配置文件改了一堆其实大部分参数只作用于第一类“服务运行日志”而他们真正想查的慢查询日志走的完全是另一条配置路径。2. 配置文件的“重灾区”logfile、loglevel 和时间戳的取舍Redis 的运行日志配置项主要集中在redis.conf里核心参数有三个loglevel、logfile和logtimestamp不同版本名称可能略有差异。这三个参数看似简单踩坑的人特别多。2.1 loglevel 从 debug 到 warning各自适合什么场景Redis 支持四个日志级别优先级从低到高分别是debug打印大量内部调试信息适合源码级排查不适合日常环境。verbose比 debug 精简但依然很啰嗦会记录大部分关键操作。notice默认级别记录启动、持久化、主从切换等核心事件生产环境推荐。warning只记录报错和严重警告适合磁盘和日志空间非常紧张的场景。我个人的习惯是本地开发用 verbose测试环境用 notice生产环境也用 notice只有在出问题需要深挖时才临时切到 debug排查完立刻改回来。你可能会问为什么生产环境不用 verbose因为 Redis 是单线程模型日志写入本身也会有 I/O 开销级别太高在大流量下会放大延迟。更关键的是日志文件增长会让磁盘报警变得频繁而 verbose 里很多信息在实际排障时根本用不上。配置方式很简单# redis.conf loglevel notice logfile /var/log/redis/redis-server.log2.2 logfile 设为空时日志到底去了哪里这是一个特别容易让人懵的点。logfile参数如果留空Redis 会把日志输出到标准输出stdout。在直接前台运行redis-server的时候日志直接打到终端上这符合直觉。但如果用 systemd 或者 docker 启动标准输出会被服务管理器接管日志就去到了 journald 或者容器控制台你按普通路径去服务器上的/var/log/redis/找文件结果肯定找不到。我在帮别人排查问题时见过很多次这种情况配置文件里logfile是注释状态进程也在正常跑但运维同事一直纠结“日志文件去哪了”。这时候最直接的验证方式不是翻配置而是执行# 查看进程实际使用的配置 redis-cli CONFIG GET logfile redis-cli CONFIG GET loglevel如果返回的 logfile 是空字符串说明日志输出到了标准输出。需要落盘的话在配置里指定明确路径并重启或者用CONFIG SET动态修改。这里有个细节动态修改logfile后Redis 会重新打开日志文件句柄旧文件不会自动关闭牵扯到文件句柄泄漏和日志切割问题后面单独讲。2.3 syslog 配置适合容器化和多实例部署redis.conf里有三行和 syslog 相关的配置syslog-enabled no syslog-ident redis syslog-facility local0把syslog-enabled改成yes之后Redis 会把日志转发给系统 syslog 服务。这个在单机多实例场景下尤其好用——每个实例用不同的syslog-ident做区分日志统一由 syslog 管理配合 logrotate 或 journald 的按大小切割能力可以有效避免单个日志文件无限膨胀。容器场景中如果 Redis 容器内不带日志轮转工具把 stdout 交给容器运行时管理其实比自己在容器里折腾logrotate更省心。3. 修改配置的正确姿势redis.conf、CONFIG SET 与启动参数的优先级修改 Redis 日志配置有好几种途径每种生效时机不同用法不当容易产生“改了等于没改”的错觉。3.1 三种改法各自的生效时机第一种是直接改redis.conf需要重启实例才能生效。第二种是用CONFIG SET在线修改立即生效但不持久化。第三种是启动时通过命令行参数传配置比如redis-server /path/to/redis.conf --loglevel warning --logfile /tmp/redis.log命令行参数的优先级高于配置文件而且只对当前启动实例有效。如果你既要改日志配置又不想丢配置推荐组合打法先用CONFIG SET临时改验证效果后再更新配置文件最后使用CONFIG REWRITE把当前配置写回文件。3.2 CONFIG REWRITE 的自动回写机制CONFIG REWRITE不是把所有配置都重写一遍它只把那些和默认值不同、或者通过CONFIG SET修改过的参数写回到原配置文件中。如果配置文件本身是只读的或者启动时配置文件路径有问题这个命令会返回错误。我自己遇到过一个坑项目组的 CI 流程里每次部署都会执行CONFIG SET maxmemory但从不执行CONFIG REWRITE结果某个实例重启后配置被还原内存上限又回到了默认值。日志配置也一样——如果只是CONFIG SET logfile改了在线生效但配置文件和实际运行状态不一致重启后就会串线。建议把“动态修改 → REWRITE → 确认文件内容”三步作为一个整体操作习惯redis-cli CONFIG SET loglevel notice redis-cli CONFIG SET logfile /var/log/redis/redis.log redis-cli CONFIG REWRITE redis-cli CONFIG GET logfile3.3 不重启让日志实时生效的经验有些生产环境不便于重启 Redis但日志级别确实需要调整此时用CONFIG SET loglevel是最优解。需要注意CONFIG SET loglevel是即时生效的下一行日志就开始按新级别打印不需要任何 reload 操作。此外如果日志文件路径改变CONFIG SET logfile /new/path之后Redis 会立刻打开新文件的句柄但这不意味着旧日志文件可以随便删。因为在 Linux 下旧的日志文件可能还在被进程持有直接rm后磁盘空间不一定立刻释放。正确做法是通知运维做一次日志切割比如logrotate或者等确认没有进程占用后再清理。4. 慢查询日志的完整解读Redis 中最容易误用的“内存日志”慢查询日志是排查 Redis 性能问题时最该先看的资料但它的配置和使用逻辑和传统日志文件完全不同很多人第一眼就犯迷糊。4.1 slowlog-log-slower-than 和 slowlog-max-len 的搭配逻辑两个核心参数先说含义slowlog-log-slower-than执行时间大于该微秒数的命令会被记录默认 10000 微秒10 毫秒设为负数则关闭慢查询日志设为 0 则记录所有命令。slowlog-max-len慢查询日志的条数上限默认 128超出后最老的记录会被挤出。由于日志存在内存里这个值不宜设得过大否则会白白占用内存。这里的单位是微秒不是毫秒。我见过不少人把 10000 直接当毫秒配置然后发现所有命令都被记录日志瞬间被刷满。实际配置建议可以这样定先按默认值跑一段时间观察业务里正常命令的耗时分布再把阈值设为“正常命令耗时的 3 到 5 倍”。比如你业务里大部分 GET/SET 都在 1 毫秒以内阈值可以设为 10000即 10 毫秒专门捕捉那些异常的、非典型的慢操作。# redis.conf slowlog-log-slower-than 10000 slowlog-max-len 2564.2 用命令查看和分析慢查询记录慢查询日志用SLOWLOG命令读取# 查看最近的 10 条慢查询 redis-cli SLOWLOG GET 10 # 查看慢查询总数 redis-cli SLOWLOG LEN # 清空慢查询日志 redis-cli SLOWLOG RESETSLOWLOG GET返回的每条记录包含四个核心字段序号、时间戳、执行耗时微秒、命令参数列表。比如1) 1) (integer) 22 2) (integer) 1734661812 3) (integer) 35678 4) 1) LRANGE 2) user:1024:recent 3) 0 4) -1字段解释第 1 个是唯一 ID第 2 个是 Unix 时间戳第 3 个是执行了 35678 微秒35.678 毫秒第 4 个是命令及参数。注意SLOWLOG GET的数字参数是“最近 N 条”不是“从第 N 条开始”语义上容易弄混。4.3 一个真实案例keys 正则导致的全实例卡顿有一次客户反馈“所有接口偶尔会卡 2 秒”查看监控发现 Redis 的 CPU 并没有 100%但请求耗时呈周期性尖刺。接着用SLOWLOG GET一看排在前面的是几条KEYS user:*命令查询耗时都在 1.5 秒以上。原因很简单Redis 是单线程KEYS命令要遍历整个键空间键数量越大耗时越长。这个命令执行期间其他所有读写命令都会排队等待。定位到问题后我们把代码里的KEYS模式匹配替换成了SCAN遍历同时用SLOWLOG RESET清空日志后续再看慢查询列表那条周期性的尖刺就消失了。以下是定位思路的完整链路供参考步骤 1SLOWLOG LEN看慢查询总量是否异常。步骤 2SLOWLOG GET 20看最近 20 条慢命令是哪些。步骤 3对照业务代码确认这些命令的调用来源。步骤 4用SLOWLOG RESET清空记录观察是否重新出现。这套流程不依赖任何第三方工具在 Redis 客户端里就能完成排查效率比看运行日志高很多。5. 日志文件疯涨与磁盘报警清理和轮转的正确姿势Redis 日志文件如果不做轮转早晚会变成一颗磁盘炸弹。AOF 文件的增长、运行日志文件的增长需要分开处理混在一起谈很容易顾此失彼。5.1 AOF 重写与日志文件增长的关联AOF 文件只会追加写入不做轮转和清理的话文件体积会随着写操作持续膨胀。Redis 提供了BGREWRITEAOF命令来重写 AOF 文件压缩掉中间冗余命令。但很多人会忽略一个问题AOF 重写期间会 fork 子进程内存占用会短暂上升而且重写完成后旧的 AOF 文件还会保留一段时间此时磁盘占用是两份文件叠加的状态。日志文件关联的另一面体现在AOF 重写失败时运行日志里会出现类似Background AOF rewrite terminated with error的记录。排查磁盘问题时要同时看 AOF 文件大小和运行日志的报错内容才能判断是磁盘碎片问题、权限问题还是内存 fork 失败。5.2 用 logrotate 管理运行日志的完整配置对 Linux 环境我推荐用系统自带的logrotate来管理 Redis 运行日志。在/etc/logrotate.d/redis下创建一个配置文件内容可以参考/var/log/redis/*.log { daily rotate 7 compress delaycompress missingok notifempty create 640 redis redis sharedscripts postrotate # 让 Redis 重新打开日志文件句柄 redis-cli CONFIG SET logfile /var/log/redis/redis-server.log /dev/null 21 || true endscript }关键点是postrotate里的操作日志被轮转后Redis 进程可能还持有旧的文件句柄如果不重新CONFIG SET logfile让它打开新文件后续日志会继续写入已经被重命名的旧文件导致“日志不写新文件”的怪现象。这个和 MySQL、Nginx 的日志轮转处理方式不完全一样因为 Redis 没有内建的 reopen 信号需要通过重新设置配置来刷新句柄。5.3 日志文件句柄问题删了还占磁盘磁盘满了想都不想就去rm -rf日志文件这是新手最容易踩的坑。在 Linux 里文件被删除不代表磁盘空间释放——只要还有进程持有文件句柄这个文件占用的 inode 和磁盘块就不会归还。验证方法# 查看 Redis 进程打开的文件句柄 lsof -p $(pgrep redis-server) | grep log如果日志文件显示为 deleted说明进程还握着已删文件的句柄。正确做法是通知 Redis 重新打开日志文件比如CONFIG SET logfile先改到别处再改回来或者执行logrotate的 reopen 动作然后lsof确认句柄已切换到新文件这时磁盘空间才真正释放。这个细节在很多日志切割方案里不会被提及但生产环境一旦遇到就能节省半小时以上的排查时间。6. Docker、主从复制和不同操作系统下的日志配置差异同一个 Redis 配置日志在 Docker、主从复制、macOS/Windows 下表现差异比想象中大。6.1 Docker 中 Redis 容器的日志处理用 Docker 跑 Redis日志配置的核心思路不一样了。容器里如果不指定logfile日志默认输出到 stdout由 Docker 的 json-file 驱动收集。这个方案省去了容器内日志切割的麻烦docker logs redis-server就能查看。问题在于如果容器内显式配置了logfile /var/log/redis/redis.log日志写到了容器内部文件docker logs就看不到内容了只能docker exec进容器里tail。此时如果没有给容器挂载卷容器一旦删除日志全部丢失。所以容器场景我建议把日志留在 stdout不要改到容器内文件路径除非有专门的采集需求比如挂载 volume 给外部日志系统用。另外要注意主从复制模式用 Docker Compose 部署时每个容器镜像里可能都带着一份默认配置修改前先确认宿主机挂载的配置文件和容器内实际读取的路径是否一致。否则你改了宿主机上的redis.conf容器里跑的却还是镜像自带配置日志行为和预期完全对不上。6.2 主从复制场景下日志配置要点主从复制环境中主库和从库的loglevel建议保持一致的 notice。否则从库的日志级别是 debug从库磁盘可能先吃紧。更关键的场景是主从切换晋升为主库的从库如果日志路径配置指向的还是本地文件交接后所有日志都在新主库上继续追加而旧主库已经降级两边日志会出现断裂。排障时需要对比主从双方的日志时间线尤其要关注全量同步和部分同步的日志记录# 从库日志里的常见同步信息 # Full resync requested by slave # Partial resynchronization accepted遇到主从数据不一致优先翻从库日志和主库日志的同步事件部分而不是直接去对比数据内容。日志的先后顺序能直接告诉你同步断点发生在哪个时间点。6.3 macOS/Windows 下安装后的日志路径差异macOS 上用brew install redis安装的话配置文件通常在/usr/local/etc/redis.confApple Silicon 下是/opt/homebrew/etc/redis.conf默认日志走 stdout。如果用brew services start redis方式启动服务日志会被 brew 托管到对应路径下通常和启动脚本的 stdout 重定向有关可以用brew services info redis查看状态路径再去对应位置找日志。Windows 下Redis 官方没有原生支持常用的是微软移植版或由 Memurai 等替代方案。这些版本的日志配置项大同小异但路径分隔符、默认安装目录差异较大直接照搬 Linux 的配置会踩到路径不存在的坑。无论哪个平台第一步先确认配置文件实际路径第二步用redis-cli CONFIG GET logfile确认当前生效的日志路径比凭经验去猜快得多。个人在实践中的体会是Redis 日志配置的关键不在于“配置项多复杂”而在于你能不能在几类日志之间快速切换视角。运行日志看的是进程健康慢查询日志看的是命令质量AOF 日志看的是数据安全。每次线上出问题我都会先花一分钟检查这三个视角而不是一头扎进业务代码里。作为收尾再分享一个实用小技巧在维护脚本里留一条定时任务每隔几分钟把SLOWLOG LEN和日志文件当前大小输出到一个独立指标项配合监控告警可以在事故发生的萌芽阶段就把问题接住远比等到磁盘快满了再手工排查靠谱得多。