
我刚接手一个项目时遇到过一次线上Redis连接数被打满的问题。从监控上看连接数一路飙升直到maxclients上限紧接着一堆业务告警涌入。第一反应是有人写了个连接泄漏的代码结果查了半天最后发现问题出在配置文件上——新加的Redis实例是从一台旧机器直接拷贝过来的timeout 0表示空闲连接永不断开连接池里一堆半开连接就堆在那里。那次之后我意识到Redis的日常运维问题里十有八九最后都能在配置文件里找到根因。这篇文章就围绕redis.conf展开启动时Redis到底怎么加载配置哪些参数在真实场景中会直接决定线上行为安全、内存、持久化、主从复制这些模块里哪些配置项最容易踩坑以及我目前比较顺手的一组基础模板。适合刚接触Redis的后端同学也适合那些明明能跑起来却总出莫名其妙问题的运维同学。1. redis.conf 的加载机制改完文件为什么没生效先说最基础但也最容易被忽略的一件事Redis的配置文件到底在什么时机、以什么方式被读取。1.1 启动时配置加载的优先级启动Redis时redis-server可以带一个配置文件路径也可以不带。不带配置文件时Redis会使用编译期内置的默认参数启动并不会自动去某个固定路径找redis.conf。我第一次用源码编译安装Redis时也犯过这个错——以为把redis.conf放在和redis-server同一个目录里就能被自动读取结果启动后CONFIG GET save一看和默认值完全一样配置文件压根没被加载。正确的启动方式是这样redis-server /etc/redis/redis.conf如果你用apt install redis-server安装配置文件默认放在/etc/redis/redis.conf并且init脚本里已经帮你指定了路径。用Docker跑的话一般通过挂载方式把配置带进去docker run -d --name redis \ -v /myredis/redis.conf:/etc/redis/redis.conf \ redis:7.0 redis-server /etc/redis/redis.conf优先级层面还有一层隐藏的覆盖关系命令行参数 配置文件 内置默认值。也就是说就算配置文件里写死了端口6379你启动时加了--port 6380Redis会以6380为准。这个特性在临时验证配置、或者在配置里没有显式写某个参数时非常有用。1.2 运行期修改配置CONFIG SET 与 CONFIG REWRITERedis支持动态修改配置不需要重启127.0.0.1:6379 CONFIG SET maxmemory 1gb这里有个经典陷阱CONFIG SET只修改内存中的运行时配置不会自动写回redis.conf。如果你觉得改完了就万事大吉下次重启所有运行期修改全部还原。早年间我就在生产环境用CONFIG SET maxmemory-policy allkeys-lru调过淘汰策略当时确实生效了结果一个月后机房断电重启Redis内存涨到物理上限导致OOM才想起当时的修改根本没落盘。解决方案是改完后执行一次127.0.0.1:6379 CONFIG REWRITECONFIG REWRITE会把当前运行配置中与redis.conf不一致的部分重写回配置文件里。它能保留原来的注释和结构只更新实际变更的项对习惯手动管理配置的人来说比较友好。1.3 配置文件的语法约定redis.conf的语法很直白一行一条配置格式是参数名 参数值例如maxmemory 256mb maxmemory-policy allkeys-lru#开头是注释。空白行会被忽略。参数名大小写其实不敏感但业界惯例统一小写。参数值里如果包含空格需要用双引号包起来比如requirepass的密码里若带特殊字符。布尔值用yes/no不要用true/false。字节类的值可以带单位1kb、256mb、1gb大小写不敏感。还有一个常用指令是include可以在一个配置文件里引入其他文件常用于按环境拆分配置include /etc/redis/redis-local.confinclude出现的位置是有讲究的Redis是按文件顺序逐行解析include放在文件前面后面的配置会覆盖被引入文件里的同名参数放在文件末尾则被引入文件末尾的参数会覆盖前面的。这个细节在拆分公共配置环境差异化配置时很容易搞混。2. bind、protected-mode 与 requirepass网络与安全配置的三个关键缺一个都会出事Redis默认配置是安全的但是安全的方式有点绕。很多刚接触Redis的人配置完成后发现本机能连、远程连不上或者反过来配完之后公网裸奔被扫描爆破根子都在下面这三个参数上。2.1 bind 到底该怎么填bind指定Redis监听哪些网卡地址默认配置通常是bind 127.0.0.1 -::1意思是只监听本机回环地址只允许本机连接。如果你想允许局域网内其他机器访问需要把内网IP加进去bind 127.0.0.1 192.168.1.100注意一个容易犯的错只写了内网IP、没带127.0.0.1结果本机用redis-cli连接时连不上因为本机发起连接的目标地址是127.0.0.1而Redis只监听在192.168.1.100上。通常建议把回环地址保留上。bind 0.0.0.0表示监听所有网卡。生产环境除非有明确的网络隔离策略否则不建议这么干。公网上大量针对Redis的扫描爆破扫的就是裸奔的6379端口。2.2 protected-mode 的防呆逻辑Redis 3.2之后引入了protected-mode它的工作逻辑比较特别如果protected-mode yes且没有显式配置bind并且没有设置requirepass密码那么Redis只接受本机连接。一旦你显式设置了bind比如绑定到内网IP但没有设置密码且protected-mode yesRedis会拒绝来自非本机的连接。如果显式设置了requirepass那么受保护模式对密码覆盖的访问就放行了。这个机制很多老运维会理解为我bind了内网IPprotected-mode就自动关闭了实际情况是——protected-mode仍然是yes只是这时候从非本机来源进来的连接如果没有密码直接报DENIED Redis is running in protected mode错误。这个报错我在排查跨机器连接问题时见过很多次。想彻底放行内网访问又不设密码得显式设置protected-mode no但这句话等于在裸奔。我的建议是如果不设密码protected-mode永远保持默认yes绑定的IP慎重控制如果要对外开放就老老实实打开requirepass同时配合protected-mode yes使用。2.3 requirepass 与 masterauth主从复制绕不开的密码设置设置访问密码的配置项是requirepass your-strong-password设置后所有客户端连接都必须执行AUTH your-strong-password。这个密码同样会影响主从复制如果主库设置了requirepass从库连接主库时也需要认证此时从库要配置masterauth your-strong-passwordmasterauth和requirepass是两个独立配置项。requirepass是本实例对外提供服务的认证密码masterauth是本实例作为从库时连接上游主库的密码。很多新手只设置了主库的requirepass忘了给从库配masterauth结果主从一开始复制正常一旦主从连接断开之后重新建立连接从库会一直报MASTER auth failed数据停止同步。这种问题在重启主库后尤其常见因为断开重连的动作会频繁触发。如果主从多次切换比如哨兵模式下主库变成了原来的从库密码配置就会变得更加微妙。保险的做法是集群里所有Redis实例的requirepass和masterauth设置为同一个值这样无论谁变成主库复制链路都能正常认证。2.4 ACLrequirepass 的进阶替代Redis 6.0之后支持ACL可以针对不同用户设置不同的命令权限和键空间权限。比如只允许某个用户执行只读命令user readonly on readonly-password ~* readonly如果只是想快速搭建、控制复杂度requirepass仍然是够用的。ACL更适合多团队共享一个Redis实例、需要做权限隔离的场景但配置文件管理成本更高一旦配错排查起来也更费劲。3. maxmemory 与 maxmemory-policy内存参数配置不当Redis 可能会被系统杀掉Redis的内存管理配置是所有配置项里最容易凭感觉乱填的领域。之前遇到过一个实例maxmemory设成物理内存的100%想着反正机器有32GRedis最多也就全占了结果内存到20多G时系统开始使用swapRedis响应从微秒级飙到秒级最后进程被内核OOM Killer直接带走。3.1 maxmemory设置成多少才合适maxmemory默认是0表示不限制内存使用。问题在于不限制不代表不会出问题——Redis内存膨胀到物理内存上限之后会动用swap性能断崖式下跌最终被系统OOM Kill。Redis自己的报错是OOM command not allowed when used memory maxmemory但系统层面的OOM Kill往往比这个来得更早、更难排查。配置建议如果这台机器上只有Redis一个主要进程maxmemory设置为物理内存的60%-70%左右给系统和其他进程留出余量。比如16G内存的机器设置maxmemory 10gb如果机器上还跑着其他Java应用要根据实际内存占用情况调低。这里没有绝对公式原则是给内核和操作系统留够至少20%的缓冲避免Redis自身满了之后引发swap风暴。3.2 maxmemory-policy淘汰策略怎么选内存达到maxmemory上限后Redis根据maxmemory-policy决定行为。默认策略是noeviction即不再接受写入请求直接报错但不影响读操作。对于数据库以外的纯缓存场景这个默认值几乎是必然会出问题的——缓存写不进去时业务会大量报错。常用策略对比直接看这张表策略作用范围淘汰目标适用场景noeviction无不淘汰写请求直接报错用于数据完整性要求高的场景allkeys-lru所有键淘汰最久未使用的键通用缓存场景最常用volatile-lru设置了过期时间的键淘汰最久未使用的键希望未设置过期时间的键永不淘汰allkeys-lfu所有键淘汰访问频率最低的键访问频次差异明显的场景volatile-lfu设置了过期时间的键淘汰访问频率最低的键同上但保护无过期时间的键volatile-ttl设置了过期时间的键优先淘汰剩余TTL最短的键希望快过期的先被淘汰allkeys-random所有键随机淘汰冷热不明显的场景volatile-random设置了过期时间的键随机淘汰冷热不明显的场景多数通用缓存场景我直接用maxmemory-policy allkeys-lru因为大多数业务场景下缓存数据允许被淘汰而且LRU策略对近期访问过、未来可能还会访问的预测在多数读多写少的场景下表现不错。但有一个反直觉的场景需要留意如果你用Redis存了分布式锁比如一个锁的key设置了过期时间但业务锁生命周期较长恰好触发了volatile-lru淘汰锁会提前消失。使用allkeys-lru时同样的锁key也可能因为内存压力被淘汰掉。所以在分布式锁场景内存配置要留足余量让淘汰概率尽量低。3.3 maxmemory-samples采样数对淘汰精度的影响Redis的LRU和LFU不是精确算法而是近似采样默认从所有键里随机取5个淘汰其中最久未使用的一个。这个采样值就是maxmemory-samples 5增大采样数会让淘汰结果更接近精确LRU但换来的是每次淘汰时更多的内存扫描开销。对于大部分业务5已经够用。如果Redis里键数量巨大且淘汰频繁可以提高到10试试CPU开销会略有提升一般不影响业务。4. RDB 与 AOF 持久化配置数据安全与性能之间的取舍持久化配置决定了Redis宕机后能找回多少数据。很多从缓存角度理解Redis的人会直接关掉持久化很多从数据库角度理解Redis的人又会开启全部持久化选项。这两种都过于极端需要按业务接受的数据丢失量来做取舍。4.1 RDB 快照save 规则怎么读懂RDB是Redis在指定时间点生成的全量快照。默认配置长这样save 900 1 save 300 10 save 60 10000规则是在900秒内至少有1个键发生变化则执行一次快照在300秒内至少有10个键变化则执行一次快照在60秒内至少有10000个键变化则执行一次快照。满足任意一条即触发。如果你想完全关闭RDB注释掉所有save规则或者显式写一句save 这里有一个影响很大的配置stop-writes-on-bgsave-error默认是yes。意思是如果后台生成RDB快照失败典型的比如磁盘满了Redis会拒绝写入请求直接在业务侧引发大面积写入失败。这个设计的初衷是为了避免数据已经无法持久化但还在持续接新写入的雪球效应。但对于缓存场景这个行为反而被认为是晴天打伞——反正数据丢了也能重建不如继续提供缓存服务。所以如果明确是缓存实例、且不依赖持久化可以改成stop-writes-on-bgsave-error no改这个配置前要想清楚你确实接受RDB可能写失败、数据丢失但服务不能停。我一般只在纯缓存可重建的场景才改。4.2 AOF 日志appendfsync 的三个档位AOF以追加日志的方式记录每次写命令粒度比RDB更细。开启方式appendonly yes appendfilename appendonly.aof关键的频率参数是appendfsync有三个档位always每次写命令都同步刷盘最多丢一条命令但性能开销最大。everysec每秒刷一次盘最多丢1秒的数据性能和安全的平衡点生产环境默认推荐。no由操作系统决定何时刷盘性能好但数据丢失时间不可控。我几乎不会把appendfsync设成always因为写性能损耗很大对绝大多数业务来说用不上每次写都保证不丢。everysec基本是通用选择。AOF还有一个容易被忽略的项auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mbAOF文件会不断增长Redis会在AOF文件体积比上次重写时增长了100%并且超过64MB时自动触发重写压缩AOF文件。对于长期运行且写入量大的实例这两个参数一般不用动但如果磁盘空间很紧张可以考虑把auto-aof-rewrite-min-size调大到256mb降低重写频率。4.3 RDB 与 AOF 同时开启时的行为如果同时开启RDB和AOFRedis重启时优先加载AOF文件因为AOF包含的命令日志通常比RDB快照更新、更完整。Redis 4.0之后提供了混合持久化aof-use-rdb-preamble yes开启后AOF文件头部先写一段RDB格式的全量快照再追加后续的增量命令。好处是重启加载时先快速载入RDB数据再执行增量命令加载速度和数据完整性都兼顾。Redis 7.0中这个配置默认开启一般不需要手动干预。4.4 具体业务场景下的持久化选择我通常按三种场景给配置建议纯缓存数据允许丢失关闭持久化save appendonly no最大化性能。需要一定可靠性但可以接受秒级丢失开启AOFappendonly yesappendfsync everysec可以不开启RDB。既要有恢复速度又要尽量少丢数据同时开启RDB和AOFaof-use-rdb-preamble yes。有一种常见误解是AOF开了一定比RDB慢很多实际上在everysec档位下写性能损耗通常控制在可接受范围代价主要是磁盘空间占用和文件加载时间。5. 主从复制与哨兵模式配置文件里最容易写错的一组关系Redis主从复制和哨兵高可用配置本身不算复杂真正坑人的是多个节点之间参数不联动尤其是密码、持久化策略和超时时间这类配置在主从间不一致时平时看不出来故障时才会爆发。5.1 从库配置replicaof 与 replica-read-only从库的复制配置非常简洁在主从架构下从库的redis.conf里加一行replicaof 192.168.1.10 6379这里写主库的IP和端口。复制关系建立后默认从库是只读的配置项是replica-read-only yes如果你因为某些特殊需要让从库可写设置成no。但强烈不建议在生产环境这么做否则主从数据很快不一致一旦发生主从切换这台脏数据从库会变成新的主库整个集群数据就歪了。还有一个参数在与主库断连时体现replica-serve-stale-data yes默认yes意思是主从断连后从库继续对外提供旧数据。这个行为适合读多写少、短暂断连不影响的场景。如果业务要求强一致可以设成no从库在主从断连期间会拒绝所有请求提示SYNC with master in progress但代价是下游读请求全部报错。5.2 主从增量复制的关键参数repl-backlog-size主从复制断线后如果重连较快Redis会尝试增量同步——主库把断线期间的写命令从复制积压缓冲区发给从库。这个缓冲区大小就是repl-backlog-size 1mb默认1MB很小。当从库断线时间较长、积累的写命令超过缓冲区大小时增量同步无法完成主库不得不做一次全量RDB同步把RDB文件整体传给从库。全量同步对主库磁盘IO、网络带宽、从库加载时间都有明显影响大实例甚至会阻塞主库。建议根据写入吞吐量调整repl-backlog-size 64mb具体值可以估算假设高峰期每秒写5MB命令期望容忍从库断线60秒缓冲区至少需要300MB。很多团队的实例在从库短暂抖动后就触发全量同步根因就是repl-backlog-size太小。实际配置时我会按预估峰值写入量的2-3倍设置给突发流量留余量。5.3 sentinel.conf哨兵也是配置文件驱动的哨兵节点不是通过redis.conf管理而是单独一份sentinel.conf。核心配置sentinel monitor mymaster 192.168.1.10 6379 2 sentinel down-after-milliseconds mymaster 5000 sentinel failover-timeout mymaster 15000mymaster是给这个主节点取的名字哨兵配置文件里可以同时监控多个主节点每个用不同的名字区分。最后的数字2是quorum表示至少需要多少个哨兵节点同意主库客观下线才会触发故障转移。如果主库启用了requirepass哨兵连接主库也需要认证sentinel auth-pass mymaster your-strong-password如果从库也设置了密码哨兵在故障转移时会把新的主库地址连同密码一起通知给从库这个场景下requirepass和masterauth不一致时故障转移后新主库和从库之间的复制可能直接失败。所以再次强调主从架构里所有实例的认证信息尽量保持一致。5.4 一个真实的主从配置故障有一次处理过一个问题主库因为磁盘故障重启了重启后从库日志里一直刷MASTER abored replication with an error: NOAUTH Authentication required。查了一圈主库的requirepass是最近一次安全加固时新加上的从库的masterauth没有同步更新。从库一直尝试向主库发起复制请求但因为没有匹配的认证信息被拒绝。整个过程中主库已经恢复正常从库却迟迟没有重新同步。修复方式很简单在从库执行127.0.0.1:6379 CONFIG SET masterauth your-strong-password 127.0.0.1:6379 CONFIG REWRITE # 或者直接修改 redis.conf 里的 masterauth 后重启从库然后重新执行127.0.0.1:6379 REPLICAOF 192.168.1.10 6379复制链路才恢复。这个问题根因不在Redis而是配置变更管理没有覆盖到所有节点。6. 我踩过的配置坑以及现在推荐的基础配置模板最后分享几个真实的踩坑记录以及一套我目前在不同场景下都比较顺手的基础配置模板。这些坑的共同特点是排查时容易往代码层去猜实际上最后都回到配置文件。6.1 三个真实踩坑记录第一个坑timeout与连接池空闲连接冲突。有台Redis实例配置了timeout 30本意是回收空闲连接结果客户端连接池里长期持有的空闲连接被服务端主动关闭客户端下一次复用连接时才发现连接已失效直接抛Connection reset。排查了半天最后在配置里注释掉这行才算完。如果你的客户端连接池本身有心跳检测服务端timeout设一个较大的值比如300秒或者直接保持0更稳妥。第二个坑maxmemory设置超过物理内存后触发系统级OOM。实例是16G内存的物理机配置文件里maxmemory 32gb本意是给Redis多留点空间结果Redis内存涨到23G时机器开始大量使用swapRedis命令平均耗时从0.1ms变成800ms最后整个进程被OOM Killer杀掉。这里的问题在于maxmemory只是Redis层面的内存限制操作系统层面还有物理内存压力。合理的上限应该远低于物理内存不能只看Redis自身。第三个坑运行期修改配置后没有执行CONFIG REWRITE。有一次临时调大maxmemory-policy解决了缓存淘汰引发的告警当时确实生效了后来机房断电重启Redis加载回原来的配置告警再次出现而且这次因为数据量已经涨上来了直接触发了内存溢出。从此我养成习惯任何通过CONFIG SET修改的配置确认无误后立刻执行CONFIG REWRITE落盘。6.2 我目前推荐的基础配置模板下面这份模板适用于多数常规业务场景兼容单机、主从、哨兵模式读者可以根据实际情况删减调整# 基础运行 port 6379 protected-mode yes tcp-backlog 511 timeout 0 tcp-keepalive 300 supervised no daemonize yes pidfile /var/run/redis_6379.pid loglevel notice logfile /var/log/redis/redis-server.log databases 16 # 网络与安全 bind 127.0.0.1 192.168.1.100 requirepass your-strong-password masterauth your-strong-password # 内存管理 maxmemory 10gb maxmemory-policy allkeys-lru maxmemory-samples 5 # 持久化可按业务场景裁剪 save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir /var/lib/redis appendonly yes appendfilename appendonly.aof appendfsync everysec auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-use-rdb-preamble yes # 复制相关从库使用 # replicaof 192.168.1.10 6379 replica-read-only yes replica-serve-stale-data yes repl-backlog-size 64mb replica-priority 100我的使用习惯是**先理解每个配置对应的行为边界再动手调参。**像timeout、maxmemory、appendfsync这种牵一发动全身的参数改动前会先想清楚这个Redis实例在架构里的角色——是缓存、是数据存储、还是分布式锁的载体不同角色对同一个参数的合理取值可能是相反的。配置文件的维护还有一个不容忽视的点**每次变更都应该像改代码一样走评审和记录尤其是密码类配置和持久化策略。**Redis的配置项不像代码那样有编译期校验很多参数写错并不会让你无法启动而是让实例在运行了几天、甚至几个月之后才以某种隐蔽的方式出问题。把配置纳入版本管理变更前做好备份排查问题时能少走很多弯路。