MySQL断开重连

本文总阅读量

前记

今天上班发现在在跑的业务接口无法访问了,查看的日志后发现MySQL断开连接,只有重启web程序后才能恢复正常.MySQL断开重连这个问题第一次遇到时还是有点懵逼的,主要与库选型有关才造成发现了这个问题,还有就是对MySQL的了解不熟悉,才会造成MySQL断开重连的问题需要经过一顿搜索后才能查明原因并解决.

1.发生原因

其实发生这个问题比较苛刻,因为MySQL断开重连是MySQL会去探测与自己相关的连接,当超过了设定的timeout时间后,MySQL服务器会断开这个链接,使用show variables like '%timeout%';命令查询可以看到MySQL默认的超时时间是28800秒也就是8小时.而目前在写的业务并没有正式上线,所以基本除了自己人就没有用户在使用,所以睡了一觉后就会发现接口挂了.

自己在实习之前是只用Django写demo,Django自带的ORM除了让程序员可以用面向对象的思想去写SQL语句外,自己还做了一些封装如数据库链接池,还有就是针对与服务器链接断开时,会使用mysql的ping命令,检测并重新创建链接.

2.修复问题

在这个问题还没出现之前,MySQL相关部分代码是这样的:

1
2
3
4
5
6
7
# 使用的是aiiomysql库,autocommit是一个封装好的链接池,执行sql后会自动commit
# 这样还有其他细节,如果sql语句校验,重试等,不过与问题无关,就省略了
async with autocommit.acquire() as conn:
async with conn.cursor() as cur:
ret = await cur.execute(sql, param)
res = await cur.fetchall()
return ret, res

由于用到的是aiomysql库,在发生断开重连时,他会抛出一个aiomysql.OperationalError的错误,从Django的ORM封装可以知道只要重新PING就可以解决断开重连这个问题了,所以只要把这个错误捕获,并且在里面重新执行conn.ping即可.

1
2
3
4
5
6
7
8
9
async with autocommit.acquire() as conn:
async with conn.cursor() as cur:
try:
ret = await cur.execute(sql, param)
except aiomysql.OperationalError as e:
await conn.ping()
ret = await cur.execute(sql, param)
res = await cur.fetchall()
return ret, res

通过上面的代码即可修复断开重连的问题了,但是也有可能ping完还出现aiomysql.OperationalError错误,所以需要对整个操作层进行重试封装,才能比较完美的解决该问题.

查看评论