
cx_Oracle 的 executemany() 卡住不动,而 SQL Developer 中相同操作正常,大概率是数据库行级锁未释放所致——其他会话(如未提交的 SQL Developer 事务)持有了目标表的锁,导致 Python 进程无限等待。
cx_oracle 的 `executemany()` 卡住不动,而 sql developer 中相同操作正常,大概率是数据库行级锁未释放所致——其他会话(如未提交的 sql developer 事务)持有了目标表的锁,导致 python 进程无限等待。
在使用 cx_Oracle 进行批量数据插入(如 cursor.executemany(query, data))时,若操作突然变得极慢甚至“假死”(长时间无输出、无异常、进程持续占用 CPU 或挂起),但同一 SQL 在 SQL Developer 中执行迅速,这通常并非网络、驱动或代码逻辑问题,而是典型的数据库锁等待现象。
? 根本原因:未提交的事务造成行/表锁阻塞
Oracle 是强一致性数据库,当一个会话对某行执行了 INSERT/UPDATE/DELETE 后,只要未执行 COMMIT 或 ROLLBACK,该会话就会持续持有对应数据行(甚至整个表)的排他锁(X lock)。此时,其他会话尝试修改同一行(或满足某些条件的索引键范围)时,将被强制挂起,进入 enq: TX - row lock contention 等待事件,表现为 Python 脚本“卡住”,executemany() 阻塞,且不会抛出异常(因这不是错误,而是合法的锁等待)。
⚠️ 注意:你的 except cx_Oracle.DatabaseError 块无法捕获此类锁等待——它属于数据库内部调度行为,不是连接异常或语法错误。
✅ 快速诊断与解决步骤
-
立即检查活跃会话(推荐优先尝试)
在 SQL Developer 或任意有 DBA 权限的客户端中运行以下语句,查找可能阻塞你的会话:SELECT s.sid, s.serial#, s.username, s.osuser, s.machine, s.program, s.status, s.sql_id, s.event, s.seconds_in_wait, o.object_name, o.object_type FROM v$session s LEFT JOIN v$locked_object l ON s.sid = l.session_id LEFT JOIN dba_objects o ON l.object_id = o.object_id WHERE s.status = 'ACTIVE' AND s.event LIKE 'enq: TX%';
若发现存在未提交事务(status = 'ACTIVE' 且 event 为 enq: TX - row lock contention),记下其 sid 和 serial#。
-
手动提交或回滚源头会话
- 如果你能定位到是哪个 SQL Developer 窗口(或其他 Python 脚本)触发了未提交事务,请直接在该会话中执行:
COMMIT; -- 或 ROLLBACK;(若确认无需保存更改)
- 执行后,原 Python 脚本通常会在数秒内自动恢复并完成插入。
- 如果你能定位到是哪个 SQL Developer 窗口(或其他 Python 脚本)触发了未提交事务,请直接在该会话中执行:
若无法定位源头:安全清理法
关闭所有非必要数据库连接工具(SQL Developer、DBeaver、其他 Python 进程等)。Oracle 会在客户端断开连接时自动回滚未提交事务,释放锁。等待 10–30 秒后重试 Python 脚本。-
终极手段(需 DBA 权限,谨慎操作)
若上述无效且确认无关键业务正在运行,可终止可疑会话(务必先与团队确认!):ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;
?️ 预防建议:编写健壮的 cx_Oracle 插入代码
避免未来再遇同类问题,推荐在代码中加入超时控制和显式事务管理:
import cx_Oracle
import time
# 设置连接超时与执行超时(单位:秒)
dsn = cx_Oracle.makedsn("host", 1521, service_name="orcl")
connection = cx_Oracle.connect("user", "pwd", dsn,
call_timeout=30000) # 30s 网络/执行超时
try:
cursor = connection.cursor()
# 可选:设置语句级超时(Oracle 12c+)
cursor.arraysize = 1000
cursor.execute("ALTER SESSION SET STATEMENT_TIMEOUT=60") # 60秒语句超时
cursor.executemany(query, data)
connection.commit()
print("Data inserted successfully.")
except cx_Oracle.DatabaseError as exc:
error, = exc.args
print(f"Oracle Error {error.code}: {error.message}")
connection.rollback() # 确保失败时回滚
except cx_Oracle.OperationalError as exc:
print("Connection timeout or network issue:", str(exc))
connection.rollback()
finally:
if cursor: cursor.close()
if connection: connection.close()✅ 总结
cx_Oracle.executemany() 看似“卡死”,90% 的情况是遭遇隐性锁等待,而非性能退化或 Bug。优先排查并清理未提交事务,比优化代码或升级驱动更有效。 养成在开发环境中及时 COMMIT/ROLLBACK 的习惯,并在生产脚本中加入超时与异常回滚机制,可大幅降低此类问题发生概率。


















