簡體   English   中英

SQL性能,.Net優化與最佳實踐

[英]SQL Performance, .Net Optimizations vs Best Practices

我需要你的專業人士/大師的確認/解釋,因為我的團隊告訴我“這沒關系”,這讓我感到很沮喪:)

背景:我們的主要MVC3 / .Net4 Web應用程序正在使用SQL Server 2008。 我們在任何給定點上都有大約200多個並發用戶。 服務器受到極大的打擊(鎖定,超時,整體緩慢),我正在嘗試應用我在整個職業生涯和最后一次MS認證課程中學到的東西。 他們是我們所有人都已經完成的事情(“關閉SQL連接STAT”),我試圖向我的團隊解釋這些“小事情”,盡管不是唯一一個有所作為,最后加起來。

我需要知道以下是否會對性能產生影響,或者它是否只是“最佳實踐”

1.使用“USING”關鍵字。 他們的大部分代碼都是這樣的:

public string SomeMethod(string x, string y) {
    SomethingDataContext dc = new SomethingDataContext();
    var x = dc.StoredProcedure(x, y);
}

雖然我試圖告訴他們USING會更快地關閉/釋放資源:

using (SomethingDataContext dc = new SomethingDataContext()) {
    var x = dc.StoredProcedure(x, y);
}

他們的論點是GC在代碼執行完畢后做了很好的清理工作,因此USING沒有產生巨大的影響。 是真還是假?為什么?

2.連接池

我一直聽說設置連接池可以顯着加快任何網站(至少.Net w / MSSQL)。 我建議我們在web.config中的connectiontring中添加以下內容:

...“Pooling = True; Min Pool Size = 3; Max Pool Size = 100; Connection Timeout = 10;”......

他們的論點是.Net / MSSQL已經在幕后設置了連接池,沒有必要放入我們的web.config。 對或錯? 為什么每個其他網站都說如果已經設置了池,那么應該添加池以獲得最佳性能?

3.最小化對DB的調用次數

默認的.Net MVC項目附帶的角色/成員資格提供程序很不錯 - 它非常方便,可以為您完成大部分工作。 但是這些人正在認真地使用UsersInRoles()並像一個全局變量一樣自由地使用它(每次調用此方法時它都會訪問數據庫)。 我創建了一個“用戶對象”,它在每個頁面加載(以及其他一些用戶的東西,如GUID等)上預先加載所有角色,然后查詢該對象是否具有該角色。

該網站的其他部分有FOR語句循環超過200次,並在每次傳遞=超過4,000個數據庫調用時執行20-30個sql查詢。 它以某種方式在幾秒鍾內完成,但我想要做的是將20-30個DB調用合並為一個,這樣它就可以進行200次調用(每個循環)。 但是因為SQL分析器說查詢花了“0秒”,所以它們的論點是它如此快速和小,以至於服務器可以處理這些大量的數據庫查詢。

我的想法是“是的,這些查詢運行速度很快,但它們會破壞整個SQL服務器的性能。” 這可能是一個促成因素嗎? 我是否擔心什么,或者這是服務器整體性能問題的(重要)因素?

4.其他代碼優化

首先想到的是使用StringBuilder和一個簡單的字符串變量。 我理解為什么我應該使用StringBuilder (特別是在循環中),但是他們說這沒關系 - 即使他們需要寫10k +行,他們的論點是性能增益無關緊要。

總而言之,我們所學到的所有東西都已深入到我們身上(“最小化范圍!”)只是“最佳實踐”而沒有真正的性能提升,或者它們是否都會導致實際/可衡量的性能損失?

編輯 ***謝謝大家的所有答案! 我根據你的答案提出了一個新的(第五個)問題:他們實際上並沒有使用“USING”,那么這意味着什么呢? 如果連接池自動發生,它是否會從池中連接到GC到來之前? 是否可能每個與SQL服務器的開放連接都會給服務器增加一點負擔並減慢它的速度?

根據你的建議,我計划對連接時間做一些嚴格的基准測試/記錄,因為我懷疑a)服務器很慢,b)他們沒有關閉連接和c)Profiler說它在0秒內運行,速度慢可能來自連接。

我真的很感謝你的幫助。 再次感謝

對代碼進行分支,根據當前代碼庫進行更改和基准測試+配置文件。 然后你會得到一些證據來支持你的說法。

至於你的問題,這里有:

  1. 你應該總是手動處理實現IDisposable的類,GC實際上不會調用dispose,但是如果類也實現了終結器,那么它將調用終結器,但是在大多數實現中它們只清理非托管資源。

  2. 確實.NET框架已經進行了連接池,我不確定默認值是什么,但是連接字符串值只是允許你改變它們。

  3. SQL語句的執行時間只是故事的一部分,在SQL分析器中,您將看到數據庫引擎執行查詢所花費的時間,您缺少的是Web服務器連接所需的時間。並從數據庫服務器接收結果,因此查詢可能很快,您可以通過批處理查詢節省大量的IO和網絡延遲。

  4. 這個是一個很好的一個來進行一些分析,以證明串聯構建器上連接所使用的額外內存。

奧耶。 當然,您不能讓GC關閉您的數據庫連接。 GC可能不會發生很長時間......有時幾個小時后。 一旦變量超出范圍,它就不會立即發生。 大多數人使用IDisposable使用(){}語法,這很好,但至少在某些地方,某處需要調用connection.Close()

  1. 實現IDisposable並保持inmanaged資源的對象也實現了一個finilizer,它將確保在GC期間調用dispose,問題是當它被調用時,gc可能需要花費大量時間來完成它而你需要遷移這些資源。 一旦完成,使用就會調用dispose。

  2. 您可以在webconfig中修改池的參數,但現在默認情況下它已打開,因此如果您保留默認參數,則無法獲得任何內容

  3. 您不僅要考慮執行查詢需要多長時間,還要考慮應用程序服務器和數據庫之間的連接時間,即使它在同一台計算機上增加了開銷。

  4. StringBuilder不會影響大多數Web應用程序的性能,只有當你將2次連接到同一個字符串時才會很重要,但我認為使用它是一個好主意,因為它更容易閱讀。

我認為你在這里有兩個不同的問題。

  1. 代碼的性能
  2. SQL Server數據庫的性能

SQL Server

您是否對SQL Server進行了任何監控? 您是否具體了解導致死鎖的查詢?

我會閱讀關於死鎖的這篇文章,並考慮安裝輝煌的誰是活躍的,以找出SQL Server中真正發生的事情。 你也可以考慮安裝sp_Blitz布倫特奧扎爾。 這應該可以讓您對數據庫中發生的事情有一個很好的了解,並為您提供解決該問題的工具。

其他代碼問題

我無法真正評論其他代碼問題。 所以我先看看SQL服務器。

記得

  1. 監控
  2. 找出問題
  3. 輪廓
  4. 固定
  5. 轉到1

嗯,我不是大師,但我確實有一個建議:如果他們說你錯了,告訴他們,“證明它!給我一個測試!告訴我4000個電話和200個電話一樣快,並且有對服務器的影響相同!“

與其他事情一樣。 如果你無法讓他們證明你是對的,那就證明他們是錯的,有明確的,有充分證據的測試證明你所說的是正確的。

如果他們甚至沒有公開的證據,從他們自己的服務器收集,他們可以查看和檢查代碼,那么你可能會浪費你的時間在那個團隊。

冒着重復其他人所說的話的風險,這是關於此事的我的2c

首先,你應該仔細挑選你的戰斗......我不會在所有4點上與你的同事開戰,因為一旦你未能證明其中一個,它就結束了,從他們的角度來看,他們是正確的你錯了。 還要記住,沒有人喜歡被告知他們美麗的代碼是一個丑陋的嬰兒,所以我認為你將是外交 - 不要說“這很慢”,說“我找到了一種方法,使這更快“....(當然你的團隊可能是完全合理的,所以我也基於我自己的經驗:)所以你需要從上面的4個區域中選擇一個來解決。

我的錢在#3上。 1,2和4可以有所作為,但根據我自己的經驗,並沒有那么多 - 但你在#3中所描述的聽起來像是因為可憐的舊服務器的千張紙張而死! 查詢可能執行速度很快,因為它們已經參數化,因此它們被緩存了,但是你需要記住,探查器中的“0秒”可能是900毫秒,如果你看到我的意思...加上許多事情開始變慢; 這也可能是鎖的主要來源,因為如果這些嵌套查詢中的每一個都反復敲擊同一個表,無論它運行得多快,根據您提到的用戶數量,您肯定會有爭用。 獲取SQL並在SSMS中運行它,但包括客戶端統計信息,這樣您不僅可以查看執行時間,還可以查看發送回客戶端的數據量; 這將使您更清楚地了解所涉及的開銷類型。

真正唯一可以證明這一點的方法是設置其他人提到的測試和測量,但也要確保在服務器上運行一些分析 - 鎖,IO隊列等,以便您可以顯示不僅是你的方式更快,而且它減少了服務器上的負擔。

觸及你的第五個問題 - 我不確定,但我猜想任何未自動處理的SqlConnection(通過使用)都被視為仍處於“活動狀態”,並且不再可以從池中獲取。 話雖如此 - 服務器上的連接開銷非常低,除非連接實際上正在做任何事情 - 但您可以通過使用SQL性能計數器再次證明這一點。

祝它好運,迫不及待地想知道你是怎么過的。

using子句只是語法糖,你本質上是在做

try
{
    resouce.DoStuff();
}
finally
{
     resource.Dispose()
}

當對象被垃圾收集時,Dispose可能會被調用,但前提是框架程序員在實現一次性模式方面做得很好。 所以這里反對你的同事的論點是:

i)如果我們養成使用的習慣,我們確保釋放非托管資源,因為並非所有框架程序員都能夠實現一次性模式。

ii)是的,GC最終將清理該對象,但可能需要一段時間,具體取決於該對象的年齡。 gen 2 GC清理每秒只進行一次。

所以簡而言之:

  1. 往上看

  2. 是的,池默認設置為true,最大池大小設置為100

  3. 你是對的,絕對是推動改進的最佳領域。

  4. 過早優化是萬惡之源。 首先獲得#1和#3。 使用SQL profiler和db特定方法(添加索引,對它們進行碎片整理,監視死鎖等)。

  5. 是的,可能。 最好的方法是測量它 - 查看perf計數器SQLServer:General Statistics - User Connections; 是一篇描述如何做的文章。

始終衡量您的改進,不要在沒有證據的情況下更改代碼!

我最近正在處理我們的Web應用程序和我們的電子郵件提供商之間的交互中的錯誤。 發送電子郵件時,發生協議錯誤。 但不是馬上。

我能夠確定錯誤僅在SmtpClient實例關閉時發生,這是在處理SmtpClient時發生的,這只發生在垃圾收集期間。

我注意到點擊“發送”按鈕后經常需要兩分鍾 ...

不用說,代碼現在正確地為SmtpClientMailMessage實例using塊。

對智者說一句話......

1已經在上面解決了(我同意它處理得很好,然而,並且發現它是一個很好的做法)。

2是從以前版本的ODBC中保留的一點,其中SQL Server連接是獨立配置的。 它曾經是非默認的; 現在它是默認的。

對於3和4,4不會影響SQL Server的性能 - 當然, StringBuilder可能有助於加速UI中的進程,這可能會更快地關閉SQL資源,但它們不會減少加載SQL Server。

對我來說,3聽起來像是最合乎邏輯的地方。 我嘗試盡快關閉我的數據庫連接,並盡可能少地調用。 如果您正在使用LINQ ,請將所有內容放入IQueryable或其他內容(列表,數組等),以便您可以操作它並構建所需的任何UI結構,同時在任何該hokum之前釋放連接。

所有這些都說,聽起來你需要花更多的時間在探查器上。 而不是查看每次執行所花費的時間,而不是查看處理器和內存使用情況。 僅僅因為他們的速度快並不意味着他們不是“飢餓”的執行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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