服务性能优化总结

一、数据库连接池耗时优化

1.1 问题排查

在链路工具上查看偶尔会有性能抖动,排查后发现是dbcp耗时过长
在这里插入图片描述

1.1.1 线上dbcp配置情况

线上dbcp数据库连接池配置,如下:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${db.jdbc.driverClassName}"/>
<property name="url" value="${db.jdbc.url}"/>
<property name="username" value="${db.jdbc.username}"/>
<property name="password" value="${db.jdbc.password}"/>
<property name="maxActive" value="20"/>
<property name="maxIdle" value="3"/>
<property name="maxWait" value="15000"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="180000"/>
</bean>

当前使用的版本是dbcp 1.4 版本

<dependency>
 <groupId>commons-dbcp</groupId>
 <artifactId>commons-dbcp</artifactId>
 <version>1.4</version>
</dependency>
1.1.2 dbcp配置项含义
参数默认值说明
username\传递给JDBC驱动的用于建立连接的用户名
password\传递给JDBC驱动的用于建立连接的密码
url\传递给JDBC驱动的用于建立连接的URL
driverClassName\使用的JDBC驱动的完整有效的Java 类名
initialSize0初始化连接:连接池启动时创建的初始化连接数量,1.2版本后支持
maxActive8最大活动连接:连接池在同一时间能够分配的最大活动连接的数量, 如果设置为非正数则表示不限制
maxIdle8最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制
minIdle0最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接,如果设置为0则不创建
maxWait无限最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数)超过时间则抛出异常,如果设置为-1表示无限等待
testOnReturnfalse是否在归还到池中前进行检验
testWhileIdlefalse连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,
则连接将被从池中去除.设置为true后如果要生效,validationQuery参数必须设置为非空字符串
minEvictableIdleTimeMillis1000 * 60 * 30连接在池中保持空闲而不被空闲连接回收器线程(如果有)回收的最小时间值,单位毫秒
numTestsPerEvictionRun3在每次空闲连接回收器线程(如果有)运行时检查的连接数量
timeBetweenEvictionRunsMillis-1在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. 如果设置为非正数,则不运行空闲连接回收器线程
validationQuerynullSQL查询,用来验证从连接池取出的连接,在将连接返回给调用者之前.如果指定,则查询必须是一个SQL SELECT并且必须返回至少一行记录
testOnBorrowTRUE是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个.

。validationQuery(测试连接的sql语句) : validationQuery= “SELECT 1”

。testWhileIdle (连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除) : testWhileIdle = “true”

。testOnBorrow = “false” 借出连接时不要测试,否则很影响性能,性能会下降7-10倍

。timeBetweenEvictionRunsMillis (每30秒运行一次空闲连接回收器(独立线程)。并每次检查3个连接,

如果连接空闲时间超过30分钟就销毁。销毁连接后,连接数量就少了,如果小于minIdle数量,就新建连接,维护数量不少于minIdle)

。timeBetweenEvictionRunsMillis = “30000”

。minEvictableIdleTimeMillis(池中的连接空闲x毫秒后被回收,默认30分钟):minEvictableIdleTimeMillis = “1800000”

。numTestsPerEvictionRun(在每次空闲连接回收器线程(如果有)运行时检查的连接数量,默认值就是3.) : numTestsPerEvictionRun =“3”

1.1.3 dbcp现有配置分析

我们的应用对有效参数只配置了

<property name="maxActive" value="20"/>
<property name="maxIdle" value="3"/>
<property name="maxWait" value="15000"/>

<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="180000"/>

因此,当前数据库连接池配置的含义转换下,就是:

初始化连接数initialSize是0,

最小空闲连接数minIdle是0,

最大连接数maxActive是20,

最大空闲数maxIdle是3,

最大排队等待时间maxWait是15秒,

检查空闲连接线程的时间timeBetweenEvictionRunsMillis是60秒,

空闲线程保持时间多久会被回收minEvictableIdleTimeMillis是30分钟。

1.1.4 线上数据库情况分析

在这里插入图片描述
按照user维度查询,当前连接数是120个,实例是60台,平均每个实例保持2个连接数,由于当前没有配置minIdle,也就是minIdle默认是0,当空闲数大于休眠时间是可以被回收的,

因此,当一个应用运行连接数达到≥2时刻会产生连接数扩容,小于maxIdle=3后会回收,这里是可能产生性能问题的地方。
在这里插入图片描述
再按照连接数维度查询,目前是2个物理分库6个逻辑分库,即A物理分库有1-3逻辑库,B物理分库有4-6逻辑库,单个物理分库保持在360个已连接数(如图)总连接数大概为360*2=720个,实例是60个,每个实例平均12个连接数,每个应用都会对接6个逻辑库,因此每个应用日常使用的单个逻辑库数据库连接平均也是2个,与user维度分析是一致的。

1.2 原因分析

出现耗时一般是创建连接池创建连接。在当前配置下,可能个别应用出现空闲连接较长被回收,但是minIdle未配置最小空闲保持数量,导致回收后又扩容连接数的问题从而影响性能。关于数据库连接池的配置之前没有太多考究和分析,应该是其他项目直接粘贴过来的,以后关于核心配置项都要充分思考和实践得出可行性结论。

1.3 解决过程

1.3.1 数据库连接数规划

dba反馈,数据库连接数的限制,物理机一般为3000最大,虚拟机一般为1500最大,不可以扩容连接数,要根据情况调整数据库连接池、应用机器数量。

基于当前系统架构,2个物理分库,6个逻辑分库,A物理分库(1、2、3逻辑库)B物理分库(4、5、6逻辑库),目前应用实例为30台,单机单实例配置。

根据逻辑拆分后,对现有连接数做资源规划,由于主库查询主要是学校信息,查询量较小,分配最大150连接数,其余分库分表均为学生信息,每个逻辑分库分配最大450连接数。

根据当前数据库能力,评估下最大负载能力,评估下数据库连接数的可用性

场景tp999理论连接数分析理论最大支持qps
最大负载10ms30台实例,每个实例的每个逻辑分库的最大连接数被限制为15个,30*15*(1000ms/10ms)=45000qps,即单个逻辑库可支持45000qps,由于拥有6个逻辑分库,理想情况下水平散列分摊到6个库上,即45000qps*6=27Wqps,由于目前拆分两个物理库,所以物理单库负载为27Wqps/2=13.5Wqps27Wqps,物理单库13.5Wqps
最大负载20ms30台实例,每个实例的每个逻辑分库的最大连接数被限制为15个,30*15*(1000ms/20ms)=22500qps,即单个逻辑库可支持22500qps,由于拥有6个逻辑分库,理想情况下水平散列分摊到6个库上,即22500qps*6=13.5Wqps,由于目前拆分两个物理库,所以物理单库负载为13.5Wqps/2=6.75Wqps13.5Wqps,物理单库6.75Wqps
最大负载30ms30台实例,每个实例的每个逻辑分库的最大连接数被限制为15个,30*15*(1000ms/30ms)≈15000qps,即单个逻辑库可支持15000qps,由于拥有6个逻辑分库,理想情况下水平散列分摊到6个库上,即15000qps*6=9Wqps,由于目前拆分两个物理库,所以物理单库负载为9Wqps/2=4.5Wqps9Wqps,物理单库4.5Wqps

综上分析:以上均是理论值,实际上数据库机器配置承载不了,随着qps上升数据库CPU、内存的可用性均会线性下降,性能也会线性下降。仅分析在没有慢SQL,没有超巨大流量撞库的情况下,数据库连接数是充足的,在一般情况下是用不满的

1.3.2 连接池参数调优

日常调用评估

日常调用MANAGE的核心接口tp999保持在10ms-30ms之间,日常qps在1200-1500左右

场景qpstp999理论连接数分析逻辑分库连接数(60实例)理论分析结论
日常120010ms1200qps/(单个连接数每秒吞吐量 1000ms/10ms)= 12,即1200qps在性能10ms情况下需要12个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即12/6=2,即每个逻辑库负载2个连接数;
极端情况下聚集单库上,即每个逻辑库负载12个连接数
理想 2 / 60 = 至少1
极端 12 / 60 = 至少1
1200qps,10ms,单个应用需要1个连接数配置
日常120020ms1200qps/(单个连接数每秒吞吐量 1000ms/20ms)= 24,即1200qps在性能20ms情况下需要24个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即24/6=4,即每个逻辑库负载4个连接数
极端情况下聚集单库上,即每个逻辑库负载24个连接数
理想 4 / 60 = 至少1
极端 24 / 60 = 至少1
1200qps,20ms,单个应用需要1个连接数配置
日常120030ms1200qps/(单个连接数每秒吞吐量 1000ms/30ms)≈36,即1200qps在性能30ms情况下需要36个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即36/6=6,即每个逻辑库负载6个连接数
极端情况下聚集单库上,即每个逻辑库负载36个连接数
理想 6 / 60 = 至少1
极端 36 / 60 = 至少1
1200qps,30ms,单个应用需要1~2个连接数配置
日常150010ms1500qps/(单个连接数每秒吞吐量 1000ms/10ms)= 15,即1500qps在性能10ms情况下需要15个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即15/6=2.5,即每个逻辑库负载2.5个连接数
极端情况下聚集单库上,即每个逻辑库负载15个连接数
2.5向上取整3
理想 3 / 60 = 至少1
极端 15 / 60 = 至少1
1500qps,10ms,单个应用需要1个连接数配置
日常150020ms1500qps/(单个连接数每秒吞吐量 1000ms/20ms)= 30,即1500qps在性能20ms情况下需要30个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即30/6=5,即每个逻辑库负载5个连接数
极端情况下聚集单库上,即每个逻辑库负载30个连接数
理想 5 / 60 = 至少1
极端 30/ 60 = 至少1
1500qps,20ms,单个应用需要1个连接数配置
日常150030ms1500qps/(单个连接数每秒吞吐量 1000ms/30ms)≈ 45,即1500qps在性能30ms情况下需要45个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即45/6=7.5,即每个逻辑库负载7.5个连接数
极端情况下聚集单库上,即每个逻辑库负载45个连接数
7.5向上取整8
理想 8 / 60 = 至少1
极端 45/ 60 = 至少1
1500qps,30ms,单个应用需要1个连接数配置

综上分析:日常调用量,由于调用量小,考虑散列极端情况,60个实例情况下,单个应用的数据库连接数需要1个,该情况能够覆盖日常场景对数据库连接池的要求

大促调用评估

大促峰值BIZ调用约为1W,由于缓存拦截撞库小于1W

场景qpstp999理论连接数分析逻辑分库连接数(60实例)理论分析结论
大促1000010ms10000qps/(单个连接数每秒吞吐量 1000ms/10ms)= 100,即10000qps在性能10ms情况下需要100个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即100/6≈16.6,即每个逻辑库负载16.6个连接数
16.6向上取整
17 / 60 = 至少1
10000qps,10ms,单个应用需要1个连接数配置
大促1000020ms10000qps/(单个连接数每秒吞吐量 1000ms/20ms)= 200,即10000qps在性能20ms情况下需要200个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即200/6≈33.3,即每个逻辑库负载33.3个连接数
33.3向上取整
34 / 60 = 至少1
10000qps,20ms,单个应用需要1个连接数配置
大促1000030ms10000qps/(单个连接数每秒吞吐量 1000ms/30ms)≈ 303,即10000qps在性能30ms情况下需要303个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即303/6=50.5,即每个逻辑库负载50.5个连接数
50.5向上取整
51 / 60 = 至少1
10000qps,30ms,单个应用需要1个连接数配置
大促1000050ms10000qps/(单个连接数每秒吞吐量 1000ms/50ms)≈ 500,即10000qps在性能50ms情况下需要500个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即500/6≈83.3,即每个逻辑库负载83.3个连接数
83.3向上取整
84 / 60 = 至少2
10000qps,50ms,单个应用需要2个连接数配置

综上分析:峰值调用量,由于调用量大,不考虑散列极端情况,单个应用的数据库连接数需要在1~2个之间浮动,该情况能够覆盖压测对数据库连接池的要求

压测调用评估

按照大促峰值5倍压测,极端情况5W全部穿透缓存进行撞库,分析如下

场景qpstp999理论连接数分析逻辑分库连接数(60实例)理论分析结论
压测5000010ms50000qps/(单个连接数每秒吞吐量 1000ms/10ms)= 500,即50000qps在性能10ms情况下需要500个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即500/6≈83.3,即每个逻辑库负载83.3个连接数
83.3向上取整
84 / 60 = 至少2
50000qps,10ms,单个应用需要2个连接数配置
压测5000020ms50000qps/(单个连接数每秒吞吐量 1000ms/20ms)= 1000,即50000qps在性能20ms情况下需要1000个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即1000/6≈166.6,即每个逻辑库负载166.6个连接数
166.6向上取整
167 / 60 = 至少3
50000qps,20ms,单个应用需要3个连接数配置
压测5000030ms50000qps/(单个连接数每秒吞吐量 1000ms/30ms)≈ 1515,即50000qps在性能30ms情况下需要1515个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即1515/6=252.5,即每个逻辑库负载252.5个连接数
252.5向上取整253
253 / 60 = 至少5
50000qps,30ms,单个应用需要5个连接数配置
压测5000050ms50000qps/(单个连接数每秒吞吐量 1000ms/50ms)≈ 2500,即50000qps在性能50ms情况下需要2500个连接数负载,
由于逻辑分库为6个,
理想情况下水平散列分摊到6个库上,即2500/6≈416.6,即每个逻辑库负载416.6个连接数
416.6向上取整417
417 / 60 = 至少7
50000qps,50ms,单个应用需要7个连接数配置

综上分析:峰值调用量,由于调用量大,不考虑散列极端情况,单个应用的数据库连接数需要在2~7个之间浮动,该情况能够覆盖压测对数据库连接池的要求

综合评估 & 配置调整

日常需求,60实例,1200~1500qps,单应用1个连接数
大促需求,60实例,1Wqps,单应用1~2个连接数
压测需求,60实例,5Wqps,单应用2~7个连接数

为了避免频繁创建和回收,设置initialSize为3,满足日常和大促的最大连接数要求;
minIdle为3,在满足日常及大促峰值最大使用情况下即时线程充足处于空闲状态也不要回收,保持最大使用情况的连接数量进行保持,避免频繁销毁创建;
maxIdle为5,超过该值进行回收,一般压测DB性能好也不会超过该值,若超过后可以回收减少资源消耗和占用
maxActive为7~8,为了分摊tph压力,提高单机CPU、内存利用率,采用单机双实例,共60实例进行负载
物理分库A,逻辑分库6073=1260 + 主库604=240,max最大1500,不超过limit限制
物理分库B,逻辑分库60
8*3=1440,max最大1440,不超过limit限制
在这里插入图片描述
单库配置

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${db.jdbc.driverClassName}"/>
<property name="url" value="${db.jdbc.url}"/>
<property name="username" value="${db.jdbc.username}"/>
<property name="password" value="${db.jdbc.password}"/>
<property name="initialSize" value="2"/>
<property name="minIdle" value="2"/>
<property name="maxIdle" value="3"/>
<property name="maxActive" value="4"/>
<property name="maxWait" value="2000"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="180000"/>
</bean>

物理A库(逻辑1-3库)配置

<bean id="targetDataSource1" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${db.jdbc.driverClassName}"/>
<property name="url" value="${db1.jdbc.url}"/>
<property name="username" value="${db1.jdbc.username}"/>
<property name="password" value="${db1.jdbc.password}"/>
<property name="initialSize" value="3"/>
<property name="minIdle" value="3"/>
<property name="maxIdle" value="5"/>
<property name="maxActive" value="7"/>
<property name="maxWait" value="2000"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="180000"/>
</bean>

物理B库(逻辑4-6库)配置

<bean id="targetDataSource1" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${db.jdbc.driverClassName}"/>
<property name="url" value="${db1.jdbc.url}"/>
<property name="username" value="${db1.jdbc.username}"/>
<property name="password" value="${db1.jdbc.password}"/>
<property name="initialSize" value="3"/>
<property name="minIdle" value="3"/>
<property name="maxIdle" value="5"/>
<property name="maxActive" value="8"/>
<property name="maxWait" value="2000"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="180000"/>
</bean>

1.4 实测结果

dbcp耗时过长问题解除,当前连接数满足日常、大促、压测各场景需要。

二、网络耗时问题排查

2.1 问题排查

有依赖方反馈偶尔RPC调用超时,于是进行排查,发现该时段机器批量超时
在这里插入图片描述
但是调用量无明显增加,至少本地服务没有流量洪峰
在这里插入图片描述

2.2 原因分析

初步判断,怀疑是网络问题,然后到链路工具去查看该时段内服务调用链的耗时情况
在这里插入图片描述
发现本地BIZ服务调用本地MANAGE服务存在网络超时,由于BIZ对外提供服务,之前压测存在网络问题全部机器都更换为万兆网卡,但是MANAGE的千兆网卡由于资源紧张暂时没有调整,结合PE排查后,建议更换千兆网卡为万兆网卡

2.3 解决过程

淘汰千兆网卡机器,更换为万兆网卡,去除服务不安全隐患问题

2.4 实测结果

外部RPC恢复正常,超时情况解决

三、缓存耗时问题排查

3.1 问题排查

在观察性能接口方法性能时,发现会有偶发性的tp999增高,如下图
在这里插入图片描述
跟踪了几轮后,发现经常是10.XXX.XXX.119这台机器ip有性能变差的问题,然后跟进链路工具查看链路耗时详情查看
在这里插入图片描述

3.2 原因分析

发现是Redis耗时较长,于是咨询运维同事如何排查Redis的耗时操作
答复是要查看是客户端延时还是服务端延时,如果是服务端延时看一下是否有慢查询,大key或热key

然后观察几轮后,发现均是10.XXX.XXX.119这台机器的Redis耗时过长,怀疑是网络问题,于是到R2M后台查看客户端延迟监控数据,果然基本都是10.XXX.XXX.119这台机器客户端性能差
在这里插入图片描述

3.3 解决过程

进一步沟通,反馈“这种是发生网络重传了,网络重传基本200ms左右”,看链路工具上单个方法的Redis调用次数是5次,这个业务方法正常应该会有2次,数据验证了这个说法
目前处理的方法是下线网络有问题的机器,后面替换掉。

3.4 实测结果

缓存耗时问题得到解决

四、日志耗时优化

4.1 问题排查

通过性能工具查看偶有耗时过长操作如下
在这里插入图片描述
定位该操作均不定时偶发在不同的、但同一个IP上,查看了每个耗时详情,均是logback耗时过长
在这里插入图片描述

4.2 原因分析

初步判断是logback同步写日志存在竞争导致耗时过长
一般日志的性能损耗有如下:
1、日志写文件同步锁竞争消耗
2、打印行号性能损失,而且接口调用量越大,线程争夺线程栈资源的情况越严重
关于日志优化的文章很多,可以百度查看,这里不加赘述

4.3 解决过程

1、日志异步写,但有丢日志风险,根据业务情况取舍
2、日志降级,借助统一配置中心可以实时动态修改日志级别,对日志打印进行降级操作,日志降级的好处一般是info级别日志不再进行打印和输出,这部分日志丢失,仅打印error级别日志,因此性能会提升很多
3、关闭行号打印后观察一段数据,因logback产生的抖动明显减少

4.4 实测结果

服务性能稳定,因logback导致的偶尔抖动消失

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大摩羯先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值