SQLAlchemy长时间未请求数据库连接断开

2019年4月30日10:18:40 发表评论 277 views

环境:Python3.6 + Tornado + SQLAlchemy + MySQL

问题

部署在服务器上面的项目运行正常,第二天早上起来发现除了静态页面,凡是调用数据库操作的页面均无法访问,提示500错误,初步判断为数据库连接出现问题。

排查过程在这里就不说了,直接上结论

结论

在使用 create_engine 创建引擎时,如果默认不指定连接池设置的话,一般情况下,SQLAlchemy会使用一个 QueuePool 绑定在新创建的引擎上。并附上合适的连接池参数。

在这种情况下,当你使用了 session 后就算显式地调用 session.close(),也不能把连接关闭。连接会由QueuePool 连接池进行管理并复用。

解决办法

1. 修改 mysql wait_timeout 参数

让这个时间大于连接池的回收时间(修改配置文件需要重启数据库,不推荐!)

2. Flask可以直接修改数据库连接池的配置

数据库连接池都会带有一个参数:回收时间(就是一定时间内不使用就会回收),修改这个参数的值,不要大于 wait_timeout 的值即可。在flask-SQLAlchemy中有个配置是 SQLALCHEMY_POOL_RECYCLE(多之后对线程池中的线程进行一次连接的回收),如果这个值是 -1 代表永不回收,Flask-SQLALchemy 自动设定这个值为 2 小时,我们可以将这个值设置的小于 wait_timeout 参数的值也就是8小时即可。

3.禁用SQLAlchemy提供的数据库连接池

只需要在调用 create_engine 是指定连接池为 NullPool,SQLAlchemy就会在执行 session.close() 后立刻断开数据库连接。当然,如果 session 对象被析构但是没有被调用 session.close(),则数据库连接不会被断开,直到程序终止。

SQLAlchemy长时间未请求数据库连接断开

查看原因展开

原因

连接池连接 mysql 数据库失败,应该是 mysql 数据库连接超时,mysql 数据库配置文件存在以下两个参数,是负责管理连接超时的。

1. interactive_timeout:针对交互式连接

2. wait_timeout:针对非交互式连接。

所谓的交互式连接,即在 mysql_real_connect() 函数中使用了 CLIENT_INTERACTIVE 选项。说得直白一点,通过mysql客户端连接数据库是交互式连接,通过jdbc连接数据库是非交互式连接。

这两个参数默认都是28800秒,即8小时,也就是超过8小时的连接就会自动失效。这本身并没什么问题,真正的问题是:我们做项目一般使用数据库连接池来获取连接,连接池里的连接可能会较长时间不关闭,等待被使用,这就与 mysql 连接超时机制起冲突了,当连接池配置永不关闭或者关闭时间超过8小时就会出现我所遇到的问题。

当超过8个小时没有新的数据库请求的时候,数据库连接就会断开,如果我们连接池的配置是用不关闭或者关闭时间超过8小时,这个时候连接池没有回收并且还认为连接池与数据库之间的连接还存在,就会继续连接,但是数据库连接断开了,就会报错数据库连接失败!

 

  • 微信小程序
  • 关注微信小程序
  • weinxin
  • 微信公众号
  • 关注微信公众号
  • weinxin
Wangxiao

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: