Sql-Server

將 nvarchar(max) 列更改為 nvarchar(50) 會鎖定表嗎?

  • March 8, 2018

我在 SQL Server (Azure) 中有一個包含 140 萬行的表。

我想索引其中一列;但是,它的數據類型是nvarchar(max)(預設使用我想像的 EF)。我正在使用實體框架,並且可以進行遷移,將此數據類型更改為nvarchar(50).

我擔心這需要多長時間,以及它是否會導致鎖定問題。我只擴展了列,所以我不確定會發生什麼。我確實知道此列中的數據很適合新的大小。

我打算創建一個數據庫的副本並嘗試一下。我也考慮過遷移到新列,但如果不是太痛苦,我更願意更新目前列。

我們無法完全回答您的問題。取決於太多的因素,包括表定義、表中的其他列、數據是否離頁、ORM 生成的 T-SQL 等等。文件非常好:

WITH (ONLINE = ON | OFF) 適用於:SQL Server 2016 到 SQL Server 2017 和 Azure SQL 數據庫。

允許在表保持可用時執行許多更改列操作。預設為關閉。可以對與數據類型、列長度或精度、可空性、稀疏性和排序規則相關的列更改線上執行更改列。

線上更改列允許使用者創建和自動統計在 ALTER COLUMN 操作期間引用更改的列。這允許查詢像往常一樣執行。在操作結束時,引用該列的自動統計資訊將被刪除,並且使用者創建的統計資訊無效。操作完成後,使用者必須手動更新使用者生成的統計資訊。如果該列是任何統計資訊或索引的過濾器表達式的一部分,則您不能執行更改列操作。

當線上更改列操作正在執行時,所有可能依賴於列的操作(索引、視圖等)都將阻塞或失敗並出現適當的錯誤。這保證了線上更改列不會因為操作執行時引入的依賴關係而失敗。

線上更改列與線上索引重建具有相似的要求、限制和功能。這包括:

當表包含舊 LOB 或文件流列或表具有列儲存索引時,不支持聯機索引重建。相同的限制適用於線上更改列。

正在更改的現有列需要兩倍的空間分配;對於原始列和新創建的隱藏列。

更改列線上操作期間的鎖定策略遵循用於線上索引建構的相同鎖定模式。

此外,它的測試也非常簡單。我正在使用預設讀取送出隔離級別進行 SQL Server 2017 測試。首先,我將創建表:

DROP TABLE IF EXISTS dbo.CONVERT_ME;

CREATE TABLE dbo.CONVERT_ME (
   ID BIGINT NOT NULL,
   OVERWEIGHT_COLUMN VARCHAR(MAX) NOT NULL,
   PRIMARY KEY (ID)
);

INSERT INTO dbo.CONVERT_ME WITH (TABLOCK)
SELECT TOP (1500000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), REPLICATE('Z', 50)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);

然後我將更改 column OFFLINE,這是預設值:

SET STATISTICS IO, TIME ON;

BEGIN TRANSACTION;

ALTER TABLE dbo.CONVERT_ME
ALTER COLUMN OVERWEIGHT_COLUMN VARCHAR(50) NOT NULL WITH (ONLINE = OFF);

我的機器這大約需要三秒鐘。該操作在整個事務期間採用模式修改鎖,以防止對其採用任何其他鎖,包括使用NOLOCK.

在此處輸入圖像描述

現在我將嘗試使用 WITH (ONLINE = ON)。該操作現在可以進行並行處理,因此它可以在您的伺服器上執行得更快。模式修改鎖仍然在對像上,但它發生在接近結束時而不是在整個事務中。這是鎖定表的範例快照,以顯示在大多數事務期間持有的模式修改鎖:

在此處輸入圖像描述

請注意,尚未獲取對象級模式修改鎖。以下是操作完成後事務送出之前的樣子:

在此處輸入圖像描述

對像上的模式修改鎖定以及其他一些鎖定在接近尾聲時被短暫使用。

太好了,用了應該沒問題WITH (ONLINE = ON)吧?正如我之前所說,手術所需的時間將取決於很多因素。考慮一個具有更多數據頁的不同表:

DROP TABLE IF EXISTS dbo.CONVERT_ME;

CREATE TABLE dbo.CONVERT_ME (
   ID BIGINT NOT NULL,
   OTHER_COLUMN VARCHAR(8000) NOT NULL,
   OVERWEIGHT_COLUMN VARCHAR(MAX) NOT NULL,
   PRIMARY KEY (ID)
);

INSERT INTO dbo.CONVERT_ME WITH (TABLOCK)
SELECT TOP (1500000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), REPLICATE('Z', 4200), REPLICATE('Z', 50)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);

在我的機器上,ALTER TABLE現在執行時需要 39635 毫秒,執行時需要 117877OFFLINE毫秒ONLINE

我對您的建議是閱讀文件,直到您確信自己了解鎖定注意事項,並在生產中進行切換之前使用填充了真實數據的真實表模式進行測試。

請注意:

不確定 Azure SQL 數據庫,但在正常 SQL Server 中,該ONLINE選項僅適用於企業版。嘗試在另一個版本中執行此操作將導致以下錯誤:

Msg 1712, Level 16, State 1, Line XXXXX

聯機索引操作只能在 SQL Server 企業版中執行。

引用自:https://dba.stackexchange.com/questions/199649