Python连接 MySQL 数据库的超时问题
当使用Pytohn的 Flask-SQLAlchemy
库操作 MySQL 数据时,出现'MySQL server has gone away' 了,是怎么回事呢?又该怎么办呢?分别从MySQL服务端和Python客户端来排查相关问题。
MySQL 服务端的现象
MySQL服务端的连接超时时间是由wait_timeout
变量决定的,超时时间有两种:当前会话超时和全局默认超时时间。MySQL的默认超时时间是8h,可以通过set wait_timeout
进行变更。通过以下的语句,可以修改MySQL的wait_timeout
变量。
> show variables like "%wait_timeout%"; # 查看当前会话的超时时间 +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | wait_timeout | 28800 | +--------------------------+-------+ > show global variables like "%wait_timeout%"; # 查看全局的会话超时时间 +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | wait_timeout | 800 | > set global wait_timeout=600; # 修改全局 wait_timeout
wait_timeout
的含义:当会话持续空闲,达到wait_timeout
规定的超时时间,MySQL 会关闭这个会话。通过show processlist
查看当前的会话,和会话空闲时间。
> show processlist; +-----+-------------+-----------+-------------------+---------+--------+--------------------------+------------------+----------+ | Id | User | Host | db | Command | Time | State | Info | Progress | +-----+-------------+-----------+-------------------+---------+--------+--------------------------+------------------+----------+ | 99 | root | localhost | <null> | Query | 0 | Init | show processlist | 0.0 | | 111 | root | localhost | test_database | Sleep | 2 | | <null> | 0.0 | +-----+-------------+-----------+-------------------+---------+--------+--------------------------+------------------+----------+
其中,Time
字段记录了会话的空闲时间,当Time
字段持续增长到 wait_timeout
的大小时,MySQL 就关闭这个会话,如果在达到wait_timeout
之前,会话上产生了新的请求,Time
会重新开始计时。
Flask-SQLAlchemy 客户端的情况
Flask-SQLAlchemy
中和超时相关的主要配置是SQLALCHEMY_POOL_RECYCLE
,含义是连接被自动回收的秒数,Flask-SQLAlchemy
把这个数值默认设置为2h
,如果连接空闲2h,则客户端回收该连接。
根据
Flask-SQLALchemy
文档的描述,SQLALCHEMY_POOL_RECYCLE
配置将会在后续版本中将会删除,可以使用SQLALCHEMY_ENGINE_OPTIONS
这个新配置项进行指定。
问题的产生
在企业中,一般会将MySQL的wait_timout
变量调低,比如我们公司设置为600s,以防止连接数过大,长久不释放。
这就导致了一个问题:当一个连接空闲600s后,MySQL一端就会将这个连接关闭,而 Flask-SQLAlchemy
一端维护的连接还未关闭,此时,当Flask-SQLAlchemy
使用老的连接向数据库发起请求时,由于MySQL一端已经关闭连接,此时Flask-SQLAlchemy
就出现如下的异常,显示MySQL 服务走丢了。
…… File "/home/lihz/.local/share/virtualenvs/site-verification-0gTRisYk/lib/python3.8/site-packages/MySQLdb/connections.py", line 254, in query _mysql.connection.query(self, query) sqlalchemy.exc.OperationalError: (MySQLdb._exceptions.OperationalError) (2006, 'MySQL server has gone away')
如果,连接空闲时间达到了Flask-SQLAlchemy
中SQLALCHEMY_POOL_RECYCLE
配置所规定的数值,该连接会被Flask-SQLAlchemy
主动回收,此时,若Flask-SQLAlchemy
发起数据库操作,会创建新的连接,成功连接上数据库。
其实,产生这个错误的可能性还有很多,比如人为在MySQL上kill了连接,以上描述的是比较常见的一种可能性。
问题的解决
针对这种默认超时配置不匹配导致的问题,我们有多种解决办法:
方法一:在发起数据库操作前,先向MySQL发起一个无意义的select请求,比如
select now()
或者select 1=1
。该操作的作用是,帮助Flask-SQLAlchemy
预先确认连接是否可用。如果连接不可用,通过抛出的异常,Flask-SQLAlchemy
就可以发现该连接已经被服务端关闭,从而主动回收该连接,当发起正式的数据库操作时,就会创建新的连接了。方法二:修改
Flask-SQLAlchemy
的SQLALCHEMY_POOL_RECYCLE
配置大小。将其修改为低于服务端wait_timout
变量即可。由于SQLALCHEMY_POOL_RECYCLE
配置处于废弃阶段,可以使用新的配置方法,如下:
SQLALCHEMY_DATABASE_URI = "mysql://test_user:xxx@localhost:3306/test_db?charset=utf8" SQLALCHEMY_ENGINE_OPTIONS = { "pool_recycle": 60 }
方法三:使用完连接后,主动关闭。
当然,为了方便,我选择了第二种方案。
参考:
https://flask-sqlalchemy.palletsprojects.com/en/2.x/config/
建议继续学习:
- 配置Nginx+uwsgi更方便地部署python应用 (阅读:105405)
- 如何成为Python高手 (阅读:53412)
- MySQL数据库在实际应用一些方面的介绍 (阅读:35435)
- python实现自动登录discuz论坛 (阅读:31595)
- python编程细节──遍历dict的两种方法比较 (阅读:19005)
- 每个程序员都应该学习使用Python或Ruby (阅读:16276)
- 使用python爬虫抓站的一些技巧总结:进阶篇 (阅读:12122)
- 30分钟3300%性能提升――python+memcached网页优化小记 (阅读:12140)
- 我的PHP,Python和Ruby之路 (阅读:11851)
- hbase介绍 (阅读:11041)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:昔我往矣 来源: 昔我往矣
- 标签: mysql python 数据库 问题
- 发布时间:2022-06-19 12:01:23
- [44] 如何拿下简短的域名
- [44] IOS安全–浅谈关于IOS加固的几种方法
- [43] Oracle MTS模式下 进程地址与会话信
- [43] 图书馆的世界纪录
- [41] 界面设计速成
- [40] android 开发入门
- [40] 【社会化设计】自我(self)部分――欢迎区
- [38] 读书笔记-壹百度:百度十年千倍的29条法则
- [37] 视觉调整-设计师 vs. 逻辑
- [34] 程序员技术练级攻略