磁盘 IO 的优化
磁盘介质
- 机械硬盘:一般适合大文件,例如日志
- 价格低
- 存储量大
- BPS 较大,适用于顺序读写
- IOPS 较小
- 寿命长
- 固态硬盘:适合小文件的随机读写
- 价格高
- 存储量小
- BPS 大
- IOPS 大,适用于随机读写
- 写寿命短
减少磁盘 IO
减少磁盘 IO 可以从以下几个方面来入手。
- 优化读取
- sendfile 零拷贝
- 内存盘、SSD 盘
- 减少写入
- AIO
- 增大 error_log 级别,减少写入的日志
- 关闭 access_log:直接就不写日志
- 压缩 access_log:减小写入日志的大小
- 是否启用 proxy buffering
- syslog 替代本地 IO
- 线程池 thread pool
直接 IO 绕开磁盘高速缓存
- 通常情况下,文件需要写入缓冲区然后再写到磁盘,读取的时候也是一样,先读到缓冲区,再读到用户存储层,相当于有两次拷贝
- 直接 IO 相当于绕开了缓冲区,直接从磁盘读写
直接 IO,适用于大文件
当磁盘上的文件大小超过 size 后,启用 directIO 功能,避免 Buffered IO 模式下磁盘页缓存中的拷贝消耗。
1 | Syntax: directio size | off; |
- directio:需要配置一个大小的指令,表示超过这个大小就会启用 directio。
异步 IO
在通常情况下,用户进程读取或者写入文件的时候会发生阻塞。而异步 IO 则是用户进程可以去处理其他任务,当写入或读取返回的时候再继续处理。
1 | Syntax: aio on | off | threads[=pool]; |
- aio:可以设置为 on,另外还可以设置线程池
- aio_write:通常情况下写入是写到磁盘高速缓存的,这个缓存是在内存中的,所以一般不需要开启 aio,而这个指令设置为 on 之后就会强制打开 aio,只有当接收上游服务的响应,同时开启了 proxy buffering,将响应写入到临时文件中的时候才有必要打开 aio
异步读 IO 线程池
- 编译时加 –with-threads
在静态资源服务场景下,就会有很多阻塞的场景,这时候需要线程池来处理这些请求。
1 | Syntax: thread_pool name threads=number [max_queue=number]; |
- thread_pool:仅用于做静态资源服务的时候使用
异步 IO 中的缓存
将磁盘文件读入缓存中待处理,例如 gzip 模块会使用
1 | Syntax: output_buffers number size; |
减少磁盘读写次数
empty_gif 模块
- 模块:
ngx_http_empty_gif_module
模块,通过--without-http_empty_gif_module
禁用模块 - 功能:从前端页面做用户行为分析时,由于跨域等要求,前端打点上报数据一般是 GET 请求,且考虑到浏览器解析 DOM 树的性能消耗,所以请求透明图片消耗最小,而 1*1 的 gif 图片体积最小(仅 43 字节),故通常请求 gif 图片,并在请求中把用户行为信息上报服务器
- Nginx 可以在 access 日志中获取到请求参数,进而统计用户行为,但若在磁盘中读取 1*1 的文件则有磁盘 IO 消耗,empty_gif 模块将图片放在内存中,加快了处理速度
1 | Syntax: empty_gif; |
access 日志的压缩
1 | Syntax: access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]]; |
- buffer 默认 64 KB
- gzip 默认级别为 1
- 通过 zcat 解压查看
error.log 日志输出内存
- 场景:在开发环境下定位问题时,若需要打开 debug 级别日志,但对 debug 级别大量日志引发的性能问题不能容忍,可以将日志输出到内存中
- 配置语法:
error_log memory:32m debug;
- 查看内存中日志的方法:
- gdb -p [worker 进程 id] -ex “source nginx.gdb” –batch
- nginx.gdb 脚本内容
1 | set $log = ngx_cycle->log |
syslog 协议
可以把针对磁盘 IO 的写操作通过网络写入到其他机器上面去。
网络协议:通过网络传输到 syslog 服务器
server:定义 syslog 服务器地址
facility
- 参见RFC3164,取值:kern, user, mail, daemon, auth, intern, lpr, news,uucp, clock, authpriv, ftp, ntp, audit, alert, cron, local0..local7
默认是 locaI7
severity:定义 access.log 日志的级别,默认 info 级别
tag:定义日志的 tag,默认为 nginx
nohostname:不向 syslog 中写入主机名 hostname
rsyslog 与 Nginx:使用 rsyslog 来搭建一个验证服务
sendfile 零拷贝提升性能
- 减少进程间切换
- 减少内存拷贝次数
正常情况下,需要从内核缓冲区拷贝到应用程序缓冲区,而 sendfile 零拷贝技术就是不需要从内核拷贝到应用程序缓冲区而是直接从页缓存发送到 socket 缓冲区。
但是有一个问题,那就是如果开启了 sendfile,然后又需要做 gzip 压缩的时候,是必须读取到磁盘上的,这时候就产生了一个 gzip_static 模块。
gzip_static 模块
这个模块会把静态文件在原有目录下做一下压缩,文件必须是以 .gz 结尾的,这样在 root 或者 alias 指令后面配置,会先检测是否有 gzip 文件,如果有就直接返回给客户端。
- 模块:
ngx_http_gzip_static_module
,通过--with-http_gzip_static_module
启用模块 - 功能:检测到通明 .gz 文件时,response 中以 gzip 相关 header 返回 .gz 文件的内容
1 | Syntax: gzip_static on | off | always; |
- on 表示检测客户端是否支持 gzip
- always 表示无论客户端是否支持都返回
gunzip 模块
- 模块:
ngx_http_gunzip_module
,通过--with-http_gunzip_module
启用模块 - 功能:当客户端不支持 gzip 时,且磁盘上仅有压缩文件,则实时解压缩并将其发送给客户端
1 | Syntax: gunzip on | off; |
tcmalloc
- 更快的内存分配器
- 并发能力强于 glibc:并发线程数越多,性能越好
- 减少内存碎片
- 擅长管理小块内存
http://goog-perftools.sourceforge.net/doc/tcmalloc.html
使用方式
编译 google perf tools,得到 tcmalloc 库:
编译 Nginx 时,加入编译选项
- C 语言生成可执行文件的两个步骤
- 通过 configure 设定编译的额外参数:
--with-cc-opt=OPTIONS set additional C compiler options
- 通过 configure 设定链接的额外参数:
--with-ld-opt=OPTIONS set additional linker options
--with-ld-opt=-ltcmalloc