Flask应用并发读取MySQL数据库时出现阻塞及连接丢失问题求助
问题描述
我有个Flask程序,在/路由的GET方法里用Python访问MySQL数据库,代码如下:
""" 返回当前用户的现金 """ def returncash(argdb): argdb.execute("SELECT cash FROM fin_users WHERE id = %s", (session['user_id'],)) row = argdb.fetchall() return row[0]['cash']
这段代码大部分时候正常,但快速刷新两次页面程序就会挂起,问题出在argdb.execute那一行。看起来像是同时访问同一条记录导致冻结,没有错误提示,就是卡着不往下执行。多刷几次会出现IndexError: bytearray index out of range错误。
我试着加表锁解决,改成这样:
""" 返回当前用户的现金 """ def returncash(argdb): argdb.execute("LOCK TABLES fin_users READ") argdb.execute("SELECT cash FROM fin_users WHERE id = %s", (session['user_id'],)) row = argdb.fetchall() argdb.execute("UNLOCK TABLES") return row[0]['cash']
结果又出现新的IndexError错误。
--- 编辑 ---
按照建议执行python -m pip install mysql-connector-python更新了驱动,更新成功后快速刷新页面,程序不再冻结,但立刻报Lost connection to MySQL server during query错误,打算换个数据库驱动试试。
问题根源
核心问题是数据库连接/游标被多请求共享,导致并发访问时数据混乱。Flask默认是多线程的,每个请求在不同线程里执行,如果你的db对象是全局共享的,多个线程同时用同一个游标执行查询,就会出现数据包解析错误、连接丢失这类问题。
解决步骤
1. 确保每个请求使用独立的数据库连接/游标
不要在全局初始化一个连接或游标,而是在每个请求里创建新的连接,或者使用连接池。可以用Flask的请求钩子来管理连接:
from flask import Flask, g import mysql.connector app = Flask(__name__) def get_db(): if 'db' not in g: g.db = mysql.connector.connect( host='localhost', user='your_user', password='your_password', database='your_db' ) return g.db @app.teardown_appcontext def close_db(error): if 'db' in g: g.db.close() @app.route('/') def index(): db = get_db() cursor = db.cursor(dictionary=True) # 用字典游标方便按字段名取值 cursor.execute("SELECT cash FROM fin_users WHERE id = %s", (session['user_id'],)) row = cursor.fetchone() cursor.close() return str(row['cash']) if row else "无数据"
这样每个请求都会拿到独立的连接,避免多线程共享导致的冲突。
2. 替换数据库驱动(推荐)
mysql-connector-python偶尔会有这类并发问题,换成更成熟的pymysql试试:
- 安装:
pip install pymysql - 使用方式和mysql.connector类似,只需要改导入和连接部分:
import pymysql def get_db(): if 'db' not in g: g.db = pymysql.connect( host='localhost', user='your_user', password='your_password', database='your_db', cursorclass=pymysql.cursors.DictCursor # 字典游标 ) return g.db
pymysql在并发场景下稳定性更好,这类数据包错误出现的概率低很多。
3. 不要手动加表锁
你之前加的表锁完全没必要,MySQL的InnoDB引擎默认支持行级锁,读操作不会阻塞其他读请求。手动加表锁反而会加剧并发问题,导致请求排队甚至死锁。
4. 检查MySQL服务器配置
如果换驱动后还是出现连接丢失,检查MySQL的wait_timeout和interactive_timeout参数,确保不会过早断开空闲连接。可以在my.cnf(或my.ini)里调整:
wait_timeout = 28800 interactive_timeout = 28800
修改后重启MySQL服务。
总结
优先用请求级的独立连接+成熟驱动(pymysql),这是解决这类并发问题最直接的方案。全局共享连接/游标在多线程环境下本来就是错误的用法,一定要避免。
内容的提问来源于stack exchange,提问作者balakuz

