使用MS SQL时Django事务失效问题求助
我之前在维护旧版Django项目时,也碰到过和你一模一样的问题——SQLite下事务跑的好好的,切到MS SQL就没了事务日志,导入速度暴跌还没法回滚。结合你的场景,我来拆解下问题原因和解决办法:
为什么SQLite正常,MS SQL掉链子?
SQLite的Django驱动在transaction.atomic()块里会自动关掉自动提交,老老实实走显式事务;但Django 1.11.x搭配sql_server.pyodbc驱动时,默认的数据库配置是开启AUTOCOMMIT的,而且transaction.atomic(using='mssql')在旧版里偶尔会出现连接绑定失效的情况,导致每一次save()都自动提交成独立事务——这就是你看不到BEGIN日志、速度慢5倍、异常没法回滚的根本原因。
一步步解决问题
1. 先把数据库配置的自动提交关了
修改你的MS SQL数据库配置,加上AUTOCOMMIT: False,确保事务上下文能正确接管提交逻辑:
'mssql': { 'ENGINE': 'sql_server.pyodbc', 'NAME': '----', 'HOST': '----', 'USER': '----', 'PASSWORD': '----', 'AUTOCOMMIT': False, # 新增这行,禁用默认自动提交 }
2. 确保事务和MS SQL连接绑定到位
有时候transaction.atomic(using='mssql')在旧版Django里会“飘”,可以试试显式拿到MS SQL连接再手动控制事务,更稳妥:
from django.db import connections try: # 显式获取MS SQL连接 mssql_conn = connections['mssql'] mssql_conn.set_autocommit(False) with open(filepath, 'r', newline='\n') as clean_file: for row in clean_file: measurement = json.loads(row) current_measurement = Measurementdata( mea_datetime=measurement['imp_datetime'], dun=measurement['dun_id'], exp=measurement['exp_id'], ptr=measurement['ptr_id'], mea_value=measurement['imp_value'] ) # 显式指定用MS SQL连接保存 current_measurement.save(using='mssql') # 手动提交事务 mssql_conn.commit() except Exception as e: # 异常时手动回滚 mssql_conn.rollback() raise e finally: # 恢复自动提交,不影响其他操作 mssql_conn.set_autocommit(True)
3. 导入速度慢?用批量插入直接起飞
哪怕事务正常了,逐行save()的效率也低得离谱——这也是你觉得慢5倍的关键。换成bulk_create()批量插入,速度能提升一个数量级:
try: with transaction.atomic(using='mssql'): batch_size = 1000 # 可以根据服务器性能调整,比如500或2000 batch = [] with open(filepath, 'r', newline='\n') as clean_file: for row in clean_file: measurement = json.loads(row) obj = Measurementdata( mea_datetime=measurement['imp_datetime'], dun=measurement['dun_id'], exp=measurement['exp_id'], ptr=measurement['ptr_id'], mea_value=measurement['imp_value'] ) batch.append(obj) # 攒够一批就插入 if len(batch) >= batch_size: Measurementdata.objects.using('mssql').bulk_create(batch) batch = [] # 处理最后一批不足batch_size的记录 if batch: Measurementdata.objects.using('mssql').bulk_create(batch) except Exception as e: # 事务块里异常会自动回滚,不用手动操作 print(f"导入失败,已回滚所有操作: {str(e)}")
注意:Django 1.11.x的bulk_create()对MS SQL是支持的,但如果你的模型有自定义主键生成逻辑,可能需要额外调整,不过看你的模型字段应该没问题。
验证效果
改完之后再看日志,应该能看到BEGIN TRANSACTION的记录,导入速度会明显提升,而且异常时所有已插入的数据都会被回滚。
内容的提问来源于stack exchange,提问作者Endauriel

