flask-sqlalchemy模块中mysql连接失效问题记录
sqlalchemy是python中使用mysql的orm框架
flask中使用sqlalchemy的架构图
调用方式
db.engine.execute(sql)
: 从mysql的连接池获取一个连接,执行完自动commit,归还连接到连接池中使用orm的session的方式调用
1
2
3
4db.session.commit()
db.session.rollback()
db.session.close()
db.session.remove(): 底层会调用db.session.close()
线程和session的关系
flask中使用sqlalchemy的时候,每个线程都可以直接用
db.session
获得session,使用orm的model.query
方式,实际上也是调用了session
- 每个线程有自己的threadlocal的session对象,并且随着线程销毁,会自动释放session,也就是会隐式调用session.remove,释放session的连接
多线程两种使用:
- t1=threading.Thread(…);
- 线程池: future= pool.submit(…).
- 方法1的线程使用完以后自动销毁=>session自动销毁=>连接自动释放;
- 方法2的线程使用完以后归还线程池=>session手动销毁=>连接释放。
- 不使用线程池=>连接自动释放;
- 使用线程池=>连接手动释放.
- 手动释放的方法:
db.session.remove()
空闲连接超时与连接释放bug
前面说到使用线程池时,连接没有自动释放,一直维护在线程的threadlocal存储中(tls)。那么这样似乎也没有什么关系,只要线程池大小<连接池大小,这样连接池有空闲连接,每个线程也有自己的连接可以用,一切似乎也相安无事。然而,这里有一个之前没有提到的机制:空闲连接超时回收。
mysql服务会定时清理掉连接超时的连接,这样客户端保存的该连接的引用都失效了
查看mysql超时时间的命令
1
2show global variables like 'wait_timeout';
set global wait_timeout=10*60; -- seconds
flask会定期检查连接池里的连接,删除超时的连接,重新向mysql申请连接,然后放入到连接池中,涉及到的配置参数
app.config['SQLALCHEMY_POOL_RECYCLE']
发生bug的可能原因
- mysql服务端清除了空闲时间过长的连接;
- 线程池中线程一直不销毁,因此持有了活了很久的session;
- 活了很久的session持有了空闲很久的连接, 这个连接其实已经被服务端销毁了,因此已经不可用了,但是由于其一直没有归还到连接池中,因此一直没有得到更新。
- 此时web服务收到数据请求,使用该线程中的该session中的连接,就会抛异常了,因为连接已经不可用了。
报错提示
1 | MySQL server has gone away |