Flask(flask-restful)与PyMySQL并发异常问题求助
commands out of sync错误 我之前也碰到过一模一样的问题,这个错误本质是MySQL连接的串行特性导致的——同一个连接在同一时间只能处理一个查询,哪怕你每个请求新建游标,如果底层用的是同一个共享连接,并发请求时还是会互相干扰。结合你的情况,给你几个针对性的解决方案:
1. 绝对不要共享数据库连接(核心!)
PyMySQL的Connection对象不是线程安全的,你之前修改为每个视图新建游标但没解决问题,大概率是因为连接还是全局共享的。只要多个请求共用同一个连接,就会出现“上一个查询的结果还没处理完,下一个查询就挤进来”的冲突。
解决方案:每个请求使用独立连接
借助Flask的请求上下文钩子,在每个请求开始时创建专属连接,结束时自动关闭:
from flask import Flask, g import pymysql app = Flask(__name__) def get_db(): if 'db' not in g: # 每个请求新建独立连接,绑定到当前请求上下文 g.db = pymysql.connect( host='你的数据库地址', user='用户名', password='密码', db='数据库名', charset='utf8mb4' ) return g.db @app.teardown_appcontext def close_db(error): # 请求结束后自动关闭连接 db = g.pop('db', None) if db is not None: db.close()
然后不管是视图里还是装饰器里,都通过get_db()获取当前请求的专属连接,再创建游标:
def auth_decorator(f): def wrapper(*args, **kwargs): # 装饰器里也用当前请求的独立连接 db = get_db() cursor = db.cursor() cursor.callproc('check_user_auth', (request.args.get('token'),)) auth_result = cursor.fetchall() # 确保所有结果集被消费 while cursor.nextset(): pass cursor.close() if not auth_result: return {'msg': '未授权'}, 401 return f(*args, **kwargs) return wrapper class UserResource(Resource): @auth_decorator def get(self): db = get_db() cursor = db.cursor() cursor.callproc('get_user_info', (request.args.get('id'),)) result = cursor.fetchall() # 循环处理所有结果集,避免遗漏 while cursor.nextset(): pass cursor.close() return {'data': result}
2. 确保结果集被完全消费
你提到已经调用了cursor.nextset(),但有些存储过程可能返回多个结果集(比如内部包含多个SELECT语句),只调用一次可能不够。建议用循环直到nextset()返回None,彻底清理连接的状态。
3. 用连接池提升高并发性能(可选但推荐)
如果每个请求都新建/关闭连接,高并发下会有性能损耗。可以用DBUtils的连接池来管理连接,既保证每个请求拿到独立连接,又能复用连接资源:
from DBUtils.PooledDB import PooledDB import pymysql # 初始化连接池 pool = PooledDB( creator=pymysql, maxconnections=15, # 最大空闲连接数 mincached=2, # 初始空闲连接数 host='你的数据库地址', user='用户名', password='密码', db='数据库名' ) def get_db(): # 每个请求从池里获取独立连接 return pool.connection()
用完连接后调用close(),连接池会自动把连接放回池内复用,而非真的关闭。
4. 关于Waitress服务器的说明
Waitress是多线程模型,只要你的数据库操作是线程隔离的(每个线程用独立连接),服务器本身不会导致问题。之前的错误还是出在连接共享上,和服务器无关。
总结一下:问题的核心就是共享数据库连接导致的并发冲突,只要确保每个请求(包括装饰器里的操作)都使用独立的连接,并且彻底处理所有结果集,这个错误就能解决。
内容的提问来源于stack exchange,提问作者InfinityVive

