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

Jupyter Notebook中Python操作SQL Server的执行顺序控制及锁问题排查

Jupyter Notebook中Python操作SQL Server的执行顺序控制及锁问题排查

嘿,我来帮你理清这个问题!先直接给你核心结论,再一步步说怎么排查和解决:

一、Python执行SQL操作的默认行为:同步阻塞

正常情况下,用pyodbc、pymssql这类常规库操作SQL Server时,Python是完全同步的——也就是说,只要你在单元格里调用了SQL语句(存储过程、增删改、DDL),Python会一直等着SQL Server把这个操作彻底执行完(包括事务提交/回滚),才会继续执行单元格里的后续代码,或者切换到下一个单元格。

举个例子:如果你的单元格里写了:

import pyodbc
conn = pyodbc.connect(your_conn_string)
cursor = conn.cursor()

print("开始跑存储过程...")
cursor.execute("EXEC dbo.MyLongRunningProc")
conn.commit()
print("存储过程跑完啦!")

你会看到先输出「开始跑存储过程...」,然后等存储过程在SQL Server那边彻底执行完,才会输出「存储过程跑完啦!」,这时候这个单元格才算执行完,你才能手动或者自动运行下一个单元格。

除非你特意用了异步库(比如asyncio-pyodbc)、多线程/多进程,才会出现“不等当前SQL执行完就跑下一段代码”的情况——但这种写法在普通数据分析的Jupyter Notebook里很少见,大概率你的代码还是同步的。

二、为什么会出现SQL Server锁死的情况?

结合你说的“继承的笔记本有大量依赖的DDL命令”,锁死的原因大概率是这几个:

  1. 事务未正确提交/回滚:如果某个单元格执行了修改类操作(比如CREATE TABLE、INSERT),但没调用conn.commit(),事务会一直处于打开状态,SQL Server会持有相关锁不释放,后面的DDL/操作就会被堵住,看起来像是笔记本“锁死”了。
  2. 长耗时操作持有锁:某个存储过程或DDL本身在SQL Server那边运行时间很长,期间持有了排他锁,导致后续依赖的操作无法获取锁,一直等待,笔记本这边也跟着卡住。
  3. 代码逻辑问题:比如不小心在循环里多次打开连接却没关闭,或者多个单元格共用一个未提交的连接,导致锁堆积。

三、怎么检查和控制执行顺序、解决锁问题?

1. 先确认SQL执行的同步状态

在每个关键操作前后加打印日志,直观看到执行进度:

print("准备执行CREATE TABLE...")
cursor.execute("CREATE TABLE dbo.MyNewTable (ID INT)")
conn.commit()
print("CREATE TABLE执行完成!")

如果打印顺序是先出前一句,过很久才出后一句,说明确实是在等SQL执行;如果瞬间就出两句,那可能是SQL执行有问题(比如语法错,但没抛出异常?这时候要加异常捕获)。

2. 排查SQL Server的锁情况

打开SSMS(SQL Server Management Studio),运行以下查询,看看哪些会话持有锁,以及对应的锁类型:

-- 查看当前锁情况
SELECT 
    request_session_id AS SessionID,
    resource_type AS LockType,
    resource_description AS LockResource,
    request_mode AS LockMode,
    request_status AS LockStatus
FROM sys.dm_tran_locks
WHERE resource_database_id = DB_ID('YourDatabaseName');

再结合sp_who2查询会话的程序名(一般是Python或者pyodbc),找到你的笔记本对应的会话,看看它持有哪些锁,是不是长时间没释放。

3. 控制执行顺序和避免锁的实用技巧

  • 强制事务提交:所有涉及修改的操作(DDL、INSERT/UPDATE/DELETE、存储过程)之后,必须显式调用conn.commit();如果有异常,一定要加try-except块执行conn.rollback(),比如:
    try:
        cursor.execute("EXEC dbo.MyProc")
        conn.commit()
        print("执行成功,已提交事务")
    except Exception as e:
        conn.rollback()
        print(f"执行失败,已回滚:{str(e)}")
    
  • 拆分大操作:把依赖的DDL/操作拆成更小的单元格,每个单元格只做一步,执行完一个再手动运行下一个——这样可以随时检查哪一步出了问题,也避免单个长事务持有锁太久。
  • 缩短锁持有时间:如果是查询类操作(不是DDL/修改),可以考虑用WITH (NOLOCK)提示(仅限允许脏读的场景),或者调整连接的隔离级别为READ UNCOMMITTED:
    # 设置隔离级别为READ UNCOMMITTED
    cursor.execute("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED")
    
  • 检查连接复用问题:尽量每个单元格用完连接就关闭,或者确保整个笔记本共用的连接是正确提交/回滚的,不要让多个单元格的操作共用一个未提交的事务。

备注:内容来源于stack exchange,提问作者Computermike

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.23 09:20:27