配置LEMP系統(tǒng)實(shí)用教程及技巧示例
譯文如果你在使用nginx作為Web服務(wù)器,很可能在尋找性能提升和更快的速度。Nginx在默認(rèn)情況下速度就很快,但是你可以?xún)?yōu)化其性能以及與nginx協(xié)同運(yùn)行的所有組件(比如PHP和MySQL)的性能。下面是一份并不全面的列表,介紹了配置LEMP系統(tǒng)(Linux、nginx、MySQL和PHP-FPM),以獲得***性能的要點(diǎn)和技巧。這些技巧對(duì)我來(lái)說(shuō)很管用,但是你的情況可能不一樣。別同時(shí)運(yùn)用所有這些要點(diǎn)和技巧,而是應(yīng)該逐個(gè)試一試,看看這些改動(dòng)對(duì)你的系統(tǒng)的性能有什么樣的影響。
我并不保證本文會(huì)適用于你的情況!
1. 借助noatime和nodiratime掛載分區(qū),減少磁盤(pán)的輸入/輸出
把noatime和nodiratime添加到/etc/fstab中的掛載選項(xiàng):
vi /etc/fstab
# /etc/fstab:靜態(tài)文件系統(tǒng)信息。
#
# 使用“blkid”打印設(shè)備的全局唯一標(biāo)識(shí)符;這可與UUID=結(jié)合使用,
# 作為一種更可靠的方式來(lái)命名即使磁盤(pán)被添加和移除仍可正常工作的設(shè)備。參閱fstab(5)。
#
# <文件系統(tǒng)> <掛載點(diǎn)> <類(lèi)型> <選項(xiàng)> <轉(zhuǎn)儲(chǔ)> <傳遞>
proc /proc proc defaults 0 0
# 安裝過(guò)程中,/出現(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
# 安裝過(guò)程中,置換出現(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ū),方法如下(確保你為每個(gè)分區(qū)使用正確的掛載點(diǎn)):
mount -o remount /
#p# 2. 調(diào)整nginx
2.1 worker_processes
確保你在/etc/nginx/nginx.conf中使用了數(shù)量正確的worker_processes(工作進(jìn)程)。這個(gè)數(shù)應(yīng)該相當(dāng)于下列命名的輸出中的處理器核心數(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:~#
在該例子中,我們有八個(gè)處理器核心,于是我們?cè)O(shè)置
vi /etc/nginx/nginx.conf
[...]
worker_processes 8;
[...]
2.2 keepalive_timeout、sendfile、tcp_nopush和tcp_nodelay
把keepalive_timeout設(shè)成一個(gè)合理的值,比如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 {
[...]
##
# 文件緩存的設(shè)置
##
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壓縮
你可以在下面進(jìn)一步了解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設(shè)置
##
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會(huì)話緩存
如果你服務(wù)于https網(wǎng)站,就應(yīng)該啟用SSL會(huì)話緩存:
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緩存來(lái)緩存該內(nèi)容。在你的nginx.conf中,添加一行,類(lèi)似這一行:
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必須要有,而且對(duì)nginx來(lái)說(shuō)必須是可寫(xiě)的:
mkdir /var/cache/nginx
chown www-data:www-data /var/cache/nginx
如果使用tmpfs,你甚至可以把目錄直接放入到你服務(wù)器的內(nèi)存中,這在速度上提供了另一個(gè)小小的優(yōu)勢(shì)——想了解更多的信息,請(qǐng)參閱這篇教程:《使用tmpfs,將文件/目錄存儲(chǔ)在內(nèi)存中》(http://www.howtoforge.com/storing-files-directories-in-memory-with-tmpfs)。
在你的vhost配置中,把下列語(yǔ)句塊添加到你的location ~ \.php$ {}部分(你可以修改代碼段,這取決于內(nèi)容何時(shí)緩存、何時(shí)不緩存):
[...]
# 設(shè)置var默認(rèn)值
set $no_cache "";
# 如果沒(méi)有GET/HEAD,不緩存&通過(guò)cookie將用戶(hù)標(biāo)記為不可緩存1秒
if ($request_method !~ ^(GET|HEAD)$) {
set $no_cache "1";
}
# 如果需要,不丟棄任何緩存cookie
# (由于某種原因,如果添加到之前的if語(yǔ)句塊,add_header失敗)
if ($no_cache = "1") {
add_header Set-Cookie "_mcnc=1; Max-Age=2; Path=/";
add_header X-Microcachable "0";
}
# 如果非緩存cookie已被設(shè)置,繞過(guò)緩存
if ($http_cookie ~* "_mcnc") {
set $no_cache "1";
}
# 如果標(biāo)記已被設(shè)置,繞過(guò)緩存
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;
[...]
所以整個(gè)location ~ \.php$ {}部分看起來(lái)如下:
[...]
location ~ \.php$ {
# 設(shè)置var默認(rèn)值
set $no_cache "";
#如果沒(méi)有GET/HEAD,不緩存&通過(guò)cookie將用戶(hù)標(biāo)記為不可緩存1秒
if ($request_method !~ ^(GET|HEAD)$) {
set $no_cache "1";
}
#如果需要,不丟棄任何緩存cookie
#(由于某種原因,如果添加到之前的if語(yǔ)句塊,add_header失敗)
if ($no_cache = "1") {
add_header Set-Cookie "_mcnc=1; Max-Age=2; Path=/";
add_header X-Microcachable "0";
}
#如果非緩存cookie已被設(shè)置,繞過(guò)緩存
if ($http_cookie ~* "_mcnc") {
set $no_cache "1";
}
#如果標(biāo)記已被設(shè)置,繞過(guò)緩存
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的頁(yè)面緩存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;
[...]
整個(gè)location ~ \.php$ {}部分看起來(lái)如下:
[...]
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可以讀取直接來(lái)自memcached的整個(gè)頁(yè)面。所以,如果你的W eb應(yīng)用程序能夠在memcached中存儲(chǔ)整個(gè)頁(yè)面,nginx就能從memcached讀取該頁(yè)面。示例配置應(yīng)該如下:
[...]
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應(yīng)用程序在memcached中存儲(chǔ)頁(yè)面所使用的密鑰與nginx從memcached中讀取這些頁(yè)面所使用的密碼是同一把(本例中是$host$request_uri),不過(guò)這不管用。
如果你在memcached中存儲(chǔ)了許多數(shù)據(jù),就要確保你已為memcached分配了足夠的內(nèi)容,比如:
vi /etc/memcached.conf
[...]
# 開(kāi)始內(nèi)存限額為64MB。這很合理。
# 注意:守護(hù)程序會(huì)增加到這個(gè)大小,但是不會(huì)一開(kāi)始就占用這么大的內(nèi)存。
-m 512
[...]
#p# 2.9 使用expires指令,讓瀏覽器緩存靜態(tài)文件
只要使用expires指令,就可以讓訪客的瀏覽器緩存不經(jīng)常變化的文件,如圖片、CSS和JS等文件(詳見(jiàn)http://wiki.nginx.org/HttpHeadersModule#expires):
[...]
location ~* \.(jpg|jpeg|png|gif|ico)$ {
expires 365d;
}
[...]
2.10 禁用靜態(tài)文件的日志功能
將圖片或CSS文件記入到訪問(wèn)日志中通常沒(méi)有多大意義。為了減少磁盤(pán)的輸入/輸出,我們可以禁用這類(lèi)文件的日志功能,比如如下:
[...]
location ~* \.(jpg|jpeg|png|gif|ico)$ {
log_not_found off;
access_log off;
}
[...]
#p# 3 調(diào)整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
確保你只安裝了其中一個(gè)(安裝了Xcache或APC),而不是兩個(gè)都安裝了。安裝之后重新裝入PHP-FPM:
/etc/init.d/php5-fpm reload
3.2 為Xcache/APC分配足夠的內(nèi)存
如果你有大量的PHP腳本,可能應(yīng)該增加分配給Xcache或APC的內(nèi)容。如果是Xcache,你可以在/etc/php5/conf.d/xcache.ini中這么操作:
vi /etc/php5/conf.d/xcache.ini
[...]
xcache.size = 512M
[...]
如果是APC,方法類(lèi)似:
vi /etc/php5/conf.d/apc.ini
[...]
apc.shm_size="512"
[...]
修改后重新裝入PHP-FPM:
/etc/init.d/php5-fpm reload
#p# 3.3 PHP-FPM緊急設(shè)置
這更像是提高可靠性的設(shè)置,而不是提升性能的設(shè)置:如果PHP-FPM停止工作,它會(huì)重啟自己:
vi /etc/php5/fpm/php-fpm.conf
[...]
; 如果在emergency_restart_interval設(shè)定的時(shí)間間隔內(nèi),這個(gè)數(shù)量的子進(jìn)程以SIGSEGV或SIGBUS退出,
; 那么FPM會(huì)重啟。值為“o”意味著“斷開(kāi)”。
; 默認(rèn)值:0
emergency_restart_threshold = 10
; emergency_restart_interval所用的時(shí)間間隔決定了平穩(wěn)重啟何時(shí)開(kāi)始。
; 這有助于避開(kāi)方向感應(yīng)器的共享內(nèi)存里面偶爾出現(xiàn)的故障。
; 可用單位:s(秒)、m(分)、h(小時(shí))或d(天)
; 默認(rèn)單位:秒
; 默認(rèn)值:0
emergency_restart_interval = 1m
; 子進(jìn)程等待來(lái)自主進(jìn)程的信號(hào)有所反應(yīng)的時(shí)間限制。
; 可用單位:s(秒)、m(分)、h(小時(shí))或d(天)
; 默認(rèn)單位:秒
; 默認(rèn)值:0
process_control_timeout = 10s
[...]
#p# 3.4 如果PHP >= 5.3.9,使用ondemand進(jìn)程管理器
如果你使用PHP >= 5.3.9,可以使用PHP-FPM池中的ondemand進(jìn)程管理器,而不是靜態(tài)或動(dòng)態(tài),這將為你節(jié)省一些內(nèi)存:
[...]
pm = ondemandpm.max_children = 100pm.process_idle_timeout = 5s
[...]
3.5 使用Unix套接字,而不是TCP套接字
為了降低網(wǎng)絡(luò)開(kāi)銷(xiāo),你應(yī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
[...]
如果你變更這個(gè)必須,當(dāng)然必須調(diào)整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)關(guān)錯(cuò)誤
如果你使用Unix套接字和PHP-FPM,你可能會(huì)遇到忙碌網(wǎng)站隨機(jī)出現(xiàn)的502壞網(wǎng)關(guān)錯(cuò)誤。為了避免這個(gè)問(wèn)題,我們提高了允許連接至套接字的***數(shù)量。打開(kāi)/etc/sysctl.conf...
vi /etc/sysctl.conf
然后設(shè)置:
[...]
net.core.somaxconn = 4096
[...]
運(yùn)行:
sysctl -p
之后等變更生效。
#p# 4 調(diào)整MySQL
4.1 優(yōu)化你的my.cnf
你應(yīng)該使用mysqltuner.pl或tuning-primer.sh腳本,查看你應(yīng)該在my.cnf文件中調(diào)整哪些設(shè)置。最重要的變量之一是query_cache_size,如果你使用InnoDB表,
innodb_buffer_pool_size.
這是來(lái)自配備16GB內(nèi)存的測(cè)試服務(wù)器的示例配置,大約30個(gè)數(shù)據(jù)庫(kù)有一半是MyISAM表,另一半是是InnoDB表——這對(duì)于使用基準(zhǔn)測(cè)試工具(ab)進(jìn)行壓力測(cè)試的數(shù)據(jù)庫(kù)驅(qū)動(dòng)的測(cè)試網(wǎng)站來(lái)說(shuō)效果相當(dā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
[...]
請(qǐng)注意:如果你需要遵守ACID規(guī)定,必須將innodb_flush_log_at_trx_commit設(shè)成1。想了解這方面的更多信息,請(qǐng)?jiān)L問(wèn):http://dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit。
只要你在固態(tài)硬盤(pán)上使用MySQL,才應(yīng)該將innodb_io_capacity設(shè)成很高的值。如果你在普通硬盤(pán)上使用MySQL,***別理這一行。
4.2 使用固態(tài)硬盤(pán)
如果你在固態(tài)硬盤(pán)上使用MySQL,可以大幅提升性能,因?yàn)檫@大大降低磁盤(pán)的輸入/輸出。為此,最容易的辦法就是把/var/lib/mysql目錄掛載到固態(tài)硬盤(pán)上。
#p# 5 Web應(yīng)用程序緩存
許多Web應(yīng)用程序提供了創(chuàng)建整個(gè)頁(yè)面緩存的可能性,這個(gè)緩存存儲(chǔ)在硬盤(pán)上,可以直接被nginx訪問(wèn),那樣它就能繞開(kāi)整個(gè)PHP-MySQL堆棧架構(gòu)。這樣的Web應(yīng)用程序包括:WordPress(帶WP Super Cache或W3 Total Cache插件)、Drupal(帶Boost模塊)以及TYPO3(帶nc_staticfilecache擴(kuò)展)。這大幅提升了性能。
你還可以進(jìn)一步為靜態(tài)文件緩存提速,只要使用tmpfs文件系統(tǒng)把靜態(tài)文件緩存直接放入到服務(wù)器的內(nèi)存中:
《使用tmpfs,在內(nèi)存中存儲(chǔ)文件/目錄》(http://www.howtoforge.com/storing-files-directories-in-memory-with-tmpfs)
6 相關(guān)鏈接
•nginx維基:http://wiki.nginx.org/Main
•PHP:http://www.php.net/
•PHP-FPM:http://php-fpm.org/
•MySQL:http://www.mysql.com/