数据库连接池溢出问题解决方案

问题描述

在处理高并发转录任务时,系统出现了数据库连接池溢出的问题。分析代码后发现,每个任务在处理过程中都会尝试获取数据库连接,但由于任务处理时间较长,导致连接无法及时释放,从而超出了连接池的限制,引发连接超时错误。
QueuePool limit of size 20 overflow 10 reached, connection timed out, timeout 30.00

代码分析

数据库连接相关代码

async def create_task(request_data: dict = None) -> str:
    async with get_db() as db:
        task_id = str(uuid.uuid4())
        new_task = Task(
            id=task_id, 
            status='pending',
            request_data=request_data
        )
        db.add(new_task)
        await db.commit()
        return task_id

并发任务处理代码

async def process_transcribe_request(task_id: str, transcribe_data: TranscribeSchema, test_mode: bool):
    Logger.debug(f"开始处理转录请求,任务ID: {task_id}")
    try:
        if not transcribe_data.callback_url:
            transcribe_data.callback_url = "XXXXXXX"
        Logger.debug(f"使用默认回调地址: {transcribe_data.callback_url}")

        task = await get_task_status(task_id)
        if task and task["status"] == "pending":
            Logger.info(f"检测到待处理任务: {task_id}")
        
        await check_and_process_task(task_id, transcribe_data, transcribe_data.callback_url, test_mode)
            
    except Exception as e:
        Logger.error(f"处理转录请求时发生错误: {str(e)}")
        await update_task(task_id, "failed", str(e))

async def check_and_process_task(task_id: str, data: TranscribeSchema, callback_url: str, test_mode: bool):
    while True:
        task = await get_task_status(task_id)
        Logger.debug(f"检查任务 {task_id} 状态: {task['status']}")
        if task and task["status"] == "pending":
            processing_tasks = await get_processing_tasks_count()
            Logger.debug(f"当前正在处理的任务数量: {processing_tasks}")
            if processing_tasks < 3:
                if await start_processing_task(task_id):
                    Logger.debug(f"开始处理任务 {task_id}")
                    await _process_transcribe_task(task_id, data, callback_url, test_mode)
                    Logger.debug(f"任务 {task_id} 处理完成")
                    break
            else:
                Logger.debug(f"当前正在处理的任务数量达到上限,任务 {task_id} 处于待处理状态,等待30秒后再次检查")
                await asyncio.sleep(30)
        else:
            Logger.debug(f"任务 {task_id} 状态为 {task['status']},退出检查循环")
            break

问题分析

  1. SQLAlchemy 的连接池默认大小为 20,最大溢出为 10。
  2. 每个任务都会尝试获取数据库连接,处理时间较长导致连接无法及时释放。
  3. 并发请求超过连接池限制时,出现连接超时错误。

解决方案

1. 增加连接池配置

from sqlalchemy.pool import QueuePool

engine = create_engine(
    DATABASE_URL,
    poolclass=QueuePool,
    pool_size=30,  # 增加连接池大小
    max_overflow=20,  # 增加最大溢出连接数
    pool_timeout=60,  # 增加连接超时时间
    pool_pre_ping=True  # 启用连接健康检查
)

2. 优化数据库连接的使用

  • 确保在完成数据库操作后及时关闭连接。
  • 使用连接池复用而不是频繁创建新连接。
  • 考虑使用批量操作减少连接占用时间。

3. 实现连接重试机制

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=4, max=10)
)
async def get_db_connection():
    async with get_db() as db:
        return db

4. 考虑使用队列控制并发任务数量

这部分代码已经实现了任务队列,但可以考虑根据系统资源调整并发数量。

5. 监控连接池状态

def monitor_pool_status():
    """监控数据库连接池状态"""
    return {
        "pool_size": engine.pool.size(),
        "checkedin": engine.pool.checkedin(),
        "overflow": engine.pool.overflow(),
        "checkedout": engine.pool.checkedout(),
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aq_Seabiscuit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值