跳過到頁腳內容
Iron Academy Logo
C#常見問題

nvarchar(max)在SQL中對Entity Framework開發者的危險

Tim Corey
10m 27s

在 SQL 中處理 nvarchar 時,開發人員通常忽略了這種資料型別對效能的影響——尤其是在使用 Entity Framework 的 C# 開發中。 在一段名為"The Dangers of nvarchar(max) in SQL for Entity Framework Developers"的10分鐘專注視頻中,Tim Corey 探討了在 SQL Server 資料庫中將 nvarchar(max) 作為字串欄位預設值的影響。

本文是一篇詳細解釋 Tim 的影片,只使用他的演示和推理,並包含範例和效能比較。 如果您依賴 nvarchar(max) 而不理解它在底層的運作,這將讓您大開眼界。

理解問題:Entity Framework 中的預設行為

Tim 首先描述了一個常見的 Entity Framework 情景,其中 C# 開發人員定義了一個模型,模型中有如 FirstName 和 LastName 的欄位。 當透過遷移在 SQL Server 中自動建立表時,生成的架構預設會將這些字串欄位設置為 nvarchar(max)。

如 Tim 所解釋,這發生的原因是因為 Entity Framework 不知道分配適當的字串大小,因此選擇了安全的路徑——預設分配最大長度。 這意味著每個 nvarchar 欄允許多達 2^31–1 個字元,包含最大儲存空間以 GB 為單位。

這個決定看似方便,但它隱藏了危險的效能成本。

兩張表的範例設置:nvarchar(max) vs 固定長度

為了強調問題,Tim 創建了兩個相同的表:

  • Users:使用 nvarchar(50) 作為名字和姓氏。

  • UsersToTheMax:使用 nvarchar(max) 為相同欄位。

在 2:39,Tim 解釋了如何使用 Dapper 填充兩個表,讓兩者都有 1 百萬行相同的數據,確保只有 nvarchar 資料型態不同。

該設置使他能夠在固定長度的 Unicode 欄和變長 max 欄之間進行一致的比較。

比較查詢和執行計劃

Tim 在兩個表上使用以下 SQL 查詢:

SELECT * FROM dbo.Users ORDER BY LastName;
SELECT * FROM dbo.UsersToTheMax ORDER BY LastName;

在 3:34,他啟用了實際執行計劃,以分析 SQL Server 在執行這些查詢時內部的運行過程。

注意:此測試不是比較不同機器上的總執行時間——Tim 強調要在相同伺服器上比較查詢,相同數據,來隔離 nvarchar(max) 對效能的影響。

令人震驚的結果

執行計劃揭示了一個主要差異:

  • nvarchar(50) 查詢僅使用了批次成本的 2%。

  • nvarchar(max) 查詢則消耗了 98% 的成本。

如 Tim 所說,這意味著 max 查詢比 SQL Server 的處理方式貴 50 倍,儘管欄資料項目相同且相對較小。

在 CPU 時間方面:

  • 排序 nvarchar(50) 需要 107ms。

  • 排序 nvarchar(max) 需要 339ms。

但最大的差異在於一個特定的並行操作:

  • 固定長度:0.43秒

  • 最大長度:22.17秒

即使在相同的數據下,速度也慢了超過 50 倍。

記憶體消耗差異

Tim 深入研究記憶體授權——SQL Server 為每個查詢分配多少記憶體:

  • nvarchar(50) 查詢:340MB

  • nvarchar(max) 查詢:641MB

這本身就是一個紅旗,但在測試未緩存的欄時,影響更為顯著:

  • First Name 的固定長度:357MB

  • First Name 的最大長度:8.5GB

這種增加發生是因為 SQL Server 並不知道 nvarchar 值在定義為最大時可能會有多大,所以它預留了一個更大的記憶體塊以適應最大尺寸。

為何 nvarchar(max) 如此昂貴?

在 9:15,Tim 解釋了根本原因。 nvarchar(max) 資料型別:

  • 支援多達 2^31–1 個 Unicode 字元,最多消耗 2GB 的儲存空間。

  • 如果數值不合適,要求 SQL Server 在行外儲存數值,使用指標而非直接行內存儲。

  • 不能像固定長度列一樣索引。

因此:

  • 您不能索引 nvarchar(max) 欄位,這意味著 SQL Server 必須對整個數據集進行排序或過濾而不需優化。

  • 這影響了如 ORDER BY、WHERE 或 JOIN 等操作在 nvarchar(max) 欄位上的運行。

這種行為導致顯著記憶體使用、CPU 負荷和減速 —— 僅僅是因為選擇了錯誤的字元資料型別。

Tim 的最終建議

如 Tim 在結尾中所說:

"在您的 Entity Framework 查詢中,確保為所有字串指定大小。"

總是在字串屬性中定義最大字元數,例如 nvarchar(100) 或 nvarchar(255),根據預期資料進行定義。 這一微小的變化確保:

  • 優化的儲存空間

  • 支援索引

  • 降低查詢成本

  • 更佳的效能一致性

通過設置適當的長度,您可使資料庫架構更有效率,避免懶惰的預設設置帶來的陷阱。

結束語

Tim Corey 的影片傳遞了一個關鍵的教訓:將 nvarchar(max) 作為 SQL 中字串欄位的預設長度,會阻礙效能 —— 而您甚至可能未意識到這一點。 即使是如姓名或地址等常規Unicode文本項目,SQL Server也會分配過多的記憶體,跳過索引,並增加CPU成本。

結論是什麼? 理解 nvarchar 資料型別,除非您確實需要使用常常變長的資料來儲存大文件或可變長度內容,否則避免使用 max。

透過指定字串大小,您不僅能節省位元組和記憶體,還讓您的 Entity Framework 和 SQL 程式碼更有效、可擴展和強健。遵循 Tim 的指導,您可確保您的應用程式不是設計不良造成的緩慢。

對於任何在 .NET 中使用資料庫的人來說,這是一個最佳實踐,應成為標準工具組的一部分。 查看 Tim 的頻道以獲取更多 SQL 相關影片。

Hero Worlddot related to nvarchar(max)在SQL中對Entity Framework開發者的危險
Hero Affiliate related to nvarchar(max)在SQL中對Entity Framework開發者的危險

通過分享您所愛的東西賺得更多

您是否在為使用.NET、C#、Java、Python或Node.js的開發者創建內容?將您的專業知識轉化為額外收入!

鋼鐵支援團隊

我們每週 5 天,每天 24 小時在線上。
聊天
電子郵件
打電話給我