Nginx作为高并发入口,日志记录是排查请求问题和安全审计的重要依据,但日志写入方式如果不加约束,会间接推高swap交换分区使用率。不少运维人员发现物理内存还有空闲时swap就已经开始增长,此时单纯增加内存不一定能根治,更有效的是从日志缓冲和内核参数两条线入手,减少不必要的内存换出。

一、Nginx日志写入的默认行为与swap产生的条件
Nginx的access_log和error_log指令默认将日志写入指定文件,写入过程依赖操作系统的页缓存机制。具体来说,Nginx worker进程调用write系统调用后,数据先进入内存中的page cache,由内核决定何时通过pdflush或writeback线程刷到磁盘。这个过程本身是异步的,正常情况下不会直接引发swap占用,但如果日志量极大,短时间内产生大量脏页,而系统可用内存又不足,内核就可能把这些脏页换出到swap分区,以腾出物理内存给更活跃的进程使用。
没有设置buffer参数时,每个请求结束都会触发一次write,虽然write本身不会同步落盘,但频繁的系统调用会加剧内存分配和回收,导致内存碎片和页缓存抖动。相比之下,配置合理的日志缓冲可以让Nginx先把日志数据写入一块固定的内存缓冲区,当缓冲区写满或者到达flush时间后才一次写入文件。这样既减少了系统调用次数,也降低了页缓存被大量小IO频繁冲刷的概率,从而间接减少swap换出。
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 通过buffer和flush控制日志写入节奏
access_log /var/log/nginx/access.log main buffer=64k flush=5s;
}这里的buffer=64k表示Nginx会先累积64KB日志再写入文件,flush=5s表示即使缓冲区没满,每5秒也会强制写入一次。这种配置能显著减少高并发下的write调用频率,对降低内存压力和swap占用有实际帮助。但需要注意,buffer设置过大会增加日志丢失风险,如果Nginx异常退出,缓冲区中尚未刷盘的数据会丢失,因此需要结合业务对日志实时性的要求做平衡。
二、swap使用率过高对Nginx请求处理的直接影响
swap交换分区是磁盘上的一块区域,当物理内存不足时,内核会把部分内存页写到swap中,并在需要时再读回。这个换入换出过程称为swap in和swap out,对应vmstat输出中的si和so两列。如果Nginx worker进程自身的内存页被换出到swap,那么当该进程被调度处理新请求时,访问这些内存页就会触发缺页中断,从磁盘读回数据,这个延迟通常是内存访问的几十甚至上百倍。
实际表现往往是Nginx响应时间突然增加,并发能力下降,甚至出现请求超时。用vmstat 1观察时,如果si和so持续不为0,并且伴随较高的bi和bo值,说明系统正在频繁进行swap换页。此时即使CPU使用率不高,Nginx也会因为等待磁盘IO而无法及时处理请求。更严重的情况下,worker进程可能被完全换出,导致accept队列堆积,客户端连接被拒绝。
# 观察swap换入换出和磁盘IO情况
vmstat 1
# 查看各进程swap使用量
for file in /proc/[0-9]*/status; do
awk '/VmSwap|Name/{printf $2 " " $3 "\t"} END{print ""}' $file
done | grep -v '^$' | sort -k2 -n -r | head -10上面第二段命令用于统计所有进程的VmSwap值并排序,可以快速定位是哪些进程占用了最多的swap空间。如果Nginx worker进程的VmSwap数值很高,说明日志写入引发的内存压力已经影响到Nginx自身运行,需要尽快调整配置。
还有一个容易忽略的点:swap使用率升高并不一定是因为内存真的不够,也可能是因为内核的vm.swappiness参数设置过高。该参数的取值范围是0到100,数值越大,内核越倾向于把匿名页换出到swap。默认值通常是60,这对于高并发Web服务器来说偏激进,适当调低能够减少不必要的换出。
三、从日志与内核参数角度降低swap占用的配置思路
首先从Nginx日志配置入手。除了前面提到的buffer和flush参数,还可以根据日志级别区分写入策略。例如error_log可以设置为warn或error级别,减少低级别日志的产生量。对于访问日志,如果某些静态资源请求不需要记录,可以通过location块中的access_log off关闭特定路径的日志,避免大量无意义请求写入日志文件。
server {
listen 80;
server_name ipipp.com;
# 对静态资源关闭访问日志
location /static/ {
access_log off;
root /var/www/html;
}
# 其他请求正常记录,并设置缓冲
location / {
access_log /var/log/nginx/access.log main buffer=32k flush=3s;
proxy_pass http://backend;
}
}其次,调整内核的vm.swappiness参数。对于以Nginx为主要业务的服务器,可以将其设置为10或更小,让内核优先回收页缓存而不是换出进程内存页。修改方式既可以通过sysctl -w vm.swappiness=10临时生效,也可以写入/etc/sysctl.conf永久保存。不过要注意,如果系统内存确实紧张,过度压低swappiness可能导致内核无法及时回收内存,反而触发OOM Killer杀死进程。因此调低该参数前应确保物理内存充足,或者配合vm.vfs_cache_pressure等参数一起调整。
日志切割也是经常被忽视的一环。虽然文件大小本身不会直接增加swap占用,但如果日志文件长期不切割,使用tail、grep等工具分析日志时会一次性读入大量数据到内存,瞬间加剧内存压力。通过logrotate按天或按大小切割日志,并配合compress压缩旧日志,可以减少这种临时性内存冲击。
# /etc/logrotate.d/nginx 示例
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 640 nginx adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}四、监控与排查swap使用与Nginx日志的关联
要确认swap使用是否由Nginx日志引起,可以采用对照排查法。先记录当前swap使用量和每个Nginx worker进程的VmSwap值,然后临时将access_log设置为off或仅记录错误日志,观察一段时间后swap是否明显回落。如果关闭访问日志后swap使用率下降,说明日志写入确实是主要推手,需要启用缓冲并调整写入频率。
另一个有效的监控手段是使用pidstat -r -p PID 1实时查看指定进程的内存和swap使用变化。配合iostat -x 1观察日志所在磁盘的写延迟,如果磁盘写延迟很高,同时伴随swap out,说明日志写入和换页之间形成了争抢,此时可以将Nginx日志目录迁移到独立的磁盘或使用tmpfs存放临时日志,避免与swap分区争抢磁盘IO。
# 实时查看Nginx worker进程的swap使用 pidstat -r -p 12345 1 # 查看日志磁盘的IO压力 iostat -x 1 # 查看某个进程的具体内存分布 grep -E 'VmRSS|VmSwap|RssFile|RssShmem' /proc/12345/status
如果条件允许,还可以考虑将Nginx日志输出到syslog或远程日志服务器,由专门的日志处理节点负责收集和存储。这样Nginx服务器本地不再维护庞大的日志文件,页缓存压力大幅降低,swap占用也会得到明显改善。但这种方式需要额外的网络和日志服务组件,适合日志量特别大或者多台Nginx统一管理的场景。
最后需要强调的是,swap使用率升高是一个综合信号,Nginx日志只是可能的原因之一。在调整日志和内核参数的同时,还应该检查是否有其他进程占用了大量内存,或者是否存在内存泄漏。通过free -h、top -o RES以及slabtop等工具建立系统内存使用的完整视图,才能更准确地定位问题根源,避免盲目优化。