Redis 的性能由配置、硬件和操作系统共同决定。很多案例中,Redis 实例本身没有慢查询,也没有达到内存上限,但客户端仍然出现超时、连接被拒绝或者持久化失败,此时问题多半出在内核参数的默认值上。调整内核参数不是盲目把数值调大,而是要让内存分配、进程调度和网络栈的行为与 Redis 的工作模式匹配。

一、vm.overcommit_memory 与持久化子进程
Redis 生成 RDB 快照和 AOF 重写时都会调用 fork 创建子进程。Linux 的 fork 采用写时复制,父进程和子进程在修改页之前共享同一份物理内存。如果操作系统不允许内存过量使用,即使此时可用物理内存足够,fork 也可能因为虚拟内存检查失败而返回错误,表现为后台保存任务中断。
vm.overcommit_memory 有三个取值:0 表示启发式判断,1 表示始终允许过量使用,2 表示严格按 CommitLimit 限制分配。生产环境建议设为 1,因为 Redis 已经通过 maxmemory 管理自身内存,内核不必在 fork 时做过严的虚拟地址空间校验。如果选择 2,则需要根据 vm.overcommit_ratio 额外增加预留量,否则容易出现 fork 失败。
同时要关注交换分区。当物理内存紧张时,内核会把 Redis 的页换出到 swap,一旦请求命中了磁盘上的页,延迟会从微秒级恶化到毫秒级。vm.swappiness 控制内核换出匿名页的倾向,默认值 60 对 Redis 并不友好。推荐设置为 0 或 1,让内核尽量保留匿名页,避免 Redis 数据被换出。
sysctl -w vm.overcommit_memory=1 sysctl -w vm.swappiness=1 echo never > /sys/kernel/mm/transparent_hugepage/enabled echo never > /sys/kernel/mm/transparent_hugepage/defrag
透明大页同样会拖慢 fork 和写时复制。THP 试图使用 2MB 大页提升地址转换效率,但 Redis 写入频繁时,页拆分和内存碎片会让 fork 子进程的内存占用显著增加。建议在 /etc/rc.local 或 systemd 服务中持久化关闭。
二、网络队列与 TCP 连接建立
Redis 的 tcp-backlog 配置决定了完成 TCP 三次握手后等待应用 accept 的连接队列长度。但这个值受到内核参数 net.core.somaxconn 的硬限制。即使 Redis 里设置 tcp-backlog 511,如果 somaxconn 仍是默认的 128,最终生效的仍是 128。当瞬时连接量较高时,客户端会直接收到连接拒绝。
除了 accept 队列,半连接队列由 net.ipv4.tcp_max_syn_backlog 控制,网卡接收队列由 net.core.netdev_max_backlog 控制。突发流量下,如果这两个值过小,新连接会在内核层被丢弃,应用层只能看到握手超时。推荐将三个参数都调整到 65535 或更高,再根据实际并发微调。
sysctl -w net.core.somaxconn=65535 sysctl -w net.ipv4.tcp_max_syn_backlog=65536 sysctl -w net.core.netdev_max_backlog=65536
对短连接较多的场景,TIME_WAIT 会占用端口资源。开启 net.ipv4.tcp_tw_reuse 可以复用处于 TIME_WAIT 状态的连接,一般不会带来额外风险;但不要使用 tcp_tw_recycle,该参数在较新内核中已移除,且旧版本在 NAT 环境下会造成连接异常。还可以适当调小 net.ipv4.tcp_fin_timeout,加快端口回收速度。
长连接场景下则需要调整 keepalive 参数。Redis 客户端通常通过连接池保持长连接,但中间网络设备可能静默断开空闲连接。net.ipv4.tcp_keepalive_time 默认 7200 秒太长,可以调整为 300 秒,并配合 tcp_keepalive_intvl 和 tcp_keepalive_probes,让内核更早发现失效连接。
三、文件描述符与 OOM 保护
Redis 每个客户端连接、每个监听套接字以及内部事件循环都会占用文件描述符。如果进程的 nofile 限制还是默认的 1024,当连接数接近这个值时,Redis 会开始报错。先通过 ulimit -n 临时提高,再编辑 /etc/security/limits.conf 为 Redis 用户配置持久化值。系统级 fs.file-max 通常也需要相应调大。
ulimit -n 100000 fs.file-max=1000000
第二行适合写入 /etc/sysctl.conf,第一行通常放在 limits.conf 中。需要注意 limits.conf 的格式是 redis soft nofile 100000 和 redis hard nofile 100000,如果使用 systemd 管理 Redis,还需要在 service 文件中设置 LimitNOFILE=100000,否则 systemd 会覆盖 shell 的 ulimit 值。
OOM 保护同样关键。服务器内存耗尽时,Linux 的 OOM killer 可能触发并选中 Redis,导致缓存整体丢失。对于专用 Redis 服务器,不建议直接关闭 OOM killer,而是通过 oom_score_adj 降低 Redis 被选中的概率。例如通过 systemd 的 OOMScoreAdjust=-500 给 Redis 增加保护分,让内核优先处理其他非核心进程。若 Redis 与业务进程混部,还应结合 vm.panic_on_oom 做整体评估,避免系统进入不可控状态。
调整完成后,可以用 sysctl -p 使配置生效,再通过 redis-cli info 检查连接数和 fork 状态。内核参数调优不是一步到位,需要结合实际的监控指标反复验证。