写数据库的异常read-only分析

在做统计类分析的需求时遇到这样一个问题,当用Quartz定时启动一个任务去读取日志放入数据库时只能成功一次,第二个时间周期去操作数据库会报错,如下图:


写数据库的异常read-only分析_第1张图片

    当出错后数据不能进入数据库,原计划每天上班可查看的统计结果无法展现,由于开发这块的是经验尚浅的工程师,纠结了很久无法解决后每天默默的自己触发执行计算。实在无解后把这个问题抛出来大家一起探讨。经过查看代码发现他在定时任务的对象里创建了一个mysql连接,而下一个定时周期是24小时后,那么会不会是这个连接失效了?因为mysql有一个默认8小时空闲连接失效的机制。那么怎么保证让失效的连接重建呢?可以通过mybatis连接池的配置来解决,需要加如下一些参数:

maxWait="3000" 从池中取连接的最大等待时间,单位ms. 
initialSize="10"  初始化连接 
maxIdle="60"   最大空闲连接 
minIdle="10"   最小空闲连接
maxActive="80" 最大活动连接  

validationQuery = "SELECT 1"  验证使用的SQL语句 
testWhileIdle = "true"      指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除. 
testOnBorrow = "false"   借出连接时不要测试,否则很影响性能  timeBetweenEvictionRunsMillis = "30000"  每30秒运行一次空闲连接回收器  minEvictableIdleTimeMillis = "1800000"  池中的连接空闲30分钟后被回收  numTestsPerEvictionRun="10" 在每次空闲连接回收器线程(如果有)运行时检查的连接数量  removeAbandoned="true"  连接泄漏回收参数,当可用连接数少于3个时才执行  removeAbandonedTimeout="180"  连接泄漏回收参数,180秒,泄露的连接可以被删除的超时值  


使用上述配置,连接池在返回数据库连接给申请者时会多执行一条sql语句来确保该连接的有效性。如果数据库方已经关闭了,连接池会重新建立连接并返回给申请者。通过测试似乎跟testWhileIdle没有关系,不管其是true或false都正常工作。在构造GenericObjectPool [BasicDataSource在其createDataSource () 方法中也会使用GenericObjectPool]时,会生成一个内嵌类Evictor,实现自Runnable接口。如果timeBetweenEvictionRunsMillis大于0,每过 timeBetweenEvictionRunsMillis毫秒Evictor会调用evict()方法,检查连接池中的连接的闲置时间是否大于 minEvictableIdleTimeMillis毫秒(_minEvictableIdleTimeMillis小于等于0时则忽略,默认为30分钟),是则销毁此对象,然后调用ensureMinIdle方法检查确保池中对象个数不小于_minIdle。如果连接池的连接数小于最小空闲连接数,则创建数据库连接,同时检查连接池的连接是否小于maxIdle,是则把刚创建的连接放入连接池中,否则销毁此对象。

上述方式一能确保不出现本文开头提到的错误,但是不好的方面是每次执行sql时都会额外执行一条提供的validationQuery sql;第二种方式在数据库重启后minEvictableIdleTimeMillis毫秒前访问web应用,连接数据库使用的还是连接池中老的连接,所以还会出现上述的错误,timeBetweenEvictionRunsMillis和minEvictableIdleTimeMillis也不宜设置过小,会加重系统开销。根据具体情况来考虑使用哪种方式。对于数据库可能会经常重启,web应用和数据库机器的网络连接不稳定,可以采取第一种方式,否则使用第二种。由于mysql的默认最大空闲时间8小时,所以只要把minEvictableIdleTimeMillis设置小于此值即可。例如配置每十分钟检查超过空闲一个小时的连接

你可能感兴趣的:(写数据库的异常read-only分析)