配置LEMP系統(tǒng)實用教程及技巧示例
譯文如果你在使用nginx作為Web服務器,很可能在尋找性能提升和更快的速度。Nginx在默認情況下速度就很快,但是你可以優(yōu)化其性能以及與nginx協(xié)同運行的所有組件(比如PHP和MySQL)的性能。下面是一份并不全面的列表,介紹了配置LEMP系統(tǒng)(Linux、nginx、MySQL和PHP-FPM),以獲得***性能的要點和技巧。這些技巧對我來說很管用,但是你的情況可能不一樣。別同時運用所有這些要點和技巧,而是應該逐個試一試,看看這些改動對你的系統(tǒng)的性能有什么樣的影響。
我并不保證本文會適用于你的情況!
1. 借助noatime和nodiratime掛載分區(qū),減少磁盤的輸入/輸出
把noatime和nodiratime添加到/etc/fstab中的掛載選項:
vi /etc/fstab
# /etc/fstab:靜態(tài)文件系統(tǒng)信息。
#
# 使用“blkid”打印設備的全局唯一標識符;這可與UUID=結合使用,
# 作為一種更可靠的方式來命名即使磁盤被添加和移除仍可正常工作的設備。參閱fstab(5)。
#
# <文件系統(tǒng)> <掛載點> <類型> <選項> <轉儲> <傳遞>
proc /proc proc defaults 0 0
# 安裝過程中,/出現(xiàn)在/dev/sda2上
UUID=9cc886cd-98f3-435a-9830-46b316e2a20e / ext3 errors=remount-ro,noatime,nodiratime,usrjquota=quota.user,grpjquota=quota.group,jqfmt=vfsv0 0 1
# 安裝過程中,置換出現(xiàn)在/dev/sda1上
UUID=bba13162-121d-40a4-90a7-10f78a0097ae none swap sw 0 0
/dev/scd0 /media/cdrom0 udf,iso9660 user,noauto 0 0
#Parallels共享文件夾掛載
none /media/psf prl_fs sync,nosuid,nodev,noatime,share,nofail 0 0
重新掛載修改后的分區(qū),方法如下(確保你為每個分區(qū)使用正確的掛載點):
mount -o remount /
#p# 2. 調整nginx
2.1 worker_processes
確保你在/etc/nginx/nginx.conf中使用了數(shù)量正確的worker_processes(工作進程)。這個數(shù)應該相當于下列命名的輸出中的處理器核心數(shù)量:
cat /proc/cpuinfo | grep processor
root@server1:~# cat /proc/cpuinfo | grep processor
processor : 0
processor : 1
processor : 2
processor : 3
processor : 4
processor : 5
processor : 6
processor : 7
root@server1:~#
在該例子中,我們有八個處理器核心,于是我們設置
vi /etc/nginx/nginx.conf
[...]
worker_processes 8;
[...]
2.2 keepalive_timeout、sendfile、tcp_nopush和tcp_nodelay
把keepalive_timeout設成一個合理的值,比如2秒。啟用sendfile、tcp_nopush和tcp_nodelay:
vi /etc/nginx/nginx.conf
[...]
http {
[...]
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 2;
types_hash_max_size 2048;
server_tokens off;
[...]
}
[...]
#p# 2.3 文件緩存
啟用nginx文件緩存:
vi /etc/nginx/nginx.conf
[...]
http {
[...]
##
# 文件緩存的設置
##
open_file_cache max=5000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
[...]
}
[...]
#p# 2.4 啟用Gzip壓縮
你可以在下面進一步了解Gzip壓縮:《如何使用nginx的HttpGzipModule(Debian Squeeze)節(jié)省流量?》(http://www.howtoforge.com/how-to-save-traffic-with-nginxs-httpgzipmodule-debian-squeeze)。
vi /etc/nginx/nginx.conf
[...]
http {
[...]
##
# Gzip設置
##
gzip on;
gzip_static on;
gzip_disable "msie6";
gzip_http_version 1.1;
gzip_vary on;
gzip_comp_level 6;
gzip_proxied any;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
gzip_buffers 16 8k;
[...]
}
[...]
#p# 2.5 啟用SSL會話緩存
如果你服務于https網(wǎng)站,就應該啟用SSL會話緩存:
vi /etc/nginx/nginx.conf
[...]
http {
[...]
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
[...]
}
[...]
#p# 2.6 使用FastCGI緩存
如果你有可以緩存的PHP內(nèi)存,可以使用nginx FastCGI緩存來緩存該內(nèi)容。在你的nginx.conf中,添加一行,類似這一行:
vi /etc/nginx/nginx.conf
[...]
http {
[...]
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=microcache:10m max_size=1000m inactive=60m;
[...]
}
[...]
緩存目錄/var/cache/nginx必須要有,而且對nginx來說必須是可寫的:
mkdir /var/cache/nginx
chown www-data:www-data /var/cache/nginx
如果使用tmpfs,你甚至可以把目錄直接放入到你服務器的內(nèi)存中,這在速度上提供了另一個小小的優(yōu)勢——想了解更多的信息,請參閱這篇教程:《使用tmpfs,將文件/目錄存儲在內(nèi)存中》(http://www.howtoforge.com/storing-files-directories-in-memory-with-tmpfs)。
在你的vhost配置中,把下列語句塊添加到你的location ~ \.php$ {}部分(你可以修改代碼段,這取決于內(nèi)容何時緩存、何時不緩存):
[...]
# 設置var默認值
set $no_cache "";
# 如果沒有GET/HEAD,不緩存&通過cookie將用戶標記為不可緩存1秒
if ($request_method !~ ^(GET|HEAD)$) {
set $no_cache "1";
}
# 如果需要,不丟棄任何緩存cookie
# (由于某種原因,如果添加到之前的if語句塊,add_header失敗)
if ($no_cache = "1") {
add_header Set-Cookie "_mcnc=1; Max-Age=2; Path=/";
add_header X-Microcachable "0";
}
# 如果非緩存cookie已被設置,繞過緩存
if ($http_cookie ~* "_mcnc") {
set $no_cache "1";
}
# 如果標記已被設置,繞過緩存
fastcgi_no_cache $no_cache;
fastcgi_cache_bypass $no_cache;
fastcgi_cache microcache;
fastcgi_cache_key $scheme$host$request_uri$request_method;
fastcgi_cache_valid 200 301 302 10m;
fastcgi_cache_use_stale updating error timeout invalid_header http_500;
fastcgi_pass_header Set-Cookie;
fastcgi_pass_header Cookie;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
[...]
所以整個location ~ \.php$ {}部分看起來如下:
[...]
location ~ \.php$ {
# 設置var默認值
set $no_cache "";
#如果沒有GET/HEAD,不緩存&通過cookie將用戶標記為不可緩存1秒
if ($request_method !~ ^(GET|HEAD)$) {
set $no_cache "1";
}
#如果需要,不丟棄任何緩存cookie
#(由于某種原因,如果添加到之前的if語句塊,add_header失敗)
if ($no_cache = "1") {
add_header Set-Cookie "_mcnc=1; Max-Age=2; Path=/";
add_header X-Microcachable "0";
}
#如果非緩存cookie已被設置,繞過緩存
if ($http_cookie ~* "_mcnc") {
set $no_cache "1";
}
#如果標記已被設置,繞過緩存
fastcgi_no_cache $no_cache;
fastcgi_cache_bypass $no_cache;
fastcgi_cache microcache;
fastcgi_cache_key $scheme$host$request_uri$request_method;
fastcgi_cache_valid 200 301 302 10m;
fastcgi_cache_use_stale updating error timeout invalid_header http_500;
fastcgi_pass_header Set-Cookie;
fastcgi_pass_header Cookie;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
try_files $uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/lib/php5-fpm/web1.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_intercept_errors on;
}
[...]
這將把返回代碼是200、301和302的頁面緩存10分鐘。
#p# 2.7 使用FastCGI緩沖器
在你的vhost配置中,你可以把下列幾行添加到你的location ~ \.php$ {}部分:
[...]
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_read_timeout 240;
[...]
整個location ~ \.php$ {}部分看起來如下:
[...]
location ~ \.php$ {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/lib/php5-fpm/web1.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_intercept_errors on;
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_read_timeout 240;
}
[...]
#p# 2.8 使用memcached
nginx可以讀取直接來自memcached的整個頁面。所以,如果你的W eb應用程序能夠在memcached中存儲整個頁面,nginx就能從memcached讀取該頁面。示例配置應該如下:
[...]
location ~ \.php$ {
set $no_cache "";
if ($query_string ~ ".+") {
set $no_cache "1";
}
if ($request_method !~ ^(GET|HEAD)$ ) {
set $no_cache "1";
}
if ($request_uri ~ "nocache") {
set $no_cache "1";
}
if ($no_cache = "1") {
return 405;
}
set $memcached_key $host$request_uri;
memcached_pass 127.0.0.1:11211;
default_type text/html;
error_page 404 405 502 = @php;
expires epoch;
}
location @php {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
fastcgi_pass unix:/var/lib/php5-fpm/web1.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_intercept_errors on;
}
[...]
重要的是,你的Web應用程序在memcached中存儲頁面所使用的密鑰與nginx從memcached中讀取這些頁面所使用的密碼是同一把(本例中是$host$request_uri),不過這不管用。
如果你在memcached中存儲了許多數(shù)據(jù),就要確保你已為memcached分配了足夠的內(nèi)容,比如:
vi /etc/memcached.conf
[...]
# 開始內(nèi)存限額為64MB。這很合理。
# 注意:守護程序會增加到這個大小,但是不會一開始就占用這么大的內(nèi)存。
-m 512
[...]
#p# 2.9 使用expires指令,讓瀏覽器緩存靜態(tài)文件
只要使用expires指令,就可以讓訪客的瀏覽器緩存不經(jīng)常變化的文件,如圖片、CSS和JS等文件(詳見http://wiki.nginx.org/HttpHeadersModule#expires):
[...]
location ~* \.(jpg|jpeg|png|gif|ico)$ {
expires 365d;
}
[...]
2.10 禁用靜態(tài)文件的日志功能
將圖片或CSS文件記入到訪問日志中通常沒有多大意義。為了減少磁盤的輸入/輸出,我們可以禁用這類文件的日志功能,比如如下:
[...]
location ~* \.(jpg|jpeg|png|gif|ico)$ {
log_not_found off;
access_log off;
}
[...]
#p# 3 調整PHP-FPM
3.1 使用A PHP Opcode緩存,比如Xcache或APC
確保你已安裝了PHP opcode緩存,比如Xcache或APC。在Debian/Ubuntu上,可以按如下方式安裝Xcache:
apt-get install php5-xcache
可以按如下方式安裝APC:
apt-get install php-apc
確保你只安裝了其中一個(安裝了Xcache或APC),而不是兩個都安裝了。安裝之后重新裝入PHP-FPM:
/etc/init.d/php5-fpm reload
3.2 為Xcache/APC分配足夠的內(nèi)存
如果你有大量的PHP腳本,可能應該增加分配給Xcache或APC的內(nèi)容。如果是Xcache,你可以在/etc/php5/conf.d/xcache.ini中這么操作:
vi /etc/php5/conf.d/xcache.ini
[...]
xcache.size = 512M
[...]
如果是APC,方法類似:
vi /etc/php5/conf.d/apc.ini
[...]
apc.shm_size="512"
[...]
修改后重新裝入PHP-FPM:
/etc/init.d/php5-fpm reload
#p# 3.3 PHP-FPM緊急設置
這更像是提高可靠性的設置,而不是提升性能的設置:如果PHP-FPM停止工作,它會重啟自己:
vi /etc/php5/fpm/php-fpm.conf
[...]
; 如果在emergency_restart_interval設定的時間間隔內(nèi),這個數(shù)量的子進程以SIGSEGV或SIGBUS退出,
; 那么FPM會重啟。值為“o”意味著“斷開”。
; 默認值:0
emergency_restart_threshold = 10
; emergency_restart_interval所用的時間間隔決定了平穩(wěn)重啟何時開始。
; 這有助于避開方向感應器的共享內(nèi)存里面偶爾出現(xiàn)的故障。
; 可用單位:s(秒)、m(分)、h(小時)或d(天)
; 默認單位:秒
; 默認值:0
emergency_restart_interval = 1m
; 子進程等待來自主進程的信號有所反應的時間限制。
; 可用單位:s(秒)、m(分)、h(小時)或d(天)
; 默認單位:秒
; 默認值:0
process_control_timeout = 10s
[...]
#p# 3.4 如果PHP >= 5.3.9,使用ondemand進程管理器
如果你使用PHP >= 5.3.9,可以使用PHP-FPM池中的ondemand進程管理器,而不是靜態(tài)或動態(tài),這將為你節(jié)省一些內(nèi)存:
[...]
pm = ondemandpm.max_children = 100pm.process_idle_timeout = 5s
[...]
3.5 使用Unix套接字,而不是TCP套接字
為了降低網(wǎng)絡開銷,你應該配置PHP-FPM池,以便使用Unix套接字,而不是TCP套接字:
[...]
;listen = 127.0.0.1:9000listen = /var/lib/php5-fpm/www.socklisten.owner = www-datalisten.group = www-datalisten.mode = 0660
[...]
如果你變更這個必須,當然必須調整nginx vhost中的location ~ \.php$ {}部分,以便使用該套接字(fastcgi_pass unix:/var/lib/php5-fpm/www.sock,而不是fastcgi_pass 127.0.0.1:9000):
[...]
location ~ \.php$ {
try_files $uri =404;
include /etc/nginx/fastcgi_params;
##fastcgi_pass 127.0.0.1:9000;
fastcgi_pass unix:/var/lib/php5-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_intercept_errors on;
}
[...]
#p# 3.6 使用套接字,避免忙碌網(wǎng)站上的502壞網(wǎng)關錯誤
如果你使用Unix套接字和PHP-FPM,你可能會遇到忙碌網(wǎng)站隨機出現(xiàn)的502壞網(wǎng)關錯誤。為了避免這個問題,我們提高了允許連接至套接字的***數(shù)量。打開/etc/sysctl.conf...
vi /etc/sysctl.conf
然后設置:
[...]
net.core.somaxconn = 4096
[...]
運行:
sysctl -p
之后等變更生效。
#p# 4 調整MySQL
4.1 優(yōu)化你的my.cnf
你應該使用mysqltuner.pl或tuning-primer.sh腳本,查看你應該在my.cnf文件中調整哪些設置。最重要的變量之一是query_cache_size,如果你使用InnoDB表,
innodb_buffer_pool_size.
這是來自配備16GB內(nèi)存的測試服務器的示例配置,大約30個數(shù)據(jù)庫有一半是MyISAM表,另一半是是InnoDB表——這對于使用基準測試工具(ab)進行壓力測試的數(shù)據(jù)庫驅動的測試網(wǎng)站來說效果相當好:
[...]
key_buffer = 256M
max_allowed_packet = 16M
thread_stack = 192K
thread_cache_size = 100
table_open_cache = 16384
table_definition_cache = 8192
sort_buffer_size = 256K
read_buffer_size = 128K
read_rnd_buffer_size = 256K
myisam_sort_buffer_size = 64M
myisam_use_mmap = 1
thread_concurrency = 10
wait_timeout = 30
myisam-recover = BACKUP,FORCE
query_cache_limit = 10M
query_cache_size = 1024M
query_cache_type = 1
join_buffer_size = 4M
log_slow_queries = /var/log/mysql/mysql-slow.log
long_query_time = 1
expire_logs_days = 10
max_binlog_size = 100M
innodb_buffer_pool_size = 2048M
innodb_log_file_size = 256M
innodb_log_buffer_size = 16M
innodb_flush_log_at_trx_commit = 0
innodb_thread_concurrency = 8
innodb_read_io_threads = 64
innodb_write_io_threads = 64
innodb_io_capacity = 50000
innodb_flush_method = O_DIRECT
innodb_file_per_table
innodb_additional_mem_pool_size = 256M
transaction-isolation = READ-COMMITTED
innodb_support_xa = 0
innodb_commit_concurrency = 8
innodb_old_blocks_time = 1000
[...]
請注意:如果你需要遵守ACID規(guī)定,必須將innodb_flush_log_at_trx_commit設成1。想了解這方面的更多信息,請訪問:http://dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit。
只要你在固態(tài)硬盤上使用MySQL,才應該將innodb_io_capacity設成很高的值。如果你在普通硬盤上使用MySQL,***別理這一行。
4.2 使用固態(tài)硬盤
如果你在固態(tài)硬盤上使用MySQL,可以大幅提升性能,因為這大大降低磁盤的輸入/輸出。為此,最容易的辦法就是把/var/lib/mysql目錄掛載到固態(tài)硬盤上。
#p# 5 Web應用程序緩存
許多Web應用程序提供了創(chuàng)建整個頁面緩存的可能性,這個緩存存儲在硬盤上,可以直接被nginx訪問,那樣它就能繞開整個PHP-MySQL堆棧架構。這樣的Web應用程序包括:WordPress(帶WP Super Cache或W3 Total Cache插件)、Drupal(帶Boost模塊)以及TYPO3(帶nc_staticfilecache擴展)。這大幅提升了性能。
你還可以進一步為靜態(tài)文件緩存提速,只要使用tmpfs文件系統(tǒng)把靜態(tài)文件緩存直接放入到服務器的內(nèi)存中:
《使用tmpfs,在內(nèi)存中存儲文件/目錄》(http://www.howtoforge.com/storing-files-directories-in-memory-with-tmpfs)
6 相關鏈接
•nginx維基:http://wiki.nginx.org/Main
•PHP:http://www.php.net/
•PHP-FPM:http://php-fpm.org/
•MySQL:http://www.mysql.com/