alibaba / druid

阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池
https://github.com/alibaba/druid/wiki
Apache License 2.0
27.88k stars 8.57k forks source link

Communications link failure #3800

Open yity opened 4 years ago

yity commented 4 years ago

druid版本为:1.1.21 如何解决此类报错呢? 程序报以下问题: content: 2020-05-13 02:13:11.680 c.y.f.c.filter.DubboFilter4Provider [ERROR] -

Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet successfully received from the server was 600,000 milliseconds ago. The last packet sent successfully to the server was 600,000 milliseconds ago.

The error may involve com.yp.point.service.impl.dao.point.PointRepository.insertUserPoint-Inline

The error occurred while setting parameter

laixiangshun commented 4 years ago

同样的问题,怎么解决的?

lowskiluk commented 4 years ago

同1.1.21版本,前后弄了一个星期终于解决了,这里说下我的解决方法: MySQL版本:5.6.x,5.7.x

直接说解决方法:

关键配置有下面三个:

数据库连接的心跳检测,如果数据库连接的空闲时间超过minEvictableIdleTimeMillis设置的时间,将进行检测并保活(告诉数据库这个连接有用,别杀掉)。

keep-alive=true

配置连接池里的连接的最大生存时间,如果超过这个时间,主动丢弃连接,单位是毫秒,7200000ms=2h

maxEvictableIdleTimeMillis=7200000

单位:秒,检测连接是否有效的超时时间

validationQueryTimeout=3

其他正常配置,仅供参考,根据自己的实际情况修改值:

initialSize=5 minIdle=5 maxActive=20

配置获取连接等待超时的时间

maxWait=30000

配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒

timeBetweenEvictionRunsMillis=30000

配置一个连接在池中最小生存的时间,单位是毫秒,30000=30s

minEvictableIdleTimeMillis=30000 validationQuery=SELECT 'x' testWhileIdle=true testOnBorrow=true testOnReturn=true

下面是很唠叨的解决过程和一些原理

。。。不一定对,没时间的可以不看。(第5点要稍微看看)

查阅内外网各种资料,通常会有以下解决方法: 1.增加mysql数据库的wait_timeout时间,默认8小时,最大可设置一年,拉满拉满,设置成一年就可以无忧了。 2.升级druid版本,低版本有bug,升级就完事了。 3.testWhileIdle、testOnBorrow、testOnReturn这三个参数设置成true,minEvictableIdleTimeMillis设置得小一点,设置成30000吧,就是30秒,这样每30秒就会检测一次连接池里的链接有没有效,无效的抛弃,很稳。 4.MySQL的jdbc连接串里加上属性autoReconnect=true,如果连接池的连接断了MySQL会自动重连,完美。 5.不知道是谁把MySQL的wait_timeout改成很短的时间(大概三十秒),改回默认的8小时就解决问题了,真讨厌。

以上方法我都排列组合地试过了,皆无效,没有用!

下面开始说正事了,先说一下原理

1.druid适合什么系统? 通俗点讲,druid设计的初衷是给大并发大流量的系统使用的数据库连接池,当一个冷系统,用了druid作为连接池,就可能会出现Communications link failure的问题,什么是冷系统?就是这个系统可能隔一两天甚至一个月都不会有人使用的那种,例如某些系统的管理后台,结论就是冷系统不适合用druid(太冷的系统其实不需要连接池...),那么能不能强行用呢,答案是可以的,但是要稍微搞清楚druid和MySQL的wait_timeout原理才能配置好连接池参数从而避免这类错误。

2.为什么会出现Communications link failure这种情况? 跟MySQL的wait_timeout参数有关,这个参数默认8小时(下面统一说8小时),可以随时改,如果想永久改就要改my.ini里的值。wait_timeout的作用是:MySQL会将空闲时间超过8小时的连接自动关闭掉,这个动作是偷偷地,不动声色地,所以,druid是不知道哪个连接被MySQL关闭了的,当有人进来用系统,如果druid分配了一个已经关闭掉的连接,就会报Communications link failure。

3.testWhileIdle、testOnBorrow、testOnReturn我都设置成true了,或者testWhileIdle=true,另外两个为false,为什么不生效? 不是不生效,你可能误解了这三个参数的作用,当程序从连接池获取数据库连接的时候,先通过运行设置的SQL“select 'x'”来检测连接是否有效,如果获取到的连接是已经被MySQL数据库主动关闭的连接,那么程序还是会用这个连接来跑SQL,结果就是会报错,查看源码,druid对连接是否能用是通过java.sql.Connection.isClosed() 这个方法来判断的,这个方法有个注释,说明了这个方法只能检测到程序自己关闭的程序,如果是数据库方面关闭的连接,这个方法是检测不出来的,注释原文如下: “ Retrieves whether this Connection object has beenclosed. A connection is closed if the method closehas been called on it or if certain fatal errors have occurred.This method is guaranteed to return true only whenit is called after the method Connection.close hasbeen called.

This method generally cannot be called to determine whether aconnection to a database is valid or invalid. A typical clientcan determine that a connection is invalid by catching anyexceptions that might be thrown when an operation is attempted.

” 所以,偶尔出现Communications link failure是正常的,按照源码的逻辑,就算碰到了这种跑验证SQL就报错的连接,报错之后还会继续寻找有效连接,如果连接池里找不到,就会新建连接,所以正常情况下程序的运行是不会受影响的。但是,我发现某些情况下会有影响,造成的影响是,当前端操作某些查询时,会报这个错,然后前端就会一直在loading,不出结果,刷新之后会换一个连接池里的连接,如果又拿到一个死连,就又会一直loading,只能再刷新,如果连接池里的死连很多,就要刷新N次直到把死连都丢弃,拿到有效的连接才能正常查询得到正确结果。为什么会这样呢,目测是某些写得不好的低效率SQL导致的,这种情况要结合具体的程序和SQL来排查,这里就不展开了。

4.还有没有其他解决方法: 还真有,我试过将数据库的wait_timeout改成20秒,其他配置如上面所示,此时wait_timeout<timeBetweenEvictionRunsMillis+minEvictableIdleTimeMillis=60s,这样的话就能神奇地解决这个问题,但是wait_timeout设置得这么短,有点害怕,不敢这么搞,怕会频繁GC,也没有深究原理,反正就是能解决Communications link failure的问题。。。

5.一些要注意的点 MySQL的连接字符串里,如果你MySQL版本是5以上的,请不要加autoReconnect=true,不然就算你配置了我上面说的druid属性最终还是会报奇怪的错误。

xiehong0114 commented 1 year ago

@lowskiluk

你的意思是不要加autoReconnect=true&failOverReadOnly=false ??? image