记一次性能优化,单台 4 核 8G 机器支撑 5 万 QPS

图片

来源 | https://segmentfault.com/a/1190000018075241

 

前言

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

如何优化

首先大家要明确的一点是,脱离需求谈优化都是耍流氓,所以有谁跟你说在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开发人员有牢固的基础知识,否则出现了问题还不知道怎么分析查找。

【论文复现】一种基于价格弹性矩阵的居民峰谷分时电价激励策略【需求响应】(Matlab代码实现)内容概要:本文介绍了一种基于价格弹性矩阵的居民峰谷分时电价激励策略,旨在通过需求响应机制优化电力系统的负荷分布。该研究利用Matlab进行代码实现,构建了居民用电行为与电价变动之间的价格弹性模型,通过分析不同时间段电价调整对用户用电习惯的影响,设计合理的峰谷电价方案,引导用户错峰用电,从而实现电网负荷的削峰填谷,提升电力系统运行效率与稳定性。文中详细阐述了价格弹性矩阵的构建方法、优化目标函数的设计以及求解算法的实现过程,并通过仿真验证了所提策略的有效性。; 适合人群:具备一定电力系统基础知识和Matlab编程能力,从事需求响应、电价机制研究或智能电网优化等相关领域的科研人员及研究生。; 使用场景及目标:①研究居民用电行为对电价变化的响应特性;②设计并仿真基于价格弹性矩阵的峰谷分时电价激励策略;③实现需求响应下的电力负荷优化调度;④为电力公司制定科学合理的电价政策提供理论支持和技术工具。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,深入理解价格弹性建模与优化求解过程,同时可参考文中方法拓展至其他需求响应场景,如工业用户、商业楼宇等,进一步提升研究的广度与深度。
### 云服务器配置对比:48G vs 816G #### 性能差异 - **计算能力**:816G的实例相比48G实例拥有更高的CPU心数和更大的内存容量,能够处理更复杂的计算任务和更大的并发请求。对于需要高计算性能的应用场景(如大数据分析或机器学习),816G实例更具优势[^1]。 - **内存利用率**:较大的内存可以支持更多的缓存数据,减少磁盘I/O操作,从而提高整体响应速度。如果应用依赖于内存中的数据结构(如Redis缓存),16GB内存将显著提升性能。 #### 成本与扩展性 - **成本考量**:48G实例的价格通常低于816G实例,适合预算有限但用户规模较小的情况。然而,随着用户量的增长,可能需要水平扩展(增加更多48G实例)来满足需求,这可能导致总成本上升。 - **扩展性**:816G实例更适合垂直扩展策略,即通过升级单个实例规格来应对增长的负载,减少了管理多个小实例的复杂度。 --- ### 阿里云资源部署方案建议 #### 云服务器选择 根据用户分布和预期流量,推荐以下配置: - **亚太东南1(新加坡)**:部署一台816G实例,用于承载中国香港、东南亚、东亚及中东地区的流量。 - **美西1(硅谷)**:部署一台48G实例,专注于服务美国及欧洲用户。 这种配置平衡了成本与性能需求,同时确保关键区域的用户体验。 ```bash # 创建816G实例示例 aliyun ecs RunInstances --RegionId ap-southeast-1 --InstanceType ecs.g6.large --ImageId ubuntu_20_04_64 --SecurityGroupId sg-bp1xxxxxxxxx --VSwitchId vsw-bp1xxxxxxxxx --InstanceName WebServer-SG-HighPerf --Amount 1 ``` --- #### 网关配置 使用阿里云SLB实现跨地域负载均衡,具体配置如下: - **监听规则**:设置基于地理位置的转发规则,确保用户被路由到最近的云服务器节点。 - **健康检查**:启用TCP/HTTP健康检查功能,自动移除不可用实例以保障服务稳定性。 ```bash # 创建SLB监听示例 aliyun slb CreateLoadBalancerListener --LoadBalancerId lb-bp1xxxxxxxxx --ListenerPort 80 --BackendServerPort 80 --Protocol http --Bandwidth -1 ``` --- #### MySQL云数据库部署 为支持并发访问并保证数据一致性,建议采用主从架构: - **主实例**:位于新加坡,规格为`rds.mysql.t4g.xlarge`(816G),负责写操作。 - **从实例**:位于硅谷,规格为`rds.mysql.t4g.large`(48G),提供只读副本以分担负载。 通过阿里云DTS(Data Transmission Service)实现主从同步,并定期监控延迟指标以确保备库无明显滞后[^2]。 --- #### QPS优化措施 针对预计的最大QPS(25,000),可以通过以下手段提升系统性能: - **缓存层引入**:部署Redis集群存储热点数据,降低MySQL查询压力。 - **CDN加速**:利用阿里云CDN分发静态资源,减少源站带宽消耗。 - **代码级优化**:对数据库查询语句进行索引优化,避免全表扫描。 --- #### 域名与DNS配置 购买域名并通过阿里云解析服务配置智能DNS: - **录类型**:添加A录指向SLB公网IP地址。 - **智能解析**:开启基于地理位置的解析功能,使用户访问最近的节点。 ```bash # 添加DNS录示例 aliyun alidns AddDomainRecord --DomainName example.com --RR www --Type A --Value 192.168.1.1 ``` --- ### 总结 综合考虑用户分布、流量预测及成本因素,推荐在新加坡部署816G实例,在硅谷部署48G实例,并辅以SLB、RDS主从架构以及CDN加速等组件构建完整的解决方案。此方案既保证了系统的高性能与可靠性,又具备良好的扩展性和灵活性。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值