You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask(flask-restful)与PyMySQL并发异常问题求助

解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 20:59:11