簡體   English   中英

SQLAlchemy 在插入期間掛起,同時查詢 [INFORMATION_SCHEMA]。[TABLES]

[英]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

ischemaischema的縮寫,此代碼正在該表上運行選擇。)

如何解決這個問題

我沒有看到一個好的,簡單的方法來解決這個問題。 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM