[英]SQLAlchemy hangs during insert while querying [INFORMATION_SCHEMA].[TABLES]
我有一個 Python 進程,它使用 SQLAlchemy 將一些數據插入 MS SQL 服務器數據庫。 當 Python 進程運行時,它在插入期間掛起。 我打開了 SQLAlchemy 日志記錄以獲取更多信息。 我發現它在 SQLAlchemy 似乎正在請求有關整個數據庫的表模式信息的地方掛起:
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]
都會以某種方式創建一些死鎖或阻塞。
我也讀過( 這里) [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:顯然,當在打開的事務中創建表或其他 object 且尚未提交時,查詢[INFORMATION_SCHEMA].[TABLES]
將被阻止( 來源)。 是否有人熟悉 SQLAlchemy 的內部結構來建議如何防止它首先進行此查詢?
UPDATE 3 : After posting this issue on the SQLAlchemy github ( issue link) the SQLAlchemy devs confirmed that the query of [INFORMATION_SCHEMA].[TABLES] is in fact being caused by the Pandas function to_sql()
.
所以,我的新問題是有人知道如何在 Pandas to_sql()
function 中禁用此行為嗎? 我查看了文檔,找不到任何似乎有幫助的東西。
我對 SQLAlchemy 不是很熟悉,但我可以告訴你這個問題的 Pandas 方面。
如果表不存在,Pandas 會自動創建一個新表。 它判斷表是否存在的方法是在 SQL Alchemy 中調用has_table()
。 has_table()
工作方式是查詢信息模式。 (至少,它在 MySQL 和 MSSQL 中是這樣工作的。)
這是我在 Pandas 和 SQLAlchemy 中找到的跟蹤邏輯的內容。 我們從 pandas/io/sql.py 開始,在to_sql()
。
table = SQLTable(
name,
self,
frame=frame,
index=index,
if_exists=if_exists,
index_label=index_label,
schema=schema,
dtype=dtype,
)
table.create()
SQLTable.create() 在這里定義:
class SQLTable(PandasObject):
[...]
def create(self):
if self.exists():
if self.if_exists == "fail":
raise ValueError(f"Table '{self.name}' already exists.")
elif self.if_exists == "replace":
self.pd_sql.drop_table(self.name, self.schema)
self._execute_create()
elif self.if_exists == "append":
pass
else:
raise ValueError(f"'{self.if_exists}' is not valid for if_exists")
else:
self._execute_create()
請注意,它無條件地調用exists()
。 在SQLTable.exists()
里面,你會發現:
def exists(self):
return self.pd_sql.has_table(self.name, self.schema)
這最終在 SQLAlchemy 中調用has_table()
: https : has_table()
對於 MSSQL,這是在 SQLAlchemy 中的 sqlalchemy/dialects/mssql/base.py 中實現的:
@_db_plus_owner
def has_table(self, connection, tablename, dbname, owner, schema):
if tablename.startswith("#"): # temporary table
[...]
else:
tables = ischema.tables
s = sql.select(tables.c.table_name).where(
sql.and_(
tables.c.table_type == "BASE TABLE",
tables.c.table_name == tablename,
)
)
if owner:
s = s.where(tables.c.table_schema == owner)
c = connection.execute(s)
return c.first() is not None
( ischema
是ischema
的縮寫,此代碼正在該表上運行選擇。)
我沒有看到一個好的,簡單的方法來解決這個問題。 Pandas 假設has_table()
是一個廉價的操作。 MSSQL 不遵循該假設。 沒有什么事if_exists
設置為,大熊貓將調用has_table()
期間to_sql()
不過,我可以想到一種很酷的方法來做到這一點。 如果您要對pandas.io.sql.SQLTable.create()
進行猴子補丁,使其成為空操作,那么您可以誘使 Pandas 認為該表已經存在。 這樣做的缺點是 Pandas 不會自動創建表。
在調用 to_sql/ 之前執行設置事務隔離級別讀取未提交
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.