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

使用SQLAlchemy to_sql上传数据致数据库锁定,求非分批解决方案

解决SQL Server批量上传时的数据库锁定问题

问题场景

以下是用于将DataFrame上传至SQL Server的函数:

def uploadtoDatabase(data,columns,tablename):

    connect_string = urllib.parse.quote_plus(f'DRIVER={{ODBC Driver 17 for SQL Server}};Server=ServerName;Database=DatabaseName;Encrypt=yes;Trusted_Connection=yes;TrustServerCertificate=yes')
    engine = sqlalchemy.create_engine(f'mssql+pyodbc:///?odbc_connect={connect_string}', fast_executemany=False)

    # define data to upload and chunksize (max 2100 parameters)
    requireddata = columns
    chunksize = 1000//len(requireddata)
    
    #convert boolean columns
    boolcolumns=data.select_dtypes(include=['bool']).columns
    data.loc[:,boolcolumns] = data[boolcolumns].astype(int)
    
    #convert objects to string
    objectcolumns=data.select_dtypes(include=['object']).columns
    data.loc[:,objectcolumns] = data[objectcolumns].astype(str)

    #load data
    with engine.connect() as connection:
      data[requireddata].to_sql(tablename, connection, index=False, if_exists='replace',schema = 'dbo')
      connection.close()

    engine.dispose()

执行该函数上传400万条记录时,数据库会被锁定,其他进程需等待上传完成。除了循环分批上传数据外,可通过以下方案确保上传期间其他数据库事务正常执行:

可行解决方案

1. 调整事务隔离级别

SQL Server默认的READ COMMITTED隔离级别会在批量写入期间持有锁至事务结束,可切换到快照类隔离级别,让其他事务读取数据快照而非等待锁释放:

  • 先在数据库端开启快照支持:
ALTER DATABASE YourDatabaseName SET ALLOW_SNAPSHOT_ISOLATION ON;
ALTER DATABASE YourDatabaseName SET READ_COMMITTED_SNAPSHOT ON;
  • 在代码中设置连接的隔离级别:
with engine.connect() as connection:
    connection.exec_driver_sql("SET TRANSACTION ISOLATION LEVEL READ COMMITTED SNAPSHOT")
    data[requireddata].to_sql(tablename, connection, index=False, if_exists='replace',schema = 'dbo')

2. 临时表+原子替换原表

当前使用if_exists='replace'会先删除原表再重建,全程持有表级锁。改用临时表上传后再原子替换,可大幅缩短锁持有时间:

with engine.connect() as connection:
    # 先上传数据到临时表
    temp_table = f'#{tablename}_temp'
    data[requireddata].to_sql(temp_table, connection, index=False, if_exists='replace', schema='dbo')
    # 原子操作替换原表
    connection.exec_driver_sql(f"""
        BEGIN TRANSACTION
        DROP TABLE IF EXISTS dbo.{tablename};
        EXEC sp_rename '{temp_table}', '{tablename}';
        COMMIT TRANSACTION
    """)

3. 开启fast_executemany提升写入速度

关闭fast_executemany会导致逐条插入数据,拉长事务时间和锁持有周期。开启该参数可大幅提升批量写入效率,缩短锁的影响时间:

engine = sqlalchemy.create_engine(f'mssql+pyodbc:///?odbc_connect={connect_string}', fast_executemany=True)

4. 设置数据库锁超时(缓解方案)

在数据库端配置锁超时,避免其他事务无限等待:

SET LOCK_TIMEOUT 5000; -- 设置超时时间为5秒,单位毫秒

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 14:10:27