深度优化LNMP
为了体现优化效果,本文将优化之前和优化之后的效果保存以作对比。并且每做一步优化都会进行测试比较。 安装完linux,并安装了基本web服务软件nginx、mysql、php之后,优化之前的系统内存资源占用情况及进程明细,如下图1所示:
内存占用了274M
图1
优化之前的系统资源CPU利用率及平均负载情况,如下图2所示:
图2
load average: 0.51, 0.30, 0.12指的是 系统负载,即任务队列的平均长度,三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值。
Cpu(s): 0.1% us 用户空间占用CPU百分比
0.2% sy 内核空间占用CPU百分比
0.0% ni 用户进程空间内改变过优先级的进程占用CPU百分比
99.3.0% id 空闲CPU百分比
0.4% wa 等待输入输出的CPU时间百分比
Mem 和 Swap 中的buffers和cached分别指用作内核缓存的内存量及缓冲的交换区总量
优化之前的web并发负载情况,如下图3所示:模拟1000个客户端发送200000个请求,每分钟并发 5280.34,并且出现大量TIME_WAIT
图3
20万并发资源消耗情况如下图4 所示:
系统资源使用情况CPU 和 IO
CPU
磁盘
网络
图4
此时看一下nginx错误日志,出现大量failed (24: Too many open files) ,并且 /var/log/message 中也出现 possible SYN flooding on port 80. Sending cookies.
下面开始一步一步优化,将上面日志中的警告或错误解决掉,并且提高服务器并发负载能力轻松达到1万以上并发,让有限的资源发挥最大性能
好的,让我们开始吧!
内存占用了274M
图1
优化之前的系统资源CPU利用率及平均负载情况,如下图2所示:
图2
load average: 0.51, 0.30, 0.12指的是 系统负载,即任务队列的平均长度,三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值。
Cpu(s): 0.1% us 用户空间占用CPU百分比
0.2% sy 内核空间占用CPU百分比
0.0% ni 用户进程空间内改变过优先级的进程占用CPU百分比
99.3.0% id 空闲CPU百分比
0.4% wa 等待输入输出的CPU时间百分比
Mem 和 Swap 中的buffers和cached分别指用作内核缓存的内存量及缓冲的交换区总量
优化之前的web并发负载情况,如下图3所示:模拟1000个客户端发送200000个请求,每分钟并发 5280.34,并且出现大量TIME_WAIT
图3
20万并发资源消耗情况如下图4 所示:
系统资源使用情况CPU 和 IO
CPU
磁盘
网络
图4
此时看一下nginx错误日志,出现大量failed (24: Too many open files) ,并且 /var/log/message 中也出现 possible SYN flooding on port 80. Sending cookies.
下面开始一步一步优化,将上面日志中的警告或错误解决掉,并且提高服务器并发负载能力轻松达到1万以上并发,让有限的资源发挥最大性能
好的,让我们开始吧!
首先进行linux优化加固
Linux优化加固最好的办法就是提升硬件配置,比如提高CPU运算能力,增大内存容量,提高硬盘吞吐率等。
本文谈的Linux优化加固是在不提升硬件配置的情况下,通过优化内核配置,从而提高linux服务效率,从三个方面进行:安全加固、内核调优、优化网络,本文主要讲解内核调优及网络优化。
本文谈的Linux优化加固是在不提升硬件配置的情况下,通过优化内核配置,从而提高linux服务效率,从三个方面进行:安全加固、内核调优、优化网络,本文主要讲解内核调优及网络优化。
1优化linux 启动项
使用ntsysv工具将不需要的服务关闭
优化启动服务之前的内存使用情况:使用了 275M
图5
进行优化,默认启动服务可以只保留以下六项必要服务:
iptables sshd crond syslog network messagebus
同时也要保留 nginx、mysql、php默认启动
系统服务解释请参考我的这篇文章: http://www.cnblogs.com/buffer/p/3386346.html
优化之后内存使用情况,使用了224M,节省了近50m内存,不要小看这50M内存,关键时候可以起很大作用
图6
优化启动服务之前的内存使用情况:使用了 275M
图5
进行优化,默认启动服务可以只保留以下六项必要服务:
iptables sshd crond syslog network messagebus
同时也要保留 nginx、mysql、php默认启动
系统服务解释请参考我的这篇文章: http://www.cnblogs.com/buffer/p/3386346.html
优化之后内存使用情况,使用了224M,节省了近50m内存,不要小看这50M内存,关键时候可以起很大作用
图6
2 安全加固
Linux安全加固主要针对iptables进行,控制所有INPUT数据包,除了必要的端口打开之外,其余的端口一律关闭。禁用用户ping服务器等会更加安全。详情见LNMP优化准备工作—9配置防火墙(服务器安全优化)。
删除不必要的用户:
# cp /etc/passwd /etc/passwd.sav
# cp /etc/group /etc/group.sav
# for a in adm lp sync news uucp operator games gopher mailnull nscd rpc;
do /usr/sbin/userdel $a -f; done
# for a in lp news uucp games gopher users floopy nscd rpc rpcuser nfsnobody;
do /usr/sbin/groupdel $a ; done
删除不必要的用户:
# cp /etc/passwd /etc/passwd.sav
# cp /etc/group /etc/group.sav
# for a in adm lp sync news uucp operator games gopher mailnull nscd rpc;
do /usr/sbin/userdel $a -f; done
# for a in lp news uucp games gopher users floopy nscd rpc rpcuser nfsnobody;
do /usr/sbin/groupdel $a ; done
3网络优化
一般情况下,Nginx通过TCP socket来连接客户端与上游应用,默认安装的系统对TCP有许多门限值与限制,通过内核参数来设定。这些参数的默认值往往是为一般的用途而定的,并不能满足web服务器所需的高流量、短生命的要求。
对于网络参数调优可以修改 /etc/sysctl.conf 这个文件。修改完之后使用 #sysctl –p 让内核配置生效。
3.1 内核调优——调整TIMEWAIT,解决出现大量TIMEWAIT问题
在调整TIMEWAIT参数之前,先解释一下tcp链接请求和tcp关闭请求中的报文响应流程。如图7所示为一个完整的数据报格式。
图7
Tcp的链接及断开过程中间是经过一系列状态变换的。有这些状态:
建立链接状态:LISTEN,SYN-SENT,SYN-RECEIVED,ESTABLISHED
关闭链接状态:FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT 和 CLOSED。
各状态参数解释请参考我这篇文章:
http://blog.csdn.net/neubuffer/article/details/16853747
建立链接时 ,需要经过三次握手协议,建立链接时的握手协议如下图8所示。
对于网络参数调优可以修改 /etc/sysctl.conf 这个文件。修改完之后使用 #sysctl –p 让内核配置生效。
3.1 内核调优——调整TIMEWAIT,解决出现大量TIMEWAIT问题
在调整TIMEWAIT参数之前,先解释一下tcp链接请求和tcp关闭请求中的报文响应流程。如图7所示为一个完整的数据报格式。
图7
Tcp的链接及断开过程中间是经过一系列状态变换的。有这些状态:
建立链接状态:LISTEN,SYN-SENT,SYN-RECEIVED,ESTABLISHED
关闭链接状态:FIN-WAIT-1,FIN-WAIT-2,CLOSE-WAIT,CLOSING,LAST-ACK,TIME-WAIT 和 CLOSED。
各状态参数解释请参考我这篇文章:
http://blog.csdn.net/neubuffer/article/details/16853747
建立链接时 ,需要经过三次握手协议,建立链接时的握手协议如下图8所示。
|
|
|
|
图8
Tcp链接建立过程在服务器端不会产生TIME_WAIT状态,因此本文不作为重要知识点讲解。
断开链接时 ,需要经过四次握手协议,断开链接时的握手协议如下图9所示。
|
|
|
|
|
|
|
|
|
图9
Linux系统下,TCP连接主动断开后,会以TIME_WAIT状态保留一定的时间,然后才会释放端口。当并发请求过多的时候,就会产生大量的TIME_WAIT状态的连接,无法及时断开的话,会占用大量的端口资源和服务器资源。这个时候我们可以优化TCP的内核参数,来及时将TIME_WAIT状态的端口清理掉。
通过 ab.exe 模拟1000个客户端发起200000个并发请求,然后统计linux tcp等待个数,结果如下图10:
图10
从图7中可以看出,有24986个链接等待。
接下来我们优化一下TIMEWAIT,然后再统计。
# vi /etc/sysctl.conf
在文件中加入下面几行内容:
net.ipv4.tcp_syncookies = 1 开启SYN Cookies,当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭;
net.ipv4.tcp_fin_timeout = 30 修改系統默认的 TIMEOUT 时间,
查看系统默认timeout时间: # cat /proc/sys/net/ipv4/tcp_fin_timeout 默认为60秒。
接下来,优化tcp keepalive时间,在sysctl.conf中增加以下几行:
net.ipv4.tcp_keepalive_time = 1200 #TCP发送keepalive消息的频度。默认为2小时,改小。tcp链接在空闲 1200秒之后,内核发起关闭链接probe
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3 #如果probe 3次(每次30秒)不成功,内核才彻底放弃
接下来优化其它一些参数:
net.ipv4.ip_local_port_range = 1024 65000
#向外连接的端口范围。默认为32768到61000,范围改大
net.ipv4.tcp_max_syn_backlog = 2148
#SYN队列的长度。默认为1024,改大
net.ipv4.tcp_max_tw_buckets = 5000
#系统同时保持TIME_WAIT套接字的最大数量,超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。默认为180000
net.core.netdev_max_backlog = 1000
#进入包的最大设备队列,默认300,改大
net.core.somaxconn = 511 # socket的监听队列backlog的上限,默认值为128,二nginx的默认为511 限制了nginx发挥,NGX_LISTEN_BACKLOG 511
修改完之后,执行# /sbin/sysctl –p 让设置生效
关于网络参数详解及优化,请参考我这篇文章:
http://blog.csdn.net/neubuffer/article/details/16859829
也可以直接下载优化过的文件放到/etc 目录下: http://pan.baidu.com/s/1uyZ7s
重新通过 ab.exe 模拟1000个客户端发起200000个并发请求,然后统计linux tcp等待TIMEWAIT个数 netstat -tnp | awk '{print $6}' | sort | uniq -c ,结果如下图11:
图11
从图11中可以看出,链接等待数大量降低,节省了资源。
但是此时并发量,还是在6000多,没什么提升,这是为什么呢?
这时候看一下nginx的错误日志,# tail nginx/logs/error.log 结果如图12所示:
图12
从错误日志结果中可以看出,打开的文件太多,出现这个错误的原因主要是由于linux的打开文件描述符的数目过小造成的。
需要修改文件描述符限制。接下来优化内核文件描述符参数。
3.2 内核调优——调整文件描述符限制
查看用户文件描述符限制:
# ulimit –Hn 查看硬限制
# ulimit –Sn 查看软限制
增大文件描述符限制:
查看系统file-max参数,系统所有进程一共可以打开的文件句柄描述符数量
# cat /proc/sys/fs/file-max 结果为183972 在这里够用了不需要修改。
此值一般默认为系统内存的10%(系统内存以kb计算),一般够用,根据情况可以调大该值。通过编辑/etc/sysctl.conf 添加内容:fs.file-max=102400 来调整。
由此可见本实验中出现的问题由于用户软硬文件句柄数过小导致的,那么我们增加当前用户软硬文件句柄数:
# vi /etc/security/limits.conf 添加以下内容,表示限制所有用户打开文件句柄数的软硬限制到102400个。你也可以根据系统需要,限制某个用户。
* hard nofile 102400
* soft nofile 102400
保存,重启linux让刚才的修改生效。
File-max 和 ulimit 的区别是前者指的系统所有文件描述符限制,针对整个系统;而后者指的是用户的文件描述符限制,针对的是用户。
重新通过 ab.exe 模拟1000个客户端发起200000个并发请求,然后统计linux tcp等待个数 netstat -tnp | awk '{print $6}' | sort | uniq -c ,结果如下图13:
图13
看一下系统各项性能指标,如图14所示:
系统整体CPU 及 IO
CPU
磁盘
网络
图14
从结果中可以看出,与优化前相比没有太大提升。
这时候再看一下nginx的错误日志,# tail nginx/logs/error.log 还是有打开文件过多错误。但是,这时候看一下 /var/log/message 里面已经没有警告了。
继续找原因,猜想是不是nginx的限制呢?
进行nginx优化
前面修改了linux的文件描述符限制及linux内核参数,接下来优化nginx:
修改 # vi nginx/conf/nginx.conf
1 增加nginx工作进程数并绑定到不同的CPU上,本文所用的硬件有4个CPU,顾开辟4个进程,如下:
worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000; #绑定进程和CPU对应
2 增加每个进程打开文件句柄数,理论值应该是最多打开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀,所以最好与ulimit -n的值保持相当。
worker_rlimit_nofile 50000;
3 采用epoll 模式,并增大每个进程连接数限制:
events {
use epoll; //异步非阻塞I/O模型
worker_connections 204800;
}
4 设置客户端请求头部缓冲区大小为系统分页大小的整数倍,可以用命令查看系统分页大小:# getconf PAGESIZE 。 如果经常出现 400错误,则和值有关,调大即可。
http{
client_header_buffer_size 4k;
large_client_header_buffers 4 4k;
}
5 打开请求文件缓存
http{
open_file_cache max=102400 inactive=60s;
说明:打开文件指定缓存,默认是没有启用的,max 指定缓存数量,建议和打开文件数一致,inactive 是指经过60s时间文件没被请求后删除缓存。
open_file_cache_valid 80s;
说明:80s 检查一次缓存的有效信息。
open_file_cache_min_uses 1;
说明:文件使用次数,判断有效与否。inactive 时间内一次没被使用,它将被移除。
}
重启nginx:# service nginx restart
再次通过 ab 模拟1000个客户端发起200000个并发请求,然后统计linux tcp等待个数 netstat -tnp | awk '{print $6}' | sort | uniq -c ,结果如下图15:
图15
看一下系统资源利用情况,如下图16所示:
系统整体CPU及IO
CPU
磁盘
网络
图16
由以上结果可见,优化之后并发达到1.1万多。并且TIMEWAIT数量大大减少,性能有明显提升。
实际上此时并发可以达到将近2w,由于ab并发性能成为瓶颈。我们可以开3个ab,每个ab分别并发1000发送20万个请求。实验结果如下图17所示:
图17 每个并发达到0.62万 总共并发大于1.8万
系统资源利用情况如下图18所示:
系统整体CPU及IO
CPU
磁盘
网络
内存空闲率
图18
由结果可知,优化之后,只用了不到200M内存,并发请求达到近2W,系统并发有了很大提升。其中,三个AB程序占了200M内存中的近50%。
修改 # vi nginx/conf/nginx.conf
1 增加nginx工作进程数并绑定到不同的CPU上,本文所用的硬件有4个CPU,顾开辟4个进程,如下:
worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000; #绑定进程和CPU对应
2 增加每个进程打开文件句柄数,理论值应该是最多打开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀,所以最好与ulimit -n的值保持相当。
worker_rlimit_nofile 50000;
3 采用epoll 模式,并增大每个进程连接数限制:
events {
use epoll; //异步非阻塞I/O模型
worker_connections 204800;
}
4 设置客户端请求头部缓冲区大小为系统分页大小的整数倍,可以用命令查看系统分页大小:# getconf PAGESIZE 。 如果经常出现 400错误,则和值有关,调大即可。
http{
client_header_buffer_size 4k;
large_client_header_buffers 4 4k;
}
5 打开请求文件缓存
http{
open_file_cache max=102400 inactive=60s;
说明:打开文件指定缓存,默认是没有启用的,max 指定缓存数量,建议和打开文件数一致,inactive 是指经过60s时间文件没被请求后删除缓存。
open_file_cache_valid 80s;
说明:80s 检查一次缓存的有效信息。
open_file_cache_min_uses 1;
说明:文件使用次数,判断有效与否。inactive 时间内一次没被使用,它将被移除。
}
重启nginx:# service nginx restart
再次通过 ab 模拟1000个客户端发起200000个并发请求,然后统计linux tcp等待个数 netstat -tnp | awk '{print $6}' | sort | uniq -c ,结果如下图15:
图15
看一下系统资源利用情况,如下图16所示:
系统整体CPU及IO
CPU
磁盘
网络
图16
由以上结果可见,优化之后并发达到1.1万多。并且TIMEWAIT数量大大减少,性能有明显提升。
实际上此时并发可以达到将近2w,由于ab并发性能成为瓶颈。我们可以开3个ab,每个ab分别并发1000发送20万个请求。实验结果如下图17所示:
图17 每个并发达到0.62万 总共并发大于1.8万
系统资源利用情况如下图18所示:
系统整体CPU及IO
CPU
磁盘
网络
内存空闲率
图18
由结果可知,优化之后,只用了不到200M内存,并发请求达到近2W,系统并发有了很大提升。其中,三个AB程序占了200M内存中的近50%。
进行mysql优化
提升mysql性能需要从三个方面着手,首先是硬件,提升硬件配置效果最明显;其次是mysql参数配置优化;还有数据读写架构优化。本文主要讲mysql参数配置优化,顺便解释一下硬件配置和程序中使用mysql架构优化问题。
对于硬件配置这不用说,任何服务都是基于硬件之上的,提升硬件配置,原则上会提升服务效率,比如使用更大内存,使用磁盘高级raid,数据分区使用固态IO卡,使用xfs文件系统等。
对于mysql架构设计优化,这个非常重要,sql语句优化,表存储引擎(MyISAM,
InnoDB)选择,使用索引,增加redis或者memcached缓存层等。将传统web服务的2层架构(webserver+db)变成三层架构(webserver+cache+db)甚至四层架构(webserver+logicserver+cache+db),由cache来承担分流大并发读写操作。
对于存储引擎选择有2个原则:第一个原则,大量读少量写 选用MyISAM,大量写少量读选用InnoDB。针对不同的需求使用不同的存储引擎。第二个原则,能不用InnoDB尽量不用InnoDB。总之,如果你想追求99.9%的稳定性,方便的扩展性和高可用性还是尽量用MyISAM吧。
至于为什么呢?
1 MyISAM的索引和数据是分开的,并且索引是有压缩的,内存使用率就对应提高了不少。能加载更多索引,而InnoDB是索引和数据是紧密捆绑的,没有使用压缩从而会造成InnoDB比MyISAM体积庞大不少。
2 InnoDB的行级锁是相对的,那个只有where主键时是有效,非主键的都会锁全表的。如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”
3 MyISAM相对简单所以在效率上要优于InnoDB。小型应用使用MyISAM是不错的选择。另外,MyISAM表是保存成文件的形式,在跨平台的数据转移很方便。
总之,尽量不使用InnoDB,InnoDB主要用于需要外键,事务等企业级支持,代价是速度比MyISAM有倍数的下降。
下面主要讲mysql参数配置优化:
准备一个工具: http://pan.baidu.com/s/1M8Ec tuning-primer.sh mysql性能报告工具。
在开始优化之前先测试一下当前mysql并发情况及系统资源消耗情况。用mysql自带的并发测试工具,测试myisam及innodb引擎,开启100个并发分别写入20000条数据并且查询20000次。统计消耗时间及系统资源使用情况。
下图19结果展示了分别读写myisam和innodb效率:
图19
下图20是对应的资源消耗情况:
系统整体CPU及IO
CPU
磁盘
内存
图20
通过结果可以看出mysql占用内存太高。写入2万次myisam引擎平均需要2秒钟,innodb平均需要70秒钟,写入时瓶颈出现在IO。读取2万次 两种引擎需要时间相当,4.3秒左右,读取瓶颈出现在CPU。
接下来通过运行tuning-primer.sh工具,获取初步配置建议:
# chmod +x tuning-primer.sh
# sh tuning-primer.sh 根据提示继续
结果中红色部分是当前配置不恰当的地方,根据实际情况进行优化。
接下来我们开始一步一步优化 /etc/my.cnf
本文重点以优化MyISAM 引擎为例。在[mysqld]部分增加以下内容:
1关闭innodb引擎
default-storage-engine=MYISAM
innodb=OFF
2 开启慢查询日志记录,便于抓出查询速度较慢的语句进行优化查询。
long_query_time=2 #当某个查询时间超过2s时记录下来
log-slow-queries= /usr/local/webserver/mysql/logs/slowquery.log
3 调整max_connections
允许的同时链接的客户端数量,默认数值是100。如果经常看到 Too many connections 错误,则增大该值。小型服务器一般默认100就够了,本文这里设置为200
3 优化myisam 写入效率。
分别并发100,200,写入10万次,统计系统资源使用情况,如图21所示:
bin/mysqlslap --concurrency=100 --iterations=1 --number-int-cols=4 --number-char-cols=35 --auto-generate-sql --auto-generate-sql-add-autoincrement --auto-generate-sql-load-type=write --engine=myisam --number-of-queries=100000 --debug-info --verbose -uroot -predis123
并发100系统整体CPU及IO 并发100空闲内存
并发200系统整体CPU及IO 并发200空闲内存
图21
由上图资源使用情况可见,myisam写入的主要瓶颈在系统CPU和IO,所以通过配置参数来做写入优化提升空间有限,这时候就得考虑做数据库集群了。
4 优化myisam 读取效率
分别并发100,200,读取10万次,统计系统资源使用情况,如图22所示:
bin/mysqlslap --concurrency=100 --iterations=1 --number-int-cols=4 --number-char-cols=35 --auto-generate-sql --auto-generate-sql-add-autoincrement --auto-generate-sql-load-type=read --engine=myisam --number-of-queries=100000 --debug-info --verbose -uroot -predis123
并发100系统整体CPU及IO 并发100空闲内存
并发200系统整体CPU及IO 并发200空闲内存
图23
由上图资源使用情况,myisam并发读主要瓶颈在CPU,且内存空闲率也比较多。下面通过优化myisam参数,提升并发读效率。修改 /etc/my.cnf mysql配置文件。
4.1 增加 key_buffer_size
用于索引块的缓冲区大小,增加它可更好处理索引,如果只是使用MyISAM表,可以把它设置为可用内存的 30-40%。具体根据mysql当前负载情况设置,要看Key_reads和 Key_read_requests比例,如果Key_reads 从硬盘读取键的数据块的次数。如果Key_reads较大,则Key_buffer_size值可能太小。可以用Key_reads/Key_read_requests计算缓存损失率。
本文将key_buffer_size设置为 256M
4.2 增加read_buffer_size大小
顺序读取数据时的缓冲区大小,该参数分配的内存也是以每连接为单位的。read_buffer_size 是用来当需要顺序读取数据的时候,无法使用索引的情况下的全表扫描,全索引扫描等情况下,会按照数据的存储顺序依次读取数据块,每次读取的数据快首先会暂存在 read_buffer_size 中,当 buffer 空间被写满或者全部数据读取结束后,再将 buffer 中的数据返回给上层调用者,以提高效率。
本文将read_buffer_size 设置为 2M。
4.3 增加 query_cache_size 大小
该参数对应 缓存sql语句及其结果的功能。在下次接收到同样的查询请求时,不再执行实际查询处理而直接返 回结果,有这样的查询缓存能提高查询的速度,使查询性能得到优化,前提条件是你有大量的相同或相似的查询,而很少改变表里的数据,否则没有必要使用此功 能。注意:如果你查询的表更新比较频繁,而且很少有 相同的查询,最好不要使用查询缓存。设置完之后最好是跟踪一段时间,查看是否运行良好。在一定的负载压力下,如果缓存命中率太低了,就启用它。
本文将query_cache_size设置为32M
4.4另外还有其它参数,根据系统情况调整。
thread_cache — 线程的创建和销毁的开销可能很大,因为每个线程的连接/断开都需要。我通常至少设置为 16。
table_cache — 缓存打开的表。通常要加大缓存数量,使得足以最大限度地缓存打开的表。如果你有200多个表的话,那么设置为 1024 也许比较合适(每个线程都需要打开表),如果连接数比较大那么就加大它的值。
关于INNODB的参数本文暂不作讲解。
保存以上修改,重启mysql,同样分别并发100,200 读取10万次,统计消耗时间,及系统资源使用情况,如下图24所示。
并发100系统整体CPU及IO 并发100空闲内存
并发200系统整体CPU及IO 并发200空闲内存
图24
由此可见,优化之后与优化之前对比,myisam整体读取性能有了很大提升。
对于硬件配置这不用说,任何服务都是基于硬件之上的,提升硬件配置,原则上会提升服务效率,比如使用更大内存,使用磁盘高级raid,数据分区使用固态IO卡,使用xfs文件系统等。
对于mysql架构设计优化,这个非常重要,sql语句优化,表存储引擎(MyISAM,
InnoDB)选择,使用索引,增加redis或者memcached缓存层等。将传统web服务的2层架构(webserver+db)变成三层架构(webserver+cache+db)甚至四层架构(webserver+logicserver+cache+db),由cache来承担分流大并发读写操作。
对于存储引擎选择有2个原则:第一个原则,大量读少量写 选用MyISAM,大量写少量读选用InnoDB。针对不同的需求使用不同的存储引擎。第二个原则,能不用InnoDB尽量不用InnoDB。总之,如果你想追求99.9%的稳定性,方便的扩展性和高可用性还是尽量用MyISAM吧。
至于为什么呢?
1 MyISAM的索引和数据是分开的,并且索引是有压缩的,内存使用率就对应提高了不少。能加载更多索引,而InnoDB是索引和数据是紧密捆绑的,没有使用压缩从而会造成InnoDB比MyISAM体积庞大不少。
2 InnoDB的行级锁是相对的,那个只有where主键时是有效,非主键的都会锁全表的。如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”
3 MyISAM相对简单所以在效率上要优于InnoDB。小型应用使用MyISAM是不错的选择。另外,MyISAM表是保存成文件的形式,在跨平台的数据转移很方便。
总之,尽量不使用InnoDB,InnoDB主要用于需要外键,事务等企业级支持,代价是速度比MyISAM有倍数的下降。
下面主要讲mysql参数配置优化:
准备一个工具: http://pan.baidu.com/s/1M8Ec tuning-primer.sh mysql性能报告工具。
在开始优化之前先测试一下当前mysql并发情况及系统资源消耗情况。用mysql自带的并发测试工具,测试myisam及innodb引擎,开启100个并发分别写入20000条数据并且查询20000次。统计消耗时间及系统资源使用情况。
下图19结果展示了分别读写myisam和innodb效率:
图19
下图20是对应的资源消耗情况:
系统整体CPU及IO
CPU
磁盘
内存
图20
通过结果可以看出mysql占用内存太高。写入2万次myisam引擎平均需要2秒钟,innodb平均需要70秒钟,写入时瓶颈出现在IO。读取2万次 两种引擎需要时间相当,4.3秒左右,读取瓶颈出现在CPU。
接下来通过运行tuning-primer.sh工具,获取初步配置建议:
# chmod +x tuning-primer.sh
# sh tuning-primer.sh 根据提示继续
结果中红色部分是当前配置不恰当的地方,根据实际情况进行优化。
接下来我们开始一步一步优化 /etc/my.cnf
本文重点以优化MyISAM 引擎为例。在[mysqld]部分增加以下内容:
1关闭innodb引擎
default-storage-engine=MYISAM
innodb=OFF
2 开启慢查询日志记录,便于抓出查询速度较慢的语句进行优化查询。
long_query_time=2 #当某个查询时间超过2s时记录下来
log-slow-queries= /usr/local/webserver/mysql/logs/slowquery.log
3 调整max_connections
允许的同时链接的客户端数量,默认数值是100。如果经常看到 Too many connections 错误,则增大该值。小型服务器一般默认100就够了,本文这里设置为200
3 优化myisam 写入效率。
分别并发100,200,写入10万次,统计系统资源使用情况,如图21所示:
bin/mysqlslap --concurrency=100 --iterations=1 --number-int-cols=4 --number-char-cols=35 --auto-generate-sql --auto-generate-sql-add-autoincrement --auto-generate-sql-load-type=write --engine=myisam --number-of-queries=100000 --debug-info --verbose -uroot -predis123
并发100系统整体CPU及IO 并发100空闲内存
并发200系统整体CPU及IO 并发200空闲内存
图21
由上图资源使用情况可见,myisam写入的主要瓶颈在系统CPU和IO,所以通过配置参数来做写入优化提升空间有限,这时候就得考虑做数据库集群了。
4 优化myisam 读取效率
分别并发100,200,读取10万次,统计系统资源使用情况,如图22所示:
bin/mysqlslap --concurrency=100 --iterations=1 --number-int-cols=4 --number-char-cols=35 --auto-generate-sql --auto-generate-sql-add-autoincrement --auto-generate-sql-load-type=read --engine=myisam --number-of-queries=100000 --debug-info --verbose -uroot -predis123
并发100系统整体CPU及IO 并发100空闲内存
并发200系统整体CPU及IO 并发200空闲内存
图23
由上图资源使用情况,myisam并发读主要瓶颈在CPU,且内存空闲率也比较多。下面通过优化myisam参数,提升并发读效率。修改 /etc/my.cnf mysql配置文件。
4.1 增加 key_buffer_size
用于索引块的缓冲区大小,增加它可更好处理索引,如果只是使用MyISAM表,可以把它设置为可用内存的 30-40%。具体根据mysql当前负载情况设置,要看Key_reads和 Key_read_requests比例,如果Key_reads 从硬盘读取键的数据块的次数。如果Key_reads较大,则Key_buffer_size值可能太小。可以用Key_reads/Key_read_requests计算缓存损失率。
本文将key_buffer_size设置为 256M
4.2 增加read_buffer_size大小
顺序读取数据时的缓冲区大小,该参数分配的内存也是以每连接为单位的。read_buffer_size 是用来当需要顺序读取数据的时候,无法使用索引的情况下的全表扫描,全索引扫描等情况下,会按照数据的存储顺序依次读取数据块,每次读取的数据快首先会暂存在 read_buffer_size 中,当 buffer 空间被写满或者全部数据读取结束后,再将 buffer 中的数据返回给上层调用者,以提高效率。
本文将read_buffer_size 设置为 2M。
4.3 增加 query_cache_size 大小
该参数对应 缓存sql语句及其结果的功能。在下次接收到同样的查询请求时,不再执行实际查询处理而直接返 回结果,有这样的查询缓存能提高查询的速度,使查询性能得到优化,前提条件是你有大量的相同或相似的查询,而很少改变表里的数据,否则没有必要使用此功 能。注意:如果你查询的表更新比较频繁,而且很少有 相同的查询,最好不要使用查询缓存。设置完之后最好是跟踪一段时间,查看是否运行良好。在一定的负载压力下,如果缓存命中率太低了,就启用它。
本文将query_cache_size设置为32M
4.4另外还有其它参数,根据系统情况调整。
thread_cache — 线程的创建和销毁的开销可能很大,因为每个线程的连接/断开都需要。我通常至少设置为 16。
table_cache — 缓存打开的表。通常要加大缓存数量,使得足以最大限度地缓存打开的表。如果你有200多个表的话,那么设置为 1024 也许比较合适(每个线程都需要打开表),如果连接数比较大那么就加大它的值。
关于INNODB的参数本文暂不作讲解。
保存以上修改,重启mysql,同样分别并发100,200 读取10万次,统计消耗时间,及系统资源使用情况,如下图24所示。
并发100系统整体CPU及IO 并发100空闲内存
并发200系统整体CPU及IO 并发200空闲内存
图24
由此可见,优化之后与优化之前对比,myisam整体读取性能有了很大提升。
进行php优化
对于一个web服务来说,除去mysql瓶颈,最影响性能的瓶颈是php等动态语言。而数据库瓶颈,可以通过第三方解决方案缓解,比如采用分布式数据库,采用内存数据库加速等。解决了数据库瓶颈,接下来就要解决动态语言php瓶颈了。好的解决办法无非这几种:
1 页面静态化,尽量避免请求动态解析。
2 采用第三方php加速器,通过缓存PHP代码编译后的结果来提高PHP脚本的性能,优化PHP代码执行速度,降低服务器负载,可以提高PHP应用执行速度最高达几倍,甚至十几倍。比较有名的加速器有,Zend Opcache,xcache,eAccelerator。
在php 5.5 及以上的版本中已经内置了Zend Opcache,而本文用的php 5.5.5,因此不需要做这步优化了。
3 通过配置php参数,充分利用系统硬件资源,从而提高服务效率。本文主要讲解这一部分。
在优化之前还是做个测试,并统计系统资源使用情况。
首先编写一个php页面,使用ab工具,启动1000个并发,发起1万个请求。
测试结果如下图25所示。
系统整体CPU及IO情况内存
图25
每秒系统处理请求 166.34个并发,非常低。并且通过资源消耗情况看,在请求开始的前10s CPU使用率非常高,接下来接着CPU使用率突降,IO一直波动。另外,内存使用一直较低。
接下来查看一下系统日志及nginx错误日志:
# tail /var/log/message
系统提示类似洪水攻击。
Nginx错误日志中则出现大量链接php错误
另外,TIME_WAIT数也相对过高
由此推断,php连接数配置过低?
下面开始优化php配置,vi php/etc/php-fpm.conf
1 增大php-fpm对打开文件描述符的限制
rlimit_files = 65536
2设置允许访问Fastcgi进程解析器的IP地址,更加安全
listen.allowed_clients = 127.0.0.1
3 增大请求缓冲队列大小
listen.backlog = 2048 默认为128,有点小,增大这个参数,可以解决系统日志中攻击提示问题以及nginx错误日志中的链接错误。
4 pm.max_requests = 1024由 500改为1024
此时再做测试,结果如下图26所示:
并发提升了,达到882.28个每秒,比优化前的166.34 提升了5倍之多,并且TIME_WAIT数量也大大减少,降到合理范围内。
系统整体CPU及IO使用情况
内存使用情况
图26
继续优化,达到并发5000以上,才告一段落:
在nginx配置中使用fast_cgi缓存,在http段加入以下内容:
fastcgi_temp_path /dev/shm/fastcgi_temp;
#cache设置
fastcgi_cache_path /dev/shm/fastcgi_cache levels=1:2
keys_zone=cfcache:10m inactive=50m max_size=256;
//为FastCGI缓存指定一个路径,目录结构等级,关键字区域存储时间和非活动删除时间。以及最大占用空间。
fastcgi_cache_key "$request_method://$host$request_uri";
fastcgi_cache_methods GET HEAD;
fastcgi_cache cfcache; //开启FastCGI缓存并且为其制定一个名称。
fastcgi_cache_valid 200 302 301 1h;
fastcgi_cache_valid any 5m; //为指定应答代码指定缓存时间,这里指定200 302 301应答缓存1小时,其余任何应答缓存5分钟
fastcgi_cache_min_uses 1; //缓存在fastcgi_cache_path内文件在inactive参数值时间内的最少使用次数,如上例,这里在50分钟内某文件1次也没有被使用,那么这个文件将被移除。
fastcgi_cache_use_stale error timeout invalid_header http_500;//对于error timeout invalid_header http_500等类型的应答内容不缓存
fastcgi_ignore_client_abort on;//忽略客户端终止请求
重启nginx ,并发1000发送10万个请求,统计执行效率及系统资源消耗情况。如下图27所示。
图27
由测试结果可知,优化之后并发达到 6004.95个。系统CPU及IO利用率也比较合理,内存占用较小,整体比较理想。
优化总结
本文采用的测试机是普通的2G内存笔记本,再做优化没有多大意义,实际生产环境中在运行着大并发请求的真实服务器上进行优化比较准确。本文Lnmp优化到此告一段落吧。
通过合理架构,合理代码逻辑,经过优化之后服务轻松可以达到并发1-2w。