mysql问题排查:
1.问题描述:
连接不可用,请求在30000ms后超时。
2.问题截图:
[2022-11-03 17:37:55.441] [ERROR] [] [pool-2-thread-1] org.hibernate.engine.jdbc.spi.SqlExceptionHelper:142 >> HikariPool-1 - Connection is not available, request timed out after 30000ms.
[2022-11-03 17:37:55.446] [ERROR] [] [pool-2-thread-1] com.hermes.service.impl.CffexService:81 >> asdasd
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:447)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:376)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:560)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:347)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:149)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy169.updateSendData(Unknown Source)
at com.hermes.service.impl.FileSendDetailCommonService.updateOrInsertFileSendDetailVO(FileSendDetailCommonService.java:2426)
at com.hermes.service.impl.FileSendDetailCommonService.sendByShenZhenTong(FileSendDetailCommonService.java:2373)
at com.hermes.service.impl.FileSendDetailCommonService.send(FileSendDetailCommonService.java:2094)
at com.hermes.service.impl.FileSendDetailCommonService.sendByPriority(FileSendDetailCommonService.java:2043)
at com.hermes.service.impl.FileSendDetailCommonService.fileSend(FileSendDetailCommonService.java:1251)
at com.hermes.service.impl.FileSendDetailCommonService$$FastClassBySpringCGLIB$$95c314bb.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:353)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:353)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
at com.hermes.service.impl.FileSendDetailCommonService$$EnhancerBySpringCGLIB$$a39d3b1a.fileSend(<generated>)
at com.hermes.service.impl.CffexService.lambda$send$0(CffexService.java:79)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:48)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:107)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:134)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getConnectionForTransactionManagement(LogicalConnectionManagedImpl.java:250)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.begin(LogicalConnectionManagedImpl.java:258)
at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.begin(JdbcResourceLocalTransactionCoordinatorImpl.java:246)
at org.hibernate.engine.transaction.internal.TransactionImpl.begin(TransactionImpl.java:83)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.beginTransaction(HibernateJpaDialect.java:184)
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:402)
... 42 common frames omitted
Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.
at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:583)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:186)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:145)
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112)
at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:38)
at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:104)
... 49 common frames omitted
3.问题排查
3.1查询mysql的variables参数配置:
①**查询所有参数配置:**show GLOBAL VARIABLES;
②**查询所有连接用户:**show full PROCESSLIST;
③**查询最大连接数:**show variables like ‘%max_connections%’;
④ **实时的数据库现在的有效连接数:**show status like ‘%Threads_connected%’;
⑤**数据库新建的连接数: **show status like ‘%Threads_create%’;
3.2问题定位
线上mysql配置的最大连接数是1024,实时查询数据库新建的连接数已经远远超过这个数目,并且查看了用户的进程,有些连接还是非常耗时的,导致MySQL连接数不能及时归还,导致连接数不够,从而从MySQL拿连接的时候,报错:连接不可用,请求在30000ms后超时。
如果数据库使用的并不多,也就是业务并不繁忙,那么可能以上数值都够用的,如果高并发,大流量的情况,很显然,数据库会吃不消报警的。如何解决?更改数据库的设定,使得它可以打开更多的表,保持更多的连接即可。
3.3问题解决
方案一: 既然连接太多,减少连接 ,显然这不太合适。
方案二: 修改MySQL配置,扩大最大可连接数。
参考方案二,操作如下:
方法一:
进入MySQL用命令行修改,但是MySQL重启后就失效了,需要重新设置。(不推荐)
命令如下:
1、show variables like ‘max_connections’;(查看当前最大连接数)
2、set global max_connections=4096;(设置最大连接数为4096)
方法二:
修改MySQL配置文件,永久生效(推荐)
1、查看数据库的安装路径 :show global variables like ‘%basedir%’;
2、进入MySQL安装目录,打开my.ini或my.cnf文件;
3、输入/max_connections,查询max_connections配置参数
4、查看max_connections=1000的配置参数,修改为max_connections=4096;如果没有这个参数,直接添加max_connections=4096即可;
5、保存配置文件并退出,重启MySQL服务即可。
调整数据库配置文件-修改配置文件my.cnf:
cd /etc
sudo vi my.cnf
添加配置:
max_connections=4096
net_read_timeout=7200
net_write_timeout=7200
重启数据库实例
service mysqld restart
4.小结:
关于连接数设置:
但是,这个连接数目的设置到底多大合适,需要考虑业务并发量最大的时候连接数能去到多少以及机器能承受多大的压力,保证操作压力是在MySQL承受范围之内的,这个需要进行适合的压测。
关于开发代码:
开发代码中注意有没有特别耗时并需要拿连接的操作
尽量不要在循环中连接数据库操作,更要避免循环中拿连接并耗时的操作,因为并发量大的时候,需要获取大量的连接,并且不能适时归还连接,导致数据库连接数不够。远程操作或者数据库操作都是比较耗网络、IO资源的,所以尽量不在循环里远程调用、不在循环里操作数据库,能批量一次性查回来尽量不要循环多次去查。(但是呢,如果是操作数据库,也不要一次性查太多数据哈,可以分批500一次酱紫)。
一个大循环、大的list操作,建议分批,再去造作每个小批次数据。
// ①使用google guava 工具提供方法对List进行分割
// 创建模拟list
List<Integer> tempList = new ArrayList<>();
for (int i = 1; i <= 20; i++) {
tempList.add(i);
}
//按每5个一组分割
List<List<Integer>> parts = Lists.partition(tempList, 5);
// 对分批list的具体业务处理 process(list) - 打印代替
System.out.println(parts);
// 使用google guava分割
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]]
// 此方式需要在项目中引入guava的pom包依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
// ②使用apache 的 collection 工具提供方法实现lsit分批
// 创建模拟list
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 按5个每组进行分割
List<List<Integer>> subs = ListUtils.partition(intList, 5);
// 对分批list的具体业务处理 process(list) - 打印代替
System.out.println(subs);
使用apache common collection 实现分批
[[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]