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

Django项目中同步插入MySQL比异步更快的原因及优化方案咨询

异步MySQL插入比同步慢的原因及优化方案

问题背景

将项目重构为异步实现(基于aiomysql+asyncio),测试后发现同步代码(pymysql)的MySQL插入速度反而更快。两者核心差异如下:

  • 数据库驱动:同步使用pymysql 1.1.0,异步使用aiomysql 0.2.0+asyncio
  • 均采用pandas完成数据计算,业务逻辑完全一致
  • 测试环境为同一电脑、同一网络
  • 版本差异:同步代码用Python 3.6+Django 3.2.25,异步代码用Python 3.8+Django 4.2.11
  • 异步采用Django异步视图,插入操作通过自定义连接池调用executemany,单次插入894条数据

慢的原因分析

  1. 全局锁导致串行化执行
    自定义DatabasePool为每个数据库添加了全局asyncio.Lock,所有数据库操作都必须先获取锁才能执行。这直接让异步操作退化为串行执行,不仅失去了异步并发的优势,还额外增加了协程调度的开销,导致比同步代码更慢。

  2. aiomysql版本过于陈旧
    使用的aiomysql 0.2.0是非常早期的版本,旧版本在executemany的实现上可能存在性能缺陷,比如未对批量插入做针对性优化,或内部异步调度逻辑存在冗余开销。

  3. 异步框架的固有开销
    异步代码本身存在事件循环调度、协程切换的额外消耗。当仅执行单个批量插入任务时,这些开销会直接体现在总耗时中,而同步代码无此负担。

  4. 版本差异的隐性影响
    Python 3.8与3.6、Django 4.2与3.2之间的底层实现差异,可能引入了额外的处理逻辑,间接拖慢了插入速度。

优化方案

  1. 移除全局数据库锁
    aiomysql连接池本身已实现协程安全,pool.acquire()方法可被多个协程安全调用,无需额外加锁。删除_locks相关代码,恢复异步并发能力:

    async def execute_sql(self, db_name, sql, args=None, executemany=False):
        pool = self._pools.get(db_name)
        if not pool:
            raise ValueError(f"No pool found for database: {db_name}")
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                try:
                    if executemany:
                        await cur.executemany(sql, args or ())
                    else:
                        await cur.execute(sql, args or ())
    
                    if sql.strip().upper().startswith("SELECT"):
                        result = await cur.fetchall()
                    else:
                        result = cur.rowcount
                except Exception as e:
                    raise
                return result
    
  2. 升级aiomysql版本
    升级到最新稳定版(如0.2.7),新版本修复了大量性能问题,尤其是针对批量操作的效率优化。执行升级命令:

    pip install --upgrade aiomysql
    
  3. 优化批量插入SQL写法
    MySQL支持单条INSERT语句包含多个VALUES组,这种方式比executemany效率更高。可以手动拼接SQL:

    def generate_batch_insert_sql(table, columns, data):
        # 生成多个VALUES占位符组
        placeholders = ", ".join(["(" + ", ".join(["%s"]*len(columns)) + ")" for _ in data])
        sql = f"INSERT INTO {table} ({', '.join(columns)}) VALUES {placeholders}"
        # 扁平化数据列表适配单条SQL参数
        flat_data = [item for sublist in data for item in sublist]
        return sql, flat_data
    
    # 使用示例
    columns = ["id", "name", "age", ...]
    sql, flat_data = generate_batch_insert_sql("example", columns, list_fen_final)
    await exec_sql_async("example_database", sql, flat_data)
    

    注意:需确保MySQL的max_allowed_packet参数足够大,避免因SQL过长报错。

  4. 调整连接池参数
    根据实际并发量优化连接池配置:

    • 适当提高maxsize(如设为50),但不超过MySQL配置的max_connections值
    • 将minsize设置为日常并发所需的最小连接数,减少连接频繁创建销毁的开销
  5. 针对性降低异步开销

    • 若存在多个数据库操作任务,采用异步并行执行(如同时插入多表、分批次并行插入),充分发挥异步优势
    • 对于单一大批量插入场景,可在异步视图中通过run_in_executor调用同步pymysql执行,规避异步调度开销

内容的提问来源于stack exchange,提问作者Rain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 08:15:12