【发布时间】:2021-02-13 00:39:40
【问题描述】:
我有一个 Python 进程,它使用 SQLAlchemy 将一些数据插入 MS SQL Server DB。当 Python 进程运行时,它会在插入期间挂起。我打开了 SQLAlchemy 日志以获取更多信息。我发现它在 SQLAlchemy 似乎正在请求有关整个 DB 的表模式信息时挂起:
2020-10-30 08:12:07 [11444:6368] sqlalchemy.engine.base.Engine._execute_context(base.py:1235) INFO: SELECT [INFORMATION_SCHEMA].[TABLES].[TABLE_NAME]
FROM [INFORMATION_SCHEMA].[TABLES]
WHERE [INFORMATION_SCHEMA].[TABLES].[TABLE_SCHEMA] = CAST(? AS NVARCHAR(max)) AND [INFORMATION_SCHEMA].[TABLES].[TABLE_TYPE] = CAST(? AS NVARCHAR(max)) ORDER BY [INFORMATION_SCHEMA].[TABLES].[TABLE_NAME]
2020-10-30 08:12:07 [11444:6368] sqlalchemy.engine.base.Engine._execute_context(base.py:1240) INFO: ('dbo', 'BASE TABLE')
此时我在数据库中还有其他“东西”在进行,包括一些打开的事务,我的猜测是无论出于何种原因查询[INFORMATION_SCHEMA].[TABLES] 都会以某种方式造成一些死锁或阻塞。
我还读到 (here),[INFORMATION_SCHEMA].[TABLES] 是一个不会导致死锁的视图,这与我对导致此问题的猜测相矛盾。
我的问题是:我可以更改 SQLAlchemy 的配置/设置,使其一开始就不会进行此查询吗?
更新 1: 插入的 Python 代码是这样的:
with sqlalchemy.create_engine("mssql+pyodbc:///?odbc_connect=%s" % params).connect() as connection:
# df is a Pandas DataFrame
df.to_sql(name=my_table, con=connection, if_exists='append', index=False)
请注意,当我在一天中的其他时间没有进行其他数据库事务时运行 Python 脚本时,代码可以正常工作。在这些情况下,日志会像这样立即继续,列出数据库中的所有表:
2020-10-30 08:13:03 [11444:6368] sqlalchemy.engine.base.Engine._execute_context(base.py:1235) INFO: SELECT [INFORMATION_SCHEMA].[TABLES].[TABLE_NAME]
FROM [INFORMATION_SCHEMA].[TABLES]
WHERE [INFORMATION_SCHEMA].[TABLES].[TABLE_SCHEMA] = CAST(? AS NVARCHAR(max)) AND [INFORMATION_SCHEMA].[TABLES].[TABLE_TYPE] = CAST(? AS NVARCHAR(max)) ORDER BY [INFORMATION_SCHEMA].[TABLES].[TABLE_NAME]
2020-10-30 08:13:03 [11444:6368] sqlalchemy.engine.base.Engine._execute_context(base.py:1240) INFO: ('dbo', 'BASE TABLE')
2020-10-30 08:13:03 [11444:6368] sqlalchemy.engine.base.Engine._init_metadata(result.py:810) DEBUG: Col ('TABLE_NAME',)
2020-10-30 08:13:03 [11444:6368] sqlalchemy.engine.base.Engine.process_rows(result.py:1260) DEBUG: Row ('t_table1',)
2020-10-30 08:13:03 [11444:6368] sqlalchemy.engine.base.Engine.process_rows(result.py:1260) DEBUG: Row ('t_table2',)
...
更新 2:
显然,当在打开的事务中创建表或其他对象但尚未提交时,查询[INFORMATION_SCHEMA].[TABLES] 将被阻止(source)。是否有人熟悉 SQLAlchemy 的内部结构来建议如何防止它首先进行此查询?
更新 3:在 SQLAlchemy github (issue link) SQLAlchemy 开发人员发布此问题后,确认 [INFORMATION_SCHEMA].[TABLES] 的查询实际上是由 Pandas @987654324 引起的@to_sql().
所以,我的新问题是,有人知道如何在 Pandas to_sql() 函数中禁用此行为吗?我查看了文档,找不到任何似乎有帮助的内容。
【问题讨论】:
-
在 GitHub 上讨论过 here.
-
@GordThompson SQLAlchemy 开发人员提到使用
ALTER DATABASE MyDatabase SET ALLOW_SNAPSHOT_ISOLATION ON或ALTER DATABASE MyDatabase SET READ_COMMITTED_SNAPSHOT ON不会通过打开快照隔离来积极锁定。我担心在数据库级别进行这些更改。您是否知道 SQL Server 中有一个设置,当存在已在该数据库中创建新表的打开事务时,对[INFORMATION_SCHEMA].[TABLES]进行查询不会阻塞? -
你可以试试
create_engine(connection_uri, isolation_level="READ_UNCOMMITTED")看看是否能防止挂起。
标签: python sql-server pandas sqlalchemy pyodbc