Недавно моя программа Flask обнаружила ошибку Lost connection to MySQL server during query
. Я искал некоторые документы и обсуждения. Кажется, что MySQL отключил соединение, но flask не перезапустил эти соединения с истекшим сроком действия.
Сейчас я использую Flask_SQLAlchemy==2.4.4
. Из документации flask 2.x я узнал, что настройка SQLALCHEMY_POOL_RECYCLE может регулировать время для flask для повторного использования просроченных соединений. Я попробовал его установить, но мою проблему это не решило.
Это моя демонстрация:
import time
import sqlalchemy as sql
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class TestData(db.Model):
id = sql.Column(sql.BigInteger, primary_key=True)
def create_app() -> Flask:
app: Flask = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://mysql_host"
app.config["SQLALCHEMY_POOL_SIZE"] = 1
app.config["SQLALCHEMY_POOL_RECYCLE"] = 3
db.init_app(app)
return app
def test():
app = create_app()
with app.app_context():
q1 = TestData.query.get(0)
print(q1)
time.sleep(10)
q2 = TestData.query.get(1)
print(q2)
test()
Я запустил SET GLOBAL wait_timeout = 8;
, чтобы установить время отключения MySQL на 8 с, а также настроил app.config["SQLALCHEMY_POOL_RECYCLE"] = 3
. После time.sleep(10)
запрос q2 по-прежнему не удался.
версия пакета
SQLAlchemy==1.3.22
Flask_SQLAlchemy==2.4.4
Flask==1.1.2
🤔 А знаете ли вы, что...
Python поддерживает множество парадигм программирования, включая процедурное, объектно-ориентированное и функциональное программирование.
Из документации по Setting Pool Recycle:
Обратите внимание, что аннулирование происходит только во время извлечения, а не для каких-либо соединений, которые находятся в извлеченном состоянии.
Ваш первый .get()
проверяет соединение, начинает транзакцию и выдает запрос SELECT. Поскольку вы не завершаете транзакцию, соединение остается «открытым» (удерживается в извлеченном состоянии), и через 10 секунд бездействия вы пытаетесь использовать то же самое соединение снова.
Исправление состоит в том, чтобы завершить транзакцию после первого .get()
, чтобы соединение вернулось в пул. Через 10 секунд ваш следующий .get()
проверит соединение и оно будет перезапущено.
q1 = TestData.query.get(0)
print(q1)
db.session.rollback() # complete the transaction
time.sleep(10)
q2 = TestData.query.get(1)
print(q2)