Sql-Server

通過 C# EF Core 或 SQL 更新

  • January 4, 2021

我們的工作涉及更新產品,我們有一個很大的產品表,價格和其他相關資訊每小時更新一次。假設它是一家亞馬遜商店,我們正在談論亞馬遜產品,我們必須更新銷售價格,buy box 價格等。我們每小時從亞馬遜提取資訊到我們的程序中並更新數據庫中的數據。

我的工作流程是,將數據庫中的所有產品拉入程序中(我們使用 C# 和 EF Core),更新相關產品,並將更新發送回數據庫。

這種方式的代價是從數據庫中讀取很多資訊到程序中,但是我覺得這樣很高效,因為 EF Core 有變化檢測,所以即使我為所有產品分配了進貨產品的價格,如果有is no change EF核心不會改變任何東西,它只會為那些資訊發生變化的產品生成更新語句。

此外,它不會生成大的更新語句,它會生成小的、有針對性的更新語句,例如 update products, set BuyBoxPrice = 12.23 where productid = 23345.

我正在和一個非常有天賦的開發人員一起工作,他對 SQL 非常自然,他認為這種方式是錯誤的,我寧願將所有傳入的資訊放在一個名為 #products 的臨時表中,並將其發送到數據庫中,然後執行一個應該這樣做的儲存過程,

update products, set BuyBoxPrice = #products.buyboxprice from products inner join #products on products.produtid = #products.productid.

因此,這種方法避免了從數據庫中進行大量讀取。

我不是那麼有經驗,我的問題是,讀取會創建鎖或降低數據庫性能,可能是嗎?

下面是我對他的方法不滿意的原因。

它創建了很多不必要的更新,這在我看來是非常浪費的,因為只有 25% 的資訊發生了變化,所以為什麼要更新所有列。

我的同事反駁說,我可以通過添加 where 語句來解決這個問題,比如

update products where products.buyboxprice <> #products.buyboxprice

我不認為這會減少你支付的罰款,我認為它仍然是相同的效果。

另一個主要擔心是大型更新會創建鎖,僅此一項就應該避免。現在我當然可以將更新分解成小於 3000 的塊等。

第三點,當SQL肚子疼的時候,它會全身而退,然後開始發生奇怪的事情,客戶大喊大叫老闆生氣,我對發生的事情幾乎沒有了解,但是在C#中,只要有什麼崩潰就對了對我來說很清楚。

所以我的問題是,誰是對的,是通過讀取和 EF 核心還是通過 SQL 進行更新的性能更高

在數據庫的上下文中,做關係的事情將是最快的。即使 Entity Framework 高效地做事,在蘋果對蘋果的比較中,它總是會有更多的成本。

話雖如此,最大的性能差異在於您如何實現以數據庫為中心的解決方案或以 C# 為中心的解決方案。我不會評論你同事推薦的替代方案,因為這似乎有點超出我的建議。我認為可能有更好的解決方案,具體取決於情況,例如使用 EF Core 的DBContext.Find()方法WHERE在主鍵值上應用謂詞,但是您的文章提出了很多需要更多思考才能回答的重要問題,所以我會更新我的等我有時間再回答。

一個快速的方法是,是的,讀取確實會導致鎖定表中的寫入,並且將整個表拉入像 C# 應用程序這樣的消費者中以僅更新幾條記錄通常是不高效的。相反,您應該WHERE在數據拉取上使用謂詞來僅訪問應用程序目前上下文的生命週期所需的行。(假設您的表已正確編入索引。)


為了解決您提出的觀點/您提出的其他問題:

  1. **“而不是將所有傳入的資訊放在一個名為 #products 的臨時表中”:**雖然使用臨時表來臨時保存要更新的記錄的主鍵是一種以潛在有效的方式做事的方式,但聽起來有點迂迴。如果您已經擁有要更新的記錄的主鍵 (ID),那麼您可以將這些 ID 傳遞給儲存過程(通過參數數據類型的幾個不同選項),或者更好的是只編寫更新使用這些 ID 聲明本身並執行它。(在許多其他解決方案中只是幾個想法)。同樣,如果您想要一個純 C# 解決方案,您應該能夠使用 EF Core 的DBContext.Find()方法僅拉入需要更改的行,然後更新它們。(可能是最好的中間解決方案。
  2. **“讀取會創建鎖還是會降低數據庫性能? ”:**是的,如果您經常讀取大量數據,它們會導致資源爭用,從而影響伺服器的性能。從表中讀取也會獲取該表上的鎖,這可能會阻止寫查詢並導致爭用。有關鎖定的更多詳細資訊,請參閱前面連結的 DBA.StackExchange 答案。
  3. **"造成了很多不必要的更新,在我看來是很浪費的,因為只有 25% 的資訊發生了變化,所以為什麼要更新所有的列。 “:**我不完全理解你這裡的說法,並不是所有的列都在更新基於您的同事建議的 SQL 解決方案。假設他的意思是只發送臨時表中需要更新的記錄的 ID,那麼只有需要更新的確切行數被更新。
  4. **“我的同事反駁說,我可以通過添加 where 語句來解決這個問題…… ”:**這向我表明,您之前的語句是假設所有行都應該始終傳遞到臨時表中。根據我之前的回复,您應該只傳入需要更新的行。
  5. 我認為這不會減少您支付的罰款,我認為它仍然具有相同的效果。 ”:WHERE如果您的臨時表僅包含需要更新的行的 ID,則無需添加您同事建議的語句. (如果您的臨時表確實包含表中的每一行,那麼如果不查看查詢的執行計劃,很難說它是否會更有效。可能它仍然比 EF Core 中拉入所有的過程更有效記錄到應用程序中,然後更新它們。)另外,如果您採用不使用臨時表的不同路線,並且涉及在邏輯上等同於您想要完成的 SQL 過濾(例如update products set buyboxprice = 'someValue' where someOtherField = 'someOtherValue') 那麼過濾將比 EF Core 中的目前程序更有效(假設您有正確的索引)。
  6. **“另一個主要擔心是大更新創建鎖”:**蘋果對蘋果,如果您在兩種情況下都有相同的查詢計劃,那麼由於您的更新將發生相同數量的鎖定。(儘管一次需要更新的行數會影響查詢計劃。)您可以使用分塊(批處理),但通常在您一次更新大約 100,000 條記錄之前實際上並不需要. 但是批處理是一種設計選擇,與 C# 和 SQL 無關,您可以在任何地方完成此操作,因此這是一個有爭議的問題。
  7. 第三點,當 SQL 出現肚子問題時,它會引發全身症狀……我對正在發生的事情知之甚少,但在 C# 中,每當發生崩潰時我都清楚。 ”:這不是 SQL 的問題對可用的框架和工具缺乏經驗(我並不是說消極的,只是事實上,作為幾年前處於同一狀態的人)。可能有一段時間您不知道如何在 Visual Studio中使用調試器,對嗎?…和****斷點…同樣使用try/catch? 在您精通 C# 工具之前,您可能很難調試崩潰和錯誤。SQL 也是如此,此外還有性能問題!= 崩潰和錯誤。他們需要一種不同的思維方式和過程來進行調試。如果您的 C# 應用程序中存在與數據庫無關的性能問題(例如,如果您的應用程序在將文件保存到磁碟時速度很慢),那麼使用您正在使用的工具調試問題就不會像崩潰那樣清晰熟悉調試崩潰。有很多很好的工具和腳本可用於調試 SQL Server 中的性能問題,請參閱下面的列表:

總而言之,作為一名 8 年的 C# 開發人員和大約 3 年的 DBA,我的經驗是,管理和操作數據在結構良好且具有最大化數據庫關係能力的良好流程的情況下是最有效的(而不是試圖用函式式語言完成相同的任務)。

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