乐趣区

关于java:记一次性能优化单台-4-核-8G-机器支撑-5-万-QPS

前言

这篇文章的主题是记录一次性能优化,在优化的过程中遇到的问题,以及如何去解决的。为大家提供一个优化的思路,首先要申明的一点是,我的形式不是惟一的,大家在性能优化之路上遇到的问题都相对不止一个解决方案。

如何优化

首先大家要明确的一点是,脱离需要谈优化都是耍流氓,所以有谁跟你说在 xx 机器上实现了百万并发,基本上能够认为是不懂装懂了,单纯的并发数齐全是无意义的。其次,咱们优化之前必须要有一个指标,须要优化到什么水平,没有明确指标的优化是不可控的。再而后,咱们必须明确的找出性能瓶颈在哪里,而不能漫无目的的一通乱搞。

需要形容

这个我的项目是我在上家公司负责一个独自的模块,原本是集成在主站代码中的,起初因为并发太大,为了防止出现问题后连累主站服务,所有由我一个人负责拆分进去。对这个模块的拆分要求是,压力测试 QPS 不能低于 3 万,数据库负载不能超过 50%,服务器负载不能超过 70%, 单次申请时长不能超过 70ms,错误率不能超过 5%。

环境的配置如下:
服务器:4 核 8G 内存,centos7 零碎,ssd 硬盘
数据库:Mysql5.7,最大连接数 800
缓存: redis, 1G 容量。
以上环境都是购买自腾讯云的服务。
压测工具:locust,应用腾讯的弹性伸缩实现分布式的压测。

需要形容如下:
用户进入首页,从数据库中查问是否有适合的弹窗配置,如果没有,则持续期待下一次申请、如果有适合的配置,则返回给前端。这里开始则有多个条件分支,如果用户点击了弹窗,则记录用户点击,并且在配置的工夫内不再返回配置,如果用户未点击,则 24 小时后持续返回本次配置,如果用户点击了,然而后续没有配置了,则接着期待下一次。

重点剖析

依据需要,咱们晓得了有几个重要的点,1、须要找出适合用户的弹窗配置,2、须要记录用户下一次返回配置的工夫并记录到数据库中,3、须要记录用户对返回的配置执行了什么操作并记录到数据库中。

调优

咱们能够看到,上述三个重点都存在数据库的操作,不只有读库,还有写库操作。从这里咱们能够看到如果不加缓存的话,所有的申请都压到数据库,势必会占满全副连接数,呈现回绝拜访的谬误,同时因为 sql 执行过慢,导致申请无奈及时返回。所以,咱们首先要做的就是讲写库操作剥离开来,晋升每一次申请响应速度,优化数据库连贯。整个零碎的架构图如下:

将写库操作放到一个先进先出的音讯队列中来做,为了缩小复杂度,应用了 redis 的 list 来做这个音讯队列。

而后进行压测,后果如下:

QPS 在 6000 左右 502 谬误大幅回升至 30%,服务器 cpu 在 60%-70% 之间来回跳动,数据库连接数被占满 tcp 连接数为 6000 左右,很显著,问题还是出在数据库,通过排查 sql 语句,查问到起因就是找出适合用户的配置操作时每次申请都要读取数据库所导致的连接数被用完。因为咱们的连接数只有 800,一旦申请过多,势必会导致数据库瓶颈。好了,问题找到了,咱们持续优化,更新的架构如下

咱们将全副的配置都加载到缓存中,只有在缓存中没有配置的时候才会去读取数据库。

接下来咱们再次压测,后果如下:
QPS 压到 2 万左右的时候就上不去了,服务器 cpu 在 60%-80% 之间跳动,数据库连接数为 300 个左右,每秒 tpc 连接数为 1.5 万左右。

这个问题是困扰我比拟久的一个问题,因为咱们能够看到,咱们 2 万的 QPS,然而 tcp 连接数却并没有达到 2 万,我猜想,tcp 连接数就是引发瓶颈的问题,然而因为什么起因所引发的临时无奈找进去。

这个时候猜想,既然是无奈建设 tcp 连贯,是否有可能是服务器限度了 socket 连接数,验证猜想,咱们看一下,在终端输出 ulimit - n 命令,显示的后果为 65535,看到这里,感觉 socket 连接数并不是限度咱们的起因,为了验证猜想,将 socket 连接数调大为 100001.

再次进行压测,后果如下:

QPS 压到 2.2 万左右的时候就上不去了,服务器 cpu 在 60%-80% 之间跳动,数据库连接数为 300 个左右,每秒 tpc 连接数为 1.7 万左右。

尽管有一点晋升,然而并没有实质性的变动,接下来的几天工夫,我发现都无奈找到优化的计划,那几天的确很好受,找不进去优化的计划,过了几天,再次将问题梳理了一遍,发现,尽管 socket 连接数足够,然而并没有全副被用上,猜想,每次申请过后,tcp 连贯并没有立刻被开释,导致 socket 无奈重用。通过查找材料,找到了问题所在,

tcp 链接在通过四次握手完结连贯后并不会立刻开释,而是处于 timewait 状态,会期待一段时间,以避免客户端后续的数据未被接管。
好了,问题找到了,咱们要接着优化,首先想到的就是调整 tcp 链接完结后等待时间,然而 linux 并没有提供这一内核参数的调整,如果要改,必须要本人从新编译内核,幸好还有另一个参数 net.ipv4.tcp_max_tw_buckets,timewait 的数量,默认是 180000。咱们调整为 6000,而后关上 timewait 疾速回收,和开启重用,残缺的参数优化如下

#timewait 的数量,默认是 180000。net.ipv4.tcp_max_tw_buckets = 6000

net.ipv4.ip_local_port_range = 1024 65000

#启用 timewait 疾速回收。net.ipv4.tcp_tw_recycle = 1

#开启重用。容许将 TIME-WAIT sockets 从新用于新的 TCP 连贯。net.ipv4.tcp_tw_reuse = 1

咱们再次压测,结果显示:
QPS5 万,服务器 cpu70%, 数据库连贯失常,tcp 连贯失常,响应工夫均匀为 60ms,错误率为 0%。

结语

到此为止,整个服务的开发、调优、和压测就完结了。回顾这一次调优,失去了很多教训,最重要的是,深刻理解了 web 开发不是一个独立的个体,而是网络、数据库、编程语言、操作系统等多门学科联合的工程实际,这就要求 web 开发人员有牢固的基础知识,否则呈现了问题还不晓得怎么剖析查找。

起源 | https://segmentfault.com/a/11…

退出移动版