linux 百万并发连接之内核优化tcp_mem

在服务端,连接达到一定数量,诸如50W时,有些隐藏很深的问题,就不断的抛出来。 通过查看dmesg命令查看,发现大量TCP: too many of orphaned sockets错误,也很正常,下面到了需要调整tcp socket参数的时候了。

第一个需要调整的是tcp_rmem,即TCP读取缓冲区,单位为字节,查看默认值

  1. cat /proc/sys/net/ipv4/tcp_rmem
  2. 4096 87380 4161536

默认值为87380 byte ≈ 86K,最小为4096 byte=4K,最大值为4064K。

第二个需要调整的是tcp_wmem,发送缓冲区,单位是字节,默认值

  1. cat /proc/sys/net/ipv4/tcp_wmem
  2. 4096 16384 4161536

解释同上

第三个需要调整的tcp_mem,调整TCP的内存大小,其单位是页,1页等于4096字节。系统默认值:

  1. cat /proc/sys/net/ipv4/tcp_mem
  2. 932448 1243264 1864896

tcp_mem(3个INTEGER变量):low, pressure, high

  • low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。
  • pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出pressure状态。
  • high:允许所有tcp sockets用于排队缓冲数据报的页面量,当内存占用超过此值,系统拒绝分配socket,后台日志输出“TCP: too many of orphaned sockets”。

一般情况下这些值是在系统启动时根据系统内存数量计算得到的。 根据当前tcp_mem最大内存页面数是1864896,当内存为(1864896*4)/1024K=7284.75M时,系统将无法为新的socket连接分配内存,即TCP连接将被拒绝。

实际测试环境中,据观察大概在99万个连接左右的时候(零头不算),进程被杀死,触发out of socket memory错误(dmesg命令查看获得)。每一个连接大致占用7.5K内存(下面给出计算方式),大致可算的此时内存占用情况(990000 * 7.5 / 1024K = 7251M)。

这样和tcp_mem最大页面值数量比较吻合,因此此值也需要修改。

三个TCP调整语句为:

  1. echo “net.ipv4.tcp_mem = 786432 2097152 3145728”>> /etc/sysctl.conf
  2. echo “net.ipv4.tcp_rmem = 4096 4096 16777216”>> /etc/sysctl.conf
  3. echo “net.ipv4.tcp_wmem = 4096 4096 16777216”>> /etc/sysctl.conf

备注: 为了节省内存,设置tcp读、写缓冲区都为4K大小,tcp_mem三个值分别为3G 8G 16G,tcp_rmemtcp_wmem最大值也是16G。

目标达成

经过若干次的尝试,最终达到目标,1024000个持久连接。1024000数字是怎么得来的呢,两台物理机器各自发出64000个请求,两个配置为6G左右的centos测试端机器(绑定7个桥接或NAT连接)各自发出640007 = 448000。也就是 1024000 = (64000) + (64000) + (640007) + (64000*7), 共使用了16个网卡(物理网卡+虚拟网卡)。
终端输出

  1. ……
  2. online user 1023990
  3. online user 1023991
  4. online user 1023992
  5. online user 1023993
  6. online user 1023994
  7. online user 1023995
  8. online user 1023996
  9. online user 1023997
  10. online user 1023998
  11. online user 1023999
  12. online user 1024000

在线用户目标达到1024000个!

服务器状态信息

服务启动时内存占用:

  1. total used free shared buffers cached
  2. Mem: 10442 271 10171 0 22 78
  3. -/+ buffers/cache: 171 10271
  4. Swap: 8127 0 8127

系统达到1024000个连接后的内存情况(执行三次 free -m 命令,获取三次结果):

  1. total used free shared buffers cached
  2. Mem: 10442 7781 2661 0 22 78
  3. -/+ buffers/cache: 7680 2762
  4. Swap: 8127 0 8127
  5. total used free shared buffers cached
  6. Mem: 10442 7793 2649 0 22 78
  7. -/+ buffers/cache: 7692 2750
  8. Swap: 8127 0 8127
  9. total used free shared buffers cached
  10. Mem: 10442 7804 2638 0 22 79
  11. -/+ buffers/cache: 7702 2740
  12. Swap: 8127 0 8127

这三次内存使用分别是7680,7692,7702,这次不取平均值,取一个中等偏上的值,定为7701M。那么程序接收1024000个连接,共消耗了 7701M-171M = 7530M内存, 7530M*1024K / 1024000 = 7.53K, 每一个连接消耗内存在为7.5K左右,这和在连接达到512000时所计算较为吻合。
虚拟机运行Centos内存占用,不太稳定,但一般相差不大,以上数值,仅供参考。

执行top -p 某刻输出信息:

  1. top – 17:23:17 up 18 min, 4 users, load average: 0.33, 0.12, 0.11
  2. Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
  3. Cpu(s): 0.2%us, 6.3%sy, 0.0%ni, 80.2%id, 0.0%wa, 4.5%hi, 8.8%si, 0.0%st
  4. Mem: 10693580k total, 6479980k used, 4213600k free, 22916k buffers
  5. Swap: 8323056k total, 0k used, 8323056k free, 80360k cached
  6. PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
  7. 2924 yongboy 20 0 82776 74m 508 R 51.3 0.7 3:53.95 server

执行vmstate:

  1. vmstat
  2. procs ———–memory———- —swap– —–io—- –system– —–cpu—–
  3. r b swpd free buff cache si so bi bo in cs us sy id wa st
  4. 0 0 0 2725572 23008 80360 0 0 21 2 1012 894 0 9 89 2 0

获取当前socket连接状态统计信息:

  1. cat /proc/net/sockstat
  2. sockets: used 1024380
  3. TCP: inuse 1024009 orphan 0 tw 0 alloc 1024014 mem 2
  4. UDP: inuse 11 mem 1
  5. UDPLITE: inuse 0
  6. RAW: inuse 0
  7. FRAG: inuse 0 memory 0

获取当前系统打开的文件句柄:

  1. sysctl -a | grep file
  2. fs.file-nr = 1025216 0 1048576
  3. fs.file-max = 1048576

此时任何类似于下面查询操作都是一个慢,等待若干时间还不见得执行完毕。

  1. netstat -nat|grep -i “8000”|grep ESTABLISHED|wc -l
  2. netstat -n | grep -i “8000” | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’

以上两个命令在二三十分钟过去了,还未执行完毕,只好停止。

小结

本次从头到尾的测试,所需要有的linux系统需要调整的参数也就是那么几个,汇总一下:

  1. echo “* – nofile 1048576” >> /etc/security/limits.conf
  2. echo “fs.file-max = 1048576” >> /etc/sysctl.conf
  3. echo “net.ipv4.ip_local_port_range = 1024 65535” >> /etc/sysctl.conf
  4. echo “net.ipv4.tcp_mem = 786432 2097152 3145728” >> /etc/sysctl.conf
  5. echo “net.ipv4.tcp_rmem = 4096 4096 16777216” >> /etc/sysctl.conf
  6. echo “net.ipv4.tcp_wmem = 4096 4096 16777216” >> /etc/sysctl.conf

其它没有调整的参数,仅仅因为它们暂时对本次测试没有带来什么影响,实际环境中需要结合需要调整类似于SO_KEEPALIVE、tcpmax_orphans等大量参数。

开放DNS速度测试

[root@2 ~]# time for i in `seq 1 100`;do dig @8.8.8.8 www.alidns.com > /dev/null 2>&1; done

real    0m14.733s
user    0m0.227s
sys     0m0.302s
[root@2 ~]# time for i in `seq 1 100`;do dig @119.29.29.29 www.alidns.com > /dev/null 2>&1; done

real    0m14.249s
user    0m0.224s
sys     0m0.327s
[root@2 ~]# time for i in `seq 1 100`;do dig @114.114.114.114 www.alidns.com > /dev/null 2>&1; done

real    0m14.856s
user    0m0.235s
sys     0m0.312s
[root@2 ~]# time for i in `seq 1 100`;do dig @223.5.5.5 www.alidns.com > /dev/null 2>&1; done

real    0m13.729s
user    0m0.235s
sys     0m0.309s
[root@2 ~]# time for i in `seq 1 100`;do dig @180.76.76.76 www.alidns.com > /dev/null 2>&1; done

real    1m8.154s
user    0m0.205s
sys     0m0.326s
[root@2 ~]# time for i in `seq 1 100`;do dig @101.226.4.6 www.alidns.com > /dev/null 2>&1; done

real    0m16.814s
user    0m0.225s
sys     0m0.307s
[root@2 ~]# time for i in `seq 1 100`;do dig @1.2.4.8 www.alidns.com > /dev/null 2>&1; done

real    0m14.493s
user    0m0.215s
sys     0m0.317s
[root@2 ~]# time for i in `seq 1 100`;do dig @208.67.222.222 www.alidns.com > /dev/null 2>&1; done

real    0m47.309s
user    0m0.228s
sys     0m0.326s
[root@2 ~]# time for i in `seq 1 100`;do dig @208.67.222.222 www.alidns.com > /dev/null 2>&1; done
c^C
real    1m36.730s
user    0m0.214s
sys     0m0.320s
[root@2 ~]# time for i in `seq 1 100`;do dig @4.2.2.1 www.alidns.com > /dev/null 2>&1; done

real    0m38.440s
user    0m0.224s
sys     0m0.303s
[root@2 ~]# time for i in `seq 1 100`;do dig @1.1.1.1 www.alidns.com > /dev/null 2>&1; done

real    0m27.393s
user    0m0.239s
sys     0m0.292s

阿里公共DNS223.5.5.5 和 223.6.6.6​的问题

阿里公共DNS 223.5.5.5 和 223.6.6.6是阿里巴巴集团推出的DNS递归解析系统,目标是成为国内互联网基础设施的组成部分,面向互联网用户提供“快速”、“稳定”、“智能”的免费DNS递归解析服务

通过批量测试发现,223.5.5.5 和 223.6.6.6解析不常用域名超慢,达到2-6秒甚至超时解析。这个不知道阿里DNS是什么时候改的,同样对比腾讯119.29.29.29及114DNS 114.114.114.114 及google DNS 8.8.8.8没有这个问题。


批量测试100次,看看返回结果时间:

[root@2 ~]# time for i in `seq 1 100`;do dig @223.6.6.6 www.dnsdizhi.com +short > /dev/null 2>&1; done

real    0m51.319s
user    0m0.215s
sys     0m0.316s
[root@2 ~]# time for i in `seq 1 100`;do dig @223.6.6.6 www.dnsdizhi.com +short > /dev/null 2>&1; done

real    0m3.533s
user    0m0.194s
sys     0m0.346s
[root@2 ~]# time for i in `seq 1 100`;do dig @223.6.6.6 www.dnsdizhi.com +short > /dev/null 2>&1; done

real    0m1.161s
user    0m0.221s
sys     0m0.328s
[root@2 ~]# time for i in `seq 1 100`;do dig @223.6.6.6 www.dnsdizhi.com +short > /dev/null 2>&1; done

real    0m1.135s
user    0m0.210s
sys     0m0.308s


第一个100次,需要51秒,而后面都非常快。


我们详细看看前100次域名解析耗时情况:

第1-10次:

[root@2 ~]# time for i in `seq 1 10`;do time dig @223.6.6.6 www.dnsdizhi.com +short; done
119.28.12.243

real    0m1.134s
user    0m0.004s
sys     0m0.002s

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.el6_10.1 <<>> @223.6.6.6 www.dnsdizhi.com +short
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached

real    0m6.005s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.032s
user    0m0.003s
sys     0m0.003s
119.28.12.243

real    0m2.072s
user    0m0.003s
sys     0m0.002s

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.el6_10.1 <<>> @223.6.6.6 www.dnsdizhi.com +short
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached

real    0m6.005s
user    0m0.001s
sys     0m0.004s

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.el6_10.1 <<>> @223.6.6.6 www.dnsdizhi.com +short
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached

real    0m6.006s
user    0m0.003s
sys     0m0.003s
119.28.12.243

real    0m0.009s
user    0m0.001s
sys     0m0.004s
119.28.12.243

real    0m0.037s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m2.032s
user    0m0.002s
sys     0m0.001s
119.28.12.243

real    0m0.012s
user    0m0.002s
sys     0m0.003s

real    0m23.347s
user    0m0.023s
sys     0m0.029s

第11-20次

[root@2 ~]# time for i in `seq 1 10`;do time dig @223.6.6.6 www.dnsdizhi.com +short; done
119.28.12.243

real    0m0.009s
user    0m0.003s
sys     0m0.003s
119.28.12.243

real    0m4.031s
user    0m0.003s
sys     0m0.002s
119.28.12.243

real    0m4.032s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.013s
user    0m0.004s
sys     0m0.002s

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.el6_10.1 <<>> @223.6.6.6 www.dnsdizhi.com +short
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached

real    0m6.005s
user    0m0.003s
sys     0m0.002s
119.28.12.243

real    0m0.032s
user    0m0.004s
sys     0m0.002s
119.28.12.243

real    0m0.011s
user    0m0.000s
sys     0m0.005s

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.el6_10.1 <<>> @223.6.6.6 www.dnsdizhi.com +short
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached

real    0m6.005s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.012s
user    0m0.004s
sys     0m0.002s
119.28.12.243

real    0m4.045s
user    0m0.000s
sys     0m0.005s

real    0m24.198s
user    0m0.026s
sys     0m0.029s

第21-30次

[root@2 ~]# time for i in `seq 1 10`;do time dig @223.6.6.6 www.dnsdizhi.com +short; done

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.el6_10.1 <<>> @223.6.6.6 www.dnsdizhi.com +short
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached

real    0m6.006s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.009s
user    0m0.003s
sys     0m0.003s
119.28.12.243

real    0m0.023s
user    0m0.003s
sys     0m0.002s
119.28.12.243

real    0m0.014s
user    0m0.004s
sys     0m0.001s
119.28.12.243

real    0m0.016s
user    0m0.002s
sys     0m0.002s
119.28.12.243

real    0m4.039s
user    0m0.002s
sys     0m0.003s

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.el6_10.1 <<>> @223.6.6.6 www.dnsdizhi.com +short
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached

real    0m6.006s
user    0m0.002s
sys     0m0.004s
119.28.12.243

real    0m0.027s
user    0m0.003s
sys     0m0.002s

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.el6_10.1 <<>> @223.6.6.6 www.dnsdizhi.com +short
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached

real    0m6.006s
user    0m0.003s
sys     0m0.003s
119.28.12.243

real    0m0.010s
user    0m0.002s
sys     0m0.003s

real    0m22.156s
user    0m0.027s
sys     0m0.026s

第31-40次

[root@2 ~]# time for i in `seq 1 10`;do time dig @223.6.6.6 www.dnsdizhi.com +short; done
119.28.12.243

real    0m0.012s
user    0m0.001s
sys     0m0.004s
119.28.12.243

real    0m4.076s
user    0m0.003s
sys     0m0.003s
119.28.12.243

real    0m2.026s
user    0m0.003s
sys     0m0.002s
119.28.12.243

real    0m0.025s
user    0m0.005s
sys     0m0.001s
119.28.12.243

real    0m0.033s
user    0m0.002s
sys     0m0.002s
119.28.12.243

real    0m0.026s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m2.026s
user    0m0.003s
sys     0m0.003s
119.28.12.243

real    0m0.026s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.010s
user    0m0.001s
sys     0m0.005s
119.28.12.243

real    0m0.028s
user    0m0.002s
sys     0m0.003s

real    0m8.291s
user    0m0.025s
sys     0m0.029s

第41-50次

[root@2 ~]# time for i in `seq 1 10`;do time dig @223.6.6.6 www.dnsdizhi.com +short; done
119.28.12.243

real    0m0.009s
user    0m0.003s
sys     0m0.002s
119.28.12.243

real    0m0.011s
user    0m0.001s
sys     0m0.005s
119.28.12.243

real    0m0.010s
user    0m0.001s
sys     0m0.003s
119.28.12.243

real    0m0.009s
user    0m0.001s
sys     0m0.003s
119.28.12.243

real    0m0.012s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.012s
user    0m0.001s
sys     0m0.003s

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.el6_10.1 <<>> @223.6.6.6 www.dnsdizhi.com +short
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached

real    0m6.004s
user    0m0.000s
sys     0m0.004s
119.28.12.243

real    0m0.008s
user    0m0.004s
sys     0m0.002s
119.28.12.243

real    0m0.015s
user    0m0.002s
sys     0m0.002s
119.28.12.243

real    0m0.017s
user    0m0.001s
sys     0m0.004s

real    0m6.109s
user    0m0.017s
sys     0m0.032s

第51-60次

[root@2 ~]# time for i in `seq 1 10`;do time dig @223.6.6.6 www.dnsdizhi.com +short; done
119.28.12.243

real    0m0.017s
user    0m0.001s
sys     0m0.005s
119.28.12.243

real    0m0.020s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.013s
user    0m0.003s
sys     0m0.002s
119.28.12.243

real    0m0.013s
user    0m0.000s
sys     0m0.006s
119.28.12.243

real    0m0.011s
user    0m0.004s
sys     0m0.001s
119.28.12.243

real    0m0.014s
user    0m0.003s
sys     0m0.002s

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.el6_10.1 <<>> @223.6.6.6 www.dnsdizhi.com +short
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached

real    0m6.005s
user    0m0.001s
sys     0m0.004s
119.28.12.243

real    0m0.019s
user    0m0.004s
sys     0m0.001s
119.28.12.243

real    0m0.024s
user    0m0.003s
sys     0m0.003s
119.28.12.243

real    0m0.017s
user    0m0.001s
sys     0m0.003s

real    0m6.155s
user    0m0.022s
sys     0m0.031s

第61-70次

# time for i in `seq 1 10`;do time dig @223.6.6.6 www.dnsdizhi.com +short; done
119.28.12.243

real    0m0.013s
user    0m0.003s
sys     0m0.003s
119.28.12.243

real    0m0.010s
user    0m0.003s
sys     0m0.002s
119.28.12.243

real    0m0.010s
user    0m0.001s
sys     0m0.004s
119.28.12.243

real    0m0.009s
user    0m0.000s
sys     0m0.004s
119.28.12.243

real    0m0.013s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.010s
user    0m0.002s
sys     0m0.003s

; <<>> DiG 9.8.2rc1-RedHat-9.8.2-0.68.rc1.el6_10.1 <<>> @223.6.6.6 www.dnsdizhi.com +short
; (1 server found)
;; global options: +cmd
;; connection timed out; no servers could be reached

real    0m6.005s
user    0m0.000s
sys     0m0.005s
119.28.12.243

real    0m2.047s
user    0m0.003s
sys     0m0.003s
119.28.12.243

real    0m0.009s
user    0m0.003s
sys     0m0.002s
119.28.12.243

real    0m0.013s
user    0m0.002s
sys     0m0.002s

real    0m8.140s
user    0m0.020s
sys     0m0.031s

第71-80次

# time for i in `seq 1 10`;do time dig @223.6.6.6 www.dnsdizhi.com +short; done
119.28.12.243

real    0m0.010s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.012s
user    0m0.001s
sys     0m0.003s
119.28.12.243

real    0m0.013s
user    0m0.002s
sys     0m0.004s
119.28.12.243

real    0m0.009s
user    0m0.001s
sys     0m0.003s
119.28.12.243

real    0m0.014s
user    0m0.001s
sys     0m0.003s
119.28.12.243

real    0m0.045s
user    0m0.004s
sys     0m0.002s
119.28.12.243

real    0m0.012s
user    0m0.001s
sys     0m0.003s
119.28.12.243

real    0m0.013s
user    0m0.001s
sys     0m0.004s
119.28.12.243

real    0m0.010s
user    0m0.000s
sys     0m0.005s
119.28.12.243

real    0m0.011s
user    0m0.002s
sys     0m0.003s

real    0m0.151s
user    0m0.017s
sys     0m0.033s

第81-90次

# time for i in `seq 1 10`;do time dig @223.6.6.6 www.dnsdizhi.com +short; done
119.28.12.243

real    0m0.009s
user    0m0.001s
sys     0m0.004s
119.28.12.243

real    0m0.029s
user    0m0.001s
sys     0m0.005s
119.28.12.243

real    0m0.011s
user    0m0.004s
sys     0m0.001s
119.28.12.243

real    0m0.009s
user    0m0.002s
sys     0m0.002s
119.28.12.243

real    0m0.013s
user    0m0.001s
sys     0m0.006s
119.28.12.243

real    0m0.010s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.012s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.015s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.009s
user    0m0.003s
sys     0m0.002s
119.28.12.243

real    0m0.012s
user    0m0.003s
sys     0m0.002s

real    0m0.131s
user    0m0.023s
sys     0m0.031s

第91-100次


# time for i in `seq 1 10`;do time dig @223.6.6.6 www.dnsdizhi.com +short; done
119.28.12.243

real    0m2.072s
user    0m0.000s
sys     0m0.005s
119.28.12.243

real    0m0.031s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.064s
user    0m0.004s
sys     0m0.001s
119.28.12.243

real    0m0.070s
user    0m0.004s
sys     0m0.001s
119.28.12.243

real    0m0.069s
user    0m0.003s
sys     0m0.002s
119.28.12.243

real    0m0.050s
user    0m0.002s
sys     0m0.002s
119.28.12.243

real    0m0.052s
user    0m0.004s
sys     0m0.002s
119.28.12.243

real    0m0.046s
user    0m0.001s
sys     0m0.003s
119.28.12.243

real    0m0.055s
user    0m0.002s
sys     0m0.003s
119.28.12.243

real    0m0.087s
user    0m0.002s
sys     0m0.004s

real    0m2.597s
user    0m0.024s
sys     0m0.027s

swoole最新的发行版安装

和任何开源项目一样, Swoole总是在最新的发行版提供最可靠的稳定性和最强的功能, 请尽量保证你使用的是最新版本

1. 直接使用Swoole官方的二进制包 (初学者 + 开发环境)

访问我们官网的下载页面

编译需求

  • Linux, OS X 系统 或 CygWin, WSL
  • PHP 7.0.0 或以上版本 (版本越高性能越好)
  • GCC 4.8 及以上

2. 使用PHP官方的PECL工具安装 (初学者)

pecl install swoole

3. 从源码编译安装 (推荐)

非内核开发研究之用途, 请下载发布版本的源码编译

cd swoole-src && \
phpize && \
./configure && \
make && sudo make install

启用扩展

编译安装到系统成功后, 需要在php.ini中加入一行extension=swoole.so来启用Swoole扩展

额外编译参数

使用例子: ./configure --enable-openssl --enable-sockets

  • --enable-openssl 或 --with-openssl-dir=DIR
  • --enable-sockets
  • --enable-http2
  • --enable-mysqlnd (需要 mysqlnd, 只是为了支持mysql->escape方法)

升级

⚠️ 如果你要从源码升级, 别忘记在源码目录执行 make clean

  1. pecl upgrade swoole
  2. git pull && cd swoole-src && make clean && make && sudo make install
  3. 如果你改变了PHP版本, 请重新执行 phpize clean && phpize后重新编译

Http性能压测工具wrk

用过了很多压测工具,却一直没找到中意的那款。最近试了wrk感觉不错,命令及结果很类似ab,写下这份使用指南给自己备忘用,如果能帮到你,那也很好。

安装

wrk支持大多数类UNIX系统,不支持windows。需要操作系统支持LuaJIT和OpenSSL,不过不用担心,大多数类Unix系统都支持。安装wrk非常简单,只要从github上下载wrk源码,在项目路径下执行make命令即可。

git clone https://github.com/wg/wrk make

make之后,会在项目路径下生成可执行文件wrk,随后就可以用其进行HTTP压测了。可以把这个可执行文件拷贝到某个已在path中的路径,比如/usr/local/bin,这样就可以在任何路径直接使用wrk了。

默认情况下wrk会使用自带的LuaJIT和OpenSSL,如果你想使用系统已安装的版本,可以使用WITH_LUAJIT和WITH_OPENSSL这两个选项来指定它们的路径。比如:

make WITH_LUAJIT=/usr WITH_OPENSSL=/usr

基本使用

  1. 命令行敲下wrk,可以看到使用帮助
Usage: wrk <options> <url>                            
  Options:                                            
    -c, --connections <N>  Connections to keep open  -d, --duration    <T>  Duration of test  -t, --threads     <N>  Number of threads to use  -s, --script      <S>  Load Lua script file  -H, --header      <H>  Add header to request  --latency          Print latency statistics  --timeout     <T>  Socket/request timeout  -v, --version          Print version details  Numeric arguments may include a SI unit (1k, 1M, 1G) Time arguments may include a time unit (2s, 2m, 2h)

简单翻成中文:

使用方法: wrk <选项> <被测HTTP服务的URL> Options:                                            
    -c, --connections <N> 跟服务器建立并保持的TCP连接数量  
    -d, --duration <T> 压测时间           
    -t, --threads <N> 使用多少个线程进行压测   
                                                      
    -s, --script <S> 指定Lua脚本路径       
    -H, --header <H> 为每一个HTTP请求添加HTTP头      
        --latency          在压测结束后,打印延迟统计信息   
        --timeout <T> 超时时间     
    -v, --version          打印正在使用的wrk的详细版本信息 <N>代表数字参数,支持国际单位 (1k, 1M, 1G) <T>代表时间参数,支持时间单位 (2s, 2m, 2h)
  1. 看下版本
wrk -v 输出: wrk 4.0.2 [epoll] Copyright (C) 2012 Will Glozer

看到是4.0.2版本的wrk,使用了epoll。这意味着我们可以用少量的线程来跟被测服务创建大量连接,进行压测。

  1. 做一次简单压测,分析下结果
wrk -t8 -c200 -d30s --latency "http://www.bing.com" 输出:
Running 30s test @ http://www.bing.com 8 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency 46.67ms 215.38ms 1.67s 95.59% Req/Sec 7.91k 1.15k 10.26k 70.77% Latency Distribution 50%    2.93ms 75%    3.78ms 90%    4.73ms 99%    1.35s  1790465 requests in 30.01s, 684.08MB read
Requests/sec: 59658.29 Transfer/sec: 22.79MB

以上使用8个线程200个连接,对bing首页进行了30秒的压测,并要求在压测结果中输出响应延迟信息。以下对压测结果进行简单注释:

Running 30s test @ http://www.bing.com (压测时间30s) 8 threads and 200 connections (共8个测试线程,200个连接)
  Thread Stats   Avg      Stdev     Max   +/- Stdev
              (平均值) (标准差)(最大值)(正负一个标准差所占比例)
    Latency 46.67ms 215.38ms 1.67s 95.59% (延迟)
    Req/Sec 7.91k 1.15k 10.26k 70.77% (处理中的请求数)
  Latency Distribution (延迟分布) 50%    2.93ms 75%    3.78ms 90%    4.73ms 99%    1.35s (99分位的延迟) 1790465 requests in 30.01s, 684.08MB read (30.01秒内共处理完成了1790465个请求,读取了684.08MB数据)
Requests/sec: 59658.29 (平均每秒处理完成59658.29个请求)
Transfer/sec: 22.79MB (平均每秒读取数据22.79MB)

可以看到,wrk使用方便,结果清晰。并且因为非阻塞IO的使用,可以在普通的测试机上创建出大量的连接,从而达到较好的压测效果。

使用Lua脚本个性化wrk压测

以上两节安装并简单使用了wrk,但这种简单的压测可能不能满足我们的需求。比如我们可能需要使用POST METHOD跟服务器交互;可能需要为每一次请求使用不同的参数,以更好的模拟服务的实际使用场景等。wrk支持用户使用–script指定Lua脚本,来定制压测过程,满足个性化需求。

  1. 介绍wrk对Lua脚本的支持

wrk支持在三个阶段对压测进行个性化,分别是启动阶段、运行阶段和结束阶段。每个测试线程,都拥有独立的Lua运行环境。

启动阶段
function setup(thread)

在脚本文件中实现setup方法,wrk就会在测试线程已经初始化但还没有启动的时候调用该方法。wrk会为每一个测试线程调用一次setup方法,并传入代表测试线程的对象thread作为参数。setup方法中可操作该thread对象,获取信息、存储信息、甚至关闭该线程。

thread.addr             - get or set the thread's server address thread:get(name)        - get the value of a global in the thread's env thread:set(name, value) - set the value of a global in the thread's env thread:stop()           - stop the thread
运行阶段
function init(args) function delay() function request() function response(status, headers, body)

init由测试线程调用,只会在进入运行阶段时,调用一次。支持从启动wrk的命令中,获取命令行参数;

delay在每次发送request之前调用,如果需要delay,那么delay相应时间;

request用来生成请求;每一次请求都会调用该方法,所以注意不要在该方法中做耗时的操作;

reponse在每次收到一个响应时调用;为提升性能,如果没有定义该方法,那么wrk不会解析headers和body;

结束阶段
function done(summary, latency, requests)

该方法在整个测试过程中只会调用一次,可从参数给定的对象中,获取压测结果,生成定制化的测试报告。

自定义脚本中可访问的变量和方法

变量:wrk

 wrk = {
    scheme  = "http",
    host    = "localhost",
    port    = nil,
    method  = "GET",
    path    = "/",
    headers = {},
    body    = nil,
    thread  = <userdata>,
  }

一个table类型的变量wrk,是全局变量,修改该table,会影响所有请求。

方法:wrk.fomat wrk.lookup wrk.connect

 function wrk.format(method, path, headers, body) wrk.format returns a HTTP request string containing the passed parameters
    merged with values from the wrk table.
    根据参数和全局变量wrk,生成一个HTTP rquest stringfunction wrk.lookup(host, service) wrk.lookup returns a table containing all known addresses for the host and service pair. This corresponds to the POSIX getaddrinfo() function.
    给定hostserviceport/well known service name),返回所有可用的服务器地址信息。 function wrk.connect(addr) wrk.connect returns true if the address can be connected to, otherwise
    it returns false. The address must be one returned from wrk.lookup().
    测试与给定的服务器地址信息是否可以成功创建连接
  1. 示例
使用POST METHOD
wrk.method = "POST" wrk.body   = "foo=bar&baz=quux" wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"

通过修改全局变量wrk,使得所有请求都使用POST方法,并指定了body和Content-Type头。

为每次request更换一个参数
request = function() uid = math.random(1, 10000000) path = "/test?uid=" .. uid return wrk.format(nil, path) end

通过在request方法中随机生成1~10000000之间的uid,使得请求中的uid参数随机。

每次请求之前延迟10ms
function delay() return 10 end
每个线程要先进行认证,认证之后获取token以进行压测
token = nil path = "/authenticate" request = function() return wrk.format("GET", path) end response = function(status, headers, body) if not token and status == 200 then token = headers["X-Token"] path = "/resource" wrk.headers["X-Token"] = token end end

在没有token的情况下,先访问/authenticate认证。认证成功后,读取token并替换path为/resource。

压测支持HTTP pipeline的服务
init = function(args) local r = {}
   r[1] = wrk.format(nil, "/?foo")
   r[2] = wrk.format(nil, "/?bar")
   r[3] = wrk.format(nil, "/?baz")

   req = table.concat(r) end request = function() return req end

通过在init方法中将三个HTTP request请求拼接在一起,实现每次发送三个请求,以使用HTTP pipeline。

https://www.cnblogs.com/xinzhao/p/6233009.html

nginx+php-fpm,使用Unix Socket还是tcp方式连接?

将Nginx与FastCGI的通信方式由TCP改为Unix Socket。TCP在高并发访问下比Unix Socket稳定,但Unix Socket速度要比TCP快”,看来这是真的存在。两者各有优缺点啊

1.worker_processes 越大越好(一定数量后性能增加不明显)


2.worker_cpu_affinity 所有cpu平分worker_processes 要比每个worker_processes 都跨cpu分配性能要好;不考虑php的执行,测试结果worker_processes数量是cpu核数的2倍性能最优

3.unix domain socket(共享内存的方式)要比tcp网络端口配置性能要好
不考虑backlog,请求速度有量级的飞跃,但错误率超过50%
加上backlog,性能有10%左右提升

4.调整nginx、php-fpm和内核的backlog(积压),connect() to unix:/tmp/php-fpm.socket failed (11: Resource temporarily unavailable) while connecting to upstream错误的返回会减少
nginx:
配置文件的server块
listen 80 default backlog=1024;

php-fpm:
配置文件的
listen.backlog = 2048

kernel参数:
/etc/sysctl.conf,不能低于上面的配置
net.ipv4.tcp_max_syn_backlog = 4096
net.core.netdev_max_backlog = 4096

5.增加单台服务器上的php-fpm的master实例,会增加fpm的处理能力,也能减少报错返回的几率
多实例启动方法,使用多个配置文件:
/usr/local/php/sbin/php-fpm -y /usr/local/php/etc/php-fpm.conf &
/usr/local/php/sbin/php-fpm -y /usr/local/php/etc/php-fpm1.conf &

nginx的fastcgi配置
    upstream phpbackend {
#      server   127.0.0.1:9000 weight=100 max_fails=10 fail_timeout=30;
#      server   127.0.0.1:9001 weight=100 max_fails=10 fail_timeout=30;
#      server   127.0.0.1:9002 weight=100 max_fails=10 fail_timeout=30;
#      server   127.0.0.1:9003 weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm.sock weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm1.sock weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm2.sock weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm3.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm4.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm5.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm6.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm7.sock weight=100 max_fails=10 fail_timeout=30;
    }

        location ~ \.php* {
            fastcgi_pass   phpbackend;
#           fastcgi_pass   unix:/var/www/php-fpm.sock;
            fastcgi_index index.php;
       ……….
       }

6.测试环境和结果

内存2G
swap2G
cpu 2核 Intel(R) Xeon(R) CPU E5405  @ 2.00GHz
采用ab远程访问测试,测试程序为php的字符串处理程序


1)在开4个php-fpm实例,nginx 8个worker_processes 每个cpu4个worker_processes ,backlog为1024,php的backlog为2048,内核backlog为4096,采用unix domain socket连接的情况下,其他保持参数不变

性能和错误率较为平衡,可接受,超过4个fpm实例,性能开始下降,错误率并没有明显下降
结论是fpm实例数,worker_processes数和cpu保持倍数关系,性能较高
影响性能和报错的参数为
php-fpm实例,nginx worker_processes数量,fpm的max_request,php的backlog,unix domain socket


10W请求,500并发无报错,1000并发报错率为0.9%

500并发:
Time taken for tests:   25 seconds avg.
Complete requests:      100000
Failed requests:        0
Write errors:           0
Requests per second:    4000 [#/sec] (mean) avg.
Time per request:       122.313 [ms] (mean)
Time per request:       0.245 [ms] (mean, across all concurrent requests)
Transfer rate:          800 [Kbytes/sec] received avg.

1000并发:
Time taken for tests:   25 seconds avg.
Complete requests:      100000
Failed requests:        524
   (Connect: 0, Length: 524, Exceptions: 0)
Write errors:           0
Non-2xx responses:      524
Requests per second:    3903.25 [#/sec] (mean)
Time per request:       256.197 [ms] (mean)
Time per request:       0.256 [ms] (mean, across all concurrent requests)
Transfer rate:          772.37 [Kbytes/sec] received

2)在其他参数不变,unix domain socket换为tcp网络端口连接,结果如下

500并发:
Concurrency Level:      500
Time taken for tests:   26.934431 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Requests per second:    3712.72 [#/sec] (mean)
Time per request:       134.672 [ms] (mean)
Time per request:       0.269 [ms] (mean, across all concurrent requests)
Transfer rate:          732.37 [Kbytes/sec] received

1000并发:
Concurrency Level:      1000
Time taken for tests:   28.385349 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Requests per second:    3522.94 [#/sec] (mean)
Time per request:       283.853 [ms] (mean)
Time per request:       0.284 [ms] (mean, across all concurrent requests)
Transfer rate:          694.94 [Kbytes/sec] received

与1)比较,有大约10%的性能下降

7. 5.16调整fpm的max_request参数为1000,并发1000报错返回降到200个以下,
Transfer rate在800左右

MySQL的binlog日志

binlog 基本认识
    MySQL的二进制日志可以说是MySQL最重要的日志了,它记录了所有的DDL和DML(除了数据查询语句)语句,以事件形式记录,还包含语句所执行的消耗的时间,MySQL的二进制日志是事务安全型的。

    一般来说开启二进制日志大概会有1%的性能损耗(参见MySQL官方中文手册 5.1.24版)。二进制有两个最重要的使用场景: 其一:MySQL Replication在Master端开启binlog,Mster把它的二进制日志传递给slaves来达到master-slave数据一致的目的。 
    其二:自然就是数据恢复了,通过使用mysqlbinlog工具来使恢复数据。
    
    二进制日志包括两类文件:二进制日志索引文件(文件名后缀为.index)用于记录所有的二进制文件,二进制日志文件(文件名后缀为.00000*)记录数据库所有的DDL和DML(除了数据查询语句)语句事件。 


一、开启binlog日志:
    vi编辑打开mysql配置文件 # vi /usr/local/mysql/etc/my.cnf  在[mysqld] 区块
    设置/添加 log-bin=mysql-bin  确认是打开状态(值 mysql-bin 是日志的基本名或前缀名);

    重启mysqld服务使配置生效 # pkill mysqld # /usr/local/mysql/bin/mysqld_safe --user=mysql &  二、也可登录mysql服务器,通过mysql的变量配置表,查看二进制日志是否已开启 单词:variable[ˈvɛriəbəl] 变量

    登录服务器 # /usr/local/mysql/bin/mysql -uroot -p123456 mysql> show variables like 'log_%'; +----------------------------------------+---------------------------------------+
    | Variable_name                          | Value                                 |
    +----------------------------------------+---------------------------------------+
    | log_bin                                | ON                                    | ------> ON表示已经开启binlog日志 | log_bin_basename                       | /usr/local/mysql/data/mysql-bin       |
    | log_bin_index                          | /usr/local/mysql/data/mysql-bin.index |
    | log_bin_trust_function_creators        | OFF                                   |
    | log_bin_use_v1_row_events              | OFF                                   |
    | log_error                              | /usr/local/mysql/data/martin.err      |
    | log_output                             | FILE |
    | log_queries_not_using_indexes          | OFF                                   |
    | log_slave_updates                      | OFF                                   |
    | log_slow_admin_statements              | OFF                                   |
    | log_slow_slave_statements              | OFF                                   |
    | log_throttle_queries_not_using_indexes | 0                                     |
    | log_warnings                           | 1                                     |
    +----------------------------------------+---------------------------------------+ 三、常用binlog日志操作命令 1.查看所有binlog日志列表 mysql> show master logs; 2.查看master状态,即最后(最新)一个binlog日志的编号名称,及其最后一个操作事件pos结束点(Position)值 mysql> show master status; 3.刷新log日志,自此刻开始产生一个新编号的binlog日志文件 mysql> flush logs;
      注:每当mysqld服务重启时,会自动执行此命令,刷新binlog日志;在mysqldump备份数据时加 -F 选项也会刷新binlog日志; 4.重置(清空)所有binlog日志 mysql> reset master;


四、查看某个binlog日志内容,常用有两种方式: 1.使用mysqlbinlog自带查看命令法:
      注: binlog是二进制文件,普通文件查看器cat more vi等都无法打开,必须使用自带的 mysqlbinlog 命令查看
          binlog日志与数据库文件在同目录中(我的环境配置安装是选择在/usr/local/mysql/data中)
      在MySQL5.5以下版本使用mysqlbinlog命令时如果报错,就加上 “--no-defaults”选项 # /usr/local/mysql/bin/mysqlbinlog /usr/local/mysql/data/mysql-bin.000013  下面截取一个片段分析: ............................................................................... # at 552 #131128 17:50:46 server id 1  end_log_pos 665   Query   thread_id=11    exec_time=0     error_code=0 ---->执行时间:17:50:46;pos点:665 SET TIMESTAMP=1385632246/*!*/;
         update zyyshop.stu set name='李四' where id=4              ---->执行的SQL /*!*/; # at 665 #131128 17:50:46 server id 1  end_log_pos 692   Xid = 1454 ---->执行时间:17:50:46;pos点:692  ...............................................................................: server id 1 数据库主机的服务号;
             end_log_pos 665 pos点
             thread_id=11 线程号 2.上面这种办法读取出binlog日志的全文内容较多,不容易分辨查看pos点信息,这里介绍一种更为方便的查询命令: mysql> show binlog events [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count];

             选项解析:
               IN 'log_name' 指定要查询的binlog文件名(不指定就是第一个binlog文件)
               FROM pos 指定从哪个pos起始点开始查起(不指定就是从整个文件首个pos点开始算)
               LIMIT [offset,] 偏移量(不指定就是0)
               row_count       查询总条数(不指定就是所有行)

             截取部分查询结果: *************************** 20. row *************************** Log_name: mysql-bin.000021  ----------------------------------------------> 查询的binlog日志文件名 Pos: 11197 ----------------------------------------------------------> pos起始点: Event_type: Query ----------------------------------------------------------> 事件类型:Query
               Server_id: 1 --------------------------------------------------------------> 标识是由哪台服务器执行的
             End_log_pos: 11308 ----------------------------------------------------------> pos结束点:11308(即:下行的pos起始点)
                    Info: use `zyyshop`; INSERT INTO `team2` VALUES (0,345,'asdf8er5') ---> 执行的sql语句 *************************** 21. row *************************** Log_name: mysql-bin.000021 Pos: 11308 ----------------------------------------------------------> pos起始点:11308(即:上行的pos结束点)
              Event_type: Query
               Server_id: 1 End_log_pos: 11417 Info: use `zyyshop`; /*!40000 ALTER TABLE `team2` ENABLE KEYS */ *************************** 22. row *************************** Log_name: mysql-bin.000021 Pos: 11417 Event_type: Query
               Server_id: 1 End_log_pos: 11510 Info: use `zyyshop`; DROP TABLE IF EXISTS `type`

      这条语句可以将指定的binlog日志文件,分成有效事件行的方式返回,并可使用limit指定pos点的起始偏移,查询条数;
      
      A.查询第一个(最早)的binlog日志: mysql> show binlog events\G; 
    
      B.指定查询 mysql-bin.000021 这个文件: mysql> show binlog events in 'mysql-bin.000021'\G;

      C.指定查询 mysql-bin.000021 这个文件,从pos点:8224开始查起: mysql> show binlog events in 'mysql-bin.000021' from 8224\G;

      D.指定查询 mysql-bin.000021 这个文件,从pos点:8224开始查起,查询10条 mysql> show binlog events in 'mysql-bin.000021' from 8224 limit 10\G;

      E.指定查询 mysql-bin.000021 这个文件,从pos点:8224开始查起,偏移2行,查询10条 mysql> show binlog events in 'mysql-bin.000021' from 8224 limit 2,10\G;


五、恢复binlog日志实验(zyyshop是数据库) 1.假设现在是凌晨4:00,我的计划任务开始执行一次完整的数据库备份:

      将zyyshop数据库备份到 /root/BAK.zyyshop.sql 文件中: # /usr/local/mysql/bin/mysqldump -uroot -p123456 -lF --log-error=/root/myDump.err -B zyyshop > /root/BAK.zyyshop.sql ...... 大约过了若干分钟,备份完成了,我不用担心数据丢失了,因为我有备份了,嘎嘎~~~ 由于我使用了-F选项,当备份工作刚开始时系统会刷新log日志,产生新的binlog日志来记录备份之后的数据库“增删改”操作,查看一下: mysql> show master status; +------------------+----------+--------------+------------------+
      | File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
      +------------------+----------+--------------+------------------+
      | mysql-bin.000023 |      120 |              |                  |
      +------------------+----------+--------------+------------------+ 也就是说, mysql-bin.000023 是用来记录4:00之后对数据库的所有“增删改”操作。 2.早9:00上班了,业务的需求会对数据库进行各种“增删改”操作~~~~~~~ @ 比如:创建一个学生表并插入、修改了数据等等:
        CREATE TABLE IF NOT EXISTS `tt` (
          `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(16) NOT NULL, `sex` enum('m','w') NOT NULL DEFAULT 'm', `age` tinyint(3) unsigned NOT NULL, `classid` char(6) DEFAULT NULL, PRIMARY KEY (`id`)
         ) ENGINE=InnoDB DEFAULT CHARSET=utf8;


      导入实验数据 mysql> insert into zyyshop.tt(`name`,`sex`,`age`,`classid`) values('yiyi','w',20,'cls1'),('xiaoer','m',22,'cls3'),('zhangsan','w',21,'cls5'),('lisi','m',20,'cls4'),('wangwu','w',26,'cls6');


      查看数据 mysql> select * from zyyshop.tt; +----+----------+-----+-----+---------+
      | id | name     | sex | age | classid |
      +----+----------+-----+-----+---------+
      |  1 | yiyi     | w   |  20 | cls1    |
      |  2 | xiaoer   | m   |  22 | cls3    |
      |  3 | zhangsan | w   |  21 | cls5    |
      |  4 | lisi     | m   |  20 | cls4    |
      |  5 | wangwu   | w   |  26 | cls6    |
      +----+----------+-----+-----+---------+ 中午时分又执行了修改数据操作 mysql> update zyyshop.tt set name='李四' where id=4; mysql> update zyyshop.tt set name='小二' where id=2;

      修改后的结果: mysql> select * from zyyshop.tt; +----+----------+-----+-----+---------+
      | id | name     | sex | age | classid |
      +----+----------+-----+-----+---------+
      |  1 | yiyi     | w   |  20 | cls1    |
      |  2 | 小二     | m   |  22 | cls3    |
      |  3 | zhangsan | w   |  21 | cls5    |
      |  4 | 李四     | m   |  20 | cls4    |
      |  5 | wangwu   | w   |  26 | cls6    |
      +----+----------+-----+-----+---------+ 假设此时是下午18:00,莫名地执行了一条悲催的SQL语句,整个数据库都没了: mysql> drop database zyyshop; 3.此刻杯具了,别慌!先仔细查看最后一个binlog日志,并记录下关键的pos点,到底是哪个pos点的操作导致了数据库的破坏(通常在最后几步);
    
      备份一下最后一个binlog日志文件: # ll /usr/local/mysql/data | grep mysql-bin # cp -v /usr/local/mysql/data/mysql-bin.000023 /root/  此时执行一次刷新日志索引操作,重新开始新的binlog日志记录文件,理论说 mysql-bin.000023 这个文件不会再有后续写入了(便于我们分析原因及查找pos点),以后所有数据库操作都会写入到下一个日志文件; mysql> flush logs; mysql> show master status; 4.读取binlog日志,分析问题
      方式一:使用mysqlbinlog读取binlog日志: # /usr/local/mysql/bin/mysqlbinlog  /usr/local/mysql/data/mysql-bin.000023  方式二:登录服务器,并查看(推荐): mysql> show binlog events in 'mysql-bin.000023';
        
        以下为末尾片段: +------------------+------+------------+-----------+-------------+------------------------------------------------------------+
        | Log_name         | Pos | Event_type | Server_id | End_log_pos | Info                                                       |
        +------------------+------+------------+-----------+-------------+------------------------------------------------------------+
        | mysql-bin.000023 |  922 | Xid        |         1 |         953 | COMMIT /* xid=3820 */ |
        | mysql-bin.000023 |  953 | Query      |         1 |        1038 | BEGIN                                                      |
        | mysql-bin.000023 | 1038 | Query      |         1 |        1164 | use `zyyshop`; update zyyshop.tt set name='李四' where id=4|
        | mysql-bin.000023 | 1164 | Xid        |         1 |        1195 | COMMIT /* xid=3822 */ |
        | mysql-bin.000023 | 1195 | Query      |         1 |        1280 | BEGIN                                                      |
        | mysql-bin.000023 | 1280 | Query      |         1 |        1406 | use `zyyshop`; update zyyshop.tt set name='小二' where id=2|
        | mysql-bin.000023 | 1406 | Xid        |         1 |        1437 | COMMIT /* xid=3823 */ |
        | mysql-bin.000023 | 1437 | Query      |         1 |        1538 | drop database zyyshop                                      |
        +------------------+------+------------+-----------+-------------+------------------------------------------------------------+ 通过分析,造成数据库破坏的pos点区间是介于 1437--1538 之间,只要恢复到1437前就可。 5.现在把凌晨备份的数据恢复: # /usr/local/mysql/bin/mysql -uroot -p123456 -v < /root/BAK.zyyshop.sql; : 至此截至当日凌晨(4:00)前的备份数据都恢复了。
          但今天一整天(4:00--18:00)的数据肿么办呢?就得从前文提到的 mysql-bin.000023 新日志做文章了......


    6.从binlog日志恢复数据
      
      恢复语法格式: # mysqlbinlog mysql-bin.0000xx | mysql -u用户名 -p密码 数据库名  常用选项: --start-position=953 起始pos点 --stop-position=1437 结束pos点 --start-datetime="2013-11-29 13:18:54" 起始时间点 --stop-datetime="2013-11-29 13:21:53" 结束时间点 --database=zyyshop                     指定只恢复zyyshop数据库(一台主机上往往有多个数据库,只限本地log日志)
            
        不常用选项: -u --user=name              Connect to the remote server as username.连接到远程主机的用户名 -p --password[=name]        Password to connect to remote server.连接到远程主机的密码 -h --host=name              Get the binlog from server.从远程主机上获取binlog日志 --read-from-remote-server   Read binary logs from a MySQL server.从某个MySQL服务器上读取binlog日志

      小结:实际是将读出的binlog日志内容,通过管道符传递给mysql命令。这些命令、文件尽量写成绝对路径;

      A.完全恢复(本例不靠谱,因为最后那条 drop database zyyshop 也在日志里,必须想办法把这条破坏语句排除掉,做部分恢复) # /usr/local/mysql/bin/mysqlbinlog  /usr/local/mysql/data/mysql-bin.000021 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop   B.指定pos结束点恢复(部分恢复):
        @ --stop-position=953 pos结束点
        注:此pos结束点介于“导入实验数据”与更新“name='李四'”之间,这样可以恢复到更改“name='李四'”之前的“导入测试数据” # /usr/local/mysql/bin/mysqlbinlog --stop-position=953 --database=zyyshop /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop  在另一终端登录查看结果(成功恢复了): mysql> select * from zyyshop.tt; +----+----------+-----+-----+---------+
        | id | name     | sex | age | classid |
        +----+----------+-----+-----+---------+
        |  1 | yiyi     | w   |  20 | cls1    |
        |  2 | xiaoer   | m   |  22 | cls3    |
        |  3 | zhangsan | w   |  21 | cls5    |
        |  4 | lisi     | m   |  20 | cls4    |
        |  5 | wangwu   | w   |  26 | cls6    |
        +----+----------+-----+-----+---------+ C.指定pso点区间恢复(部分恢复):
        更新 name='李四' 这条数据,日志区间是Pos[1038] --> End_log_pos[1164],按事务区间是:Pos[953] --> End_log_pos[1195];

        更新 name='小二' 这条数据,日志区间是Pos[1280] --> End_log_pos[1406],按事务区间是:Pos[1195] --> End_log_pos[1437];

        c1.单独恢复 name='李四' 这步操作,可这样: # /usr/local/mysql/bin/mysqlbinlog --start-position=1038 --stop-position=1164 --database=zyyshop  /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop  也可以按事务区间单独恢复,如下: # /usr/local/mysql/bin/mysqlbinlog --start-position=953 --stop-position=1195 --database=zyyshop  /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop  c2.单独恢复 name='小二' 这步操作,可这样: # /usr/local/mysql/bin/mysqlbinlog --start-position=1280 --stop-position=1406 --database=zyyshop  /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop  也可以按事务区间单独恢复,如下: # /usr/local/mysql/bin/mysqlbinlog --start-position=1195 --stop-position=1437 --database=zyyshop  /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop  c3.将 name='李四'、name='小二' 多步操作一起恢复,需要按事务区间,可这样: # /usr/local/mysql/bin/mysqlbinlog --start-position=953 --stop-position=1437 --database=zyyshop  /usr/local/mysql/data/mysql-bin.000023 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop  D.在另一终端登录查看目前结果(两名称也恢复了): mysql> select * from zyyshop.tt; +----+----------+-----+-----+---------+
        | id | name     | sex | age | classid |
        +----+----------+-----+-----+---------+
        |  1 | yiyi     | w   |  20 | cls1    |
        |  2 | 小二     | m   |  22 | cls3    |
        |  3 | zhangsan | w   |  21 | cls5    |
        |  4 | 李四     | m   |  20 | cls4    |
        |  5 | wangwu   | w   |  26 | cls6    |
        +----+----------+-----+-----+---------+ E.也可指定时间区间恢复(部分恢复):除了用pos点的办法进行恢复,也可以通过指定时间区间进行恢复,按时间恢复需要用mysqlbinlog命令读取binlog日志内容,找时间节点。
        比如,我把刚恢复的tt表删除掉,再用时间区间点恢复 mysql> drop table tt;

        @ --start-datetime="2013-11-29 13:18:54" 起始时间点
        @ --stop-datetime="2013-11-29 13:21:53" 结束时间点 # /usr/local/mysql/bin/mysqlbinlog --start-datetime="2013-11-29 13:18:54" --stop-datetime="2013-11-29 13:21:53" --database=zyyshop /usr/local/mysql/data/mysql-bin.000021 | /usr/local/mysql/bin/mysql -uroot -p123456 -v zyyshop  总结:所谓恢复,就是让mysql将保存在binlog日志中指定段落区间的sql语句逐个重新执行一次而已。

PHP7开启Opcode性能对比

Opcache 的前生是 Optimizer+ ,它是PHP的官方公司 Zend 开发的一款闭源但可以免费使用的 PHP 优化加速组件。 Optimizer+ 将PHP代码预编译生成的脚本文件 Opcode 缓存在共享内存中供以后反复使用,从而避免了从磁盘读取代码再次编译的时间消耗。同时,它还应用了一些代码优化模式,使得代码执行更快。从而加速PHP的执行。

 PHP的正常执行流程如下

 

request请求(nginx,apache,cli等)–>Zend引擎读取.php文件–>扫描其词典和表达式 –>解析文件–>创建要执行的计算机代码(称为Opcode)–>最后执行Opcode–> response 返回

每一次请求PHP脚本都会执行一遍以上步骤,如果PHP源代码没有变化,那么Opcode也不会变化,显然没有必要每次都重新生成Opcode,结合在Web中无所不在的缓存机制,我们可以把Opcode缓存下来,以后直接访问缓存的Opcode岂不是更快,启用Opcode缓存之后的流程图如下所示:

 

 Opcode cache 的目地是避免重复编译,减少 CPU 和内存开销。

下面介绍Opcache的安装

安装:

1、找到opcache的扩展,我的是php7.1
yum list php71*
2、安装扩展
yum install php71w-opcache.x86_64

配置:

zend_extension=opcache.so
[opcache]
;开启opcache
opcache.enable=1  

;CLI环境下,PHP启用OPcache
opcache.enable_cli=1

;OPcache共享内存存储大小,单位MB
opcache.memory_consumption=128  

;PHP使用了一种叫做字符串驻留(string interning)的技术来改善性能。例如,如果你在代码中使用了1000次字符串“foobar”,在PHP内部只会在第一使用这个字符串的时候分配一个不可变的内存区域来存储这个字符串,其他的999次使用都会直接指向这个内存区域。这个选项则会把这个特性提升一个层次——默认情况下这个不可变的内存区域只会存在于单个php-fpm的进程中,如果设置了这个选项,那么它将会在所有的php-fpm进程中共享。在比较大的应用中,这可以非常有效地节约内存,提高应用的性能。
这个选项的值是以兆字节(megabytes)作为单位,如果把它设置为16,则表示16MB,默认是4MB
opcache.interned_strings_buffer=8

;这个选项用于控制内存中最多可以缓存多少个PHP文件。这个选项必须得设置得足够大,大于你的项目中的所有PHP文件的总和。
设置值取值范围最小值是 200,最大值在 PHP 5.5.6 之前是 100000,PHP 5.5.6 及之后是 1000000。也就是说在200到1000000之间。
opcache.max_accelerated_files=4000

;设置缓存的过期时间(单位是秒),为0的话每次都要检查
opcache.revalidate_freq=60

;从字面上理解就是“允许更快速关闭”。它的作用是在单个请求结束时提供一种更快速的机制来调用代码中的析构器,从而加快PHP的响应速度和PHP进程资源的回收速度,这样应用程序可以更快速地响应下一个请求。把它设置为1就可以使用这个机制了。
opcache.fast_shutdown=1

;如果启用(设置为1),OPcache会在opcache.revalidate_freq设置的秒数去检测文件的时间戳(timestamp)检查脚本是否更新。
如果这个选项被禁用(设置为0),opcache.revalidate_freq会被忽略,PHP文件永远不会被检查。这意味着如果你修改了你的代码,然后你把它更新到服务器上,再在浏览器上请求更新的代码对应的功能,你会看不到更新的效果
强烈建议你在生产环境中设置为0,更新代码后,再平滑重启PHP和web服务器。
opcache.validate_timestamps=0 

;开启Opcache File Cache(实验性), 通过开启这个, 我们可以让Opcache把opcode缓存缓存到外部文件中, 对于一些脚本, 会有很明显的性能提升.
这样PHP就会在/tmp目录下Cache一些Opcode的二进制导出文件, 可以跨PHP生命周期存在.
opcache.file_cache=/tmp

查看phpinfo:

测试结果:

同样的接口从以前的几百毫秒提升到现在的50ms左右

 

coredump文件生成样例测试源码,修改coredump大小及目录

 用c编写的程序在运行出错时,系统会生成coredump文件,如果系统没有生成可以通过命令

ulimit -c unlimited

1:添加pid作为扩展名,生成的core文件名称为core.pid
0:不添加pid作为扩展名,生成的core文件名称为core
修改 /proc/sys/kernel/core_uses_pid 文件内容为: 1
修改文件命令: echo “1” > /proc/sys/kernel/core_uses_pid
或者
sysctl -w kernel.core_uses_pid=1 kernel.core_uses_pid = 1

b. 控制core文件保存位置和文件名格式
修改文件命令: echo “/corefile/core-%e-%p-%t” > /proc/sys/kernel/core_pattern
或者:
sysctl -w kernel.core_pattern=/corefile/core-%e-%p-%t kernel.core_pattern = /corefile/core-%e-%p-%t
可以将core文件统一生成到/corefile目录下,产生的文件名为core-命令名-pid-时间戳
以下是参数列表:
%p – insert pid into filename 添加pid(进程id)
%u – insert current uid into filename 添加当前uid(用户id)
%g – insert current gid into filename 添加当前gid(用户组id)
%s – insert signal that caused the coredump into the filename 添加导致产生core的信号
%t – insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
%h – insert hostname where the coredump happened into filename 添加主机名
%e – insert coredumping executable name into filename 添加导致产生core的命令名
——————— 
作者:faithfu_yy 
来源:CSDN 
原文:https://blog.csdn.net/u011417820/article/details/71435031 
版权声明:本文为博主原创文章,转载请附上博文链接!

1)coredump文件生成样例测试源码

使系统生成产生core文件,这样就可以利用core文件查看程序是在哪一行出现错误了,具体的方法如下:
1、程序编译时要加-g选项,保证debug信息生成在应用程序当中

2、如果运行过程中出错,执行下面命令查看程序哪里出现错误:

gdb a.out core

举例来说:
#include <stdio.h>
int main(int argc, char** argv) {
  int* p = NULL;
  *p = 10;
}

上面的程序会运行出错,用g++ -g编译后,生成a.out,运行a.out,产生core文件,当执行gdb a.out core时,gdb会自动停止在出错的位置。

禅道7.3升级到11.3版本,通过源代码方式升级(通用)

一、升级步骤

  1. 在我们 网站下载新版本的源码包,以.zip结尾。wget http://dl.cnezsoft.com/zentao/11.3/ZenTaoPMS.11.3.stable.zip
  2. 解压缩新的程序,覆盖到原来的目录。比如之前禅道安装的目录是在/home/wwwroot/zentao下面,则将代码覆盖到/home/wwwroot/zentao,操作方法:unzip ZenTaoPMS.11.3.stable.zip ;cd zentaopms/ 下面复制所有的文件到 /home/wwwroot/zentao下面粘帖。参考命令:rsync -azv zentaopms/ /home/wwwroot/zentao/
  3. 执行升级程序。假设禅道的访问路径是http://192.168.1.99/,升级路径为http://192.168.1.99/upgrade.php。
  4. 根据向导,选择对应的版本,按照提示进行即可。

注意:不要将原来的程序移走,再解压缩新的版本,注意是覆盖!不要拷贝成/home/wwwroot/zentao

          不要下载.exe结尾的程序进行升级,那样子会覆盖原来的数据!

二、插件兼容问题

如果升级之前有安装过第三方朋友开发的插件,需要注意检查下是否和新版本的禅道兼容。如果发现升级之后无法访问,可以替换一下禅道运行代码:

1、把禅道当前运行代码文件夹改名(一般默认是 zentao 或者 zentaopms);

2、到 禅道官网下载相同版本禅道的源码,解压后放在同目录下面,保持文件夹名字和之前禅道运行代码文件夹名称相同 。

3、把原先文件夹中的  config/my.php  www/data/upload/1  www/.ztaccess   www/.htaccess 拷贝到新禅道文件夹对应目录,再访问禅道试试。