自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

SQL查詢提速秘訣,避免鎖死數(shù)據(jù)庫的數(shù)據(jù)庫代碼

數(shù)據(jù)庫
我們說調(diào)優(yōu)數(shù)據(jù)庫既是門藝術(shù),又是門科學,這是有道理的,因為很少有全面適用的硬性規(guī)則。你在一個系統(tǒng)上解決的問題在另一個系統(tǒng)上不是問題,反之亦然。說到調(diào)優(yōu)查詢,沒有正確的答案,但這并不意味著就此應該放棄。你可以遵循以下一些原則,有望收到很好的效果。

[[220896]]

由于數(shù)據(jù)庫領(lǐng)域仍相對不成熟,每個平臺上的 SQL 開發(fā)人員都在苦苦掙扎,一次又一次犯同樣的錯誤。當然,數(shù)據(jù)庫廠商在取得一些進展,并繼續(xù)在竭力處理較重大的問題。

無論 SQL 開發(fā)人員在 SQL Server、Oracle、DB2、Sybase、MySQL,還是在其他任何關(guān)系數(shù)據(jù)庫平臺上編寫代碼,并發(fā)性、資源管理、空間管理和運行速度都仍困擾著他們。

問題的一方面是,不存在什么靈丹妙藥;針對幾乎每條***實踐,我都可以舉出至少一個例外。

我們說調(diào)優(yōu)數(shù)據(jù)庫既是門藝術(shù),又是門科學,這是有道理的,因為很少有全面適用的硬性規(guī)則。你在一個系統(tǒng)上解決的問題在另一個系統(tǒng)上不是問題,反之亦然。

說到調(diào)優(yōu)查詢,沒有正確的答案,但這并不意味著就此應該放棄。你可以遵循以下一些原則,有望收到很好的效果。

不要用 UPDATE 代替 CASE

這個問題很常見,卻很難發(fā)覺,許多開發(fā)人員常常忽視這個問題,原因是使用 UPDATE 再自然不過,這似乎合乎邏輯。

以這個場景為例:你把數(shù)據(jù)插入一個臨時表中,如果另一個值存在,需要它顯示某個值。

也許你從 Customer 表中提取記錄,想把訂單金額超過 100000 美元的客戶標記為“Preferred”。

因而,你將數(shù)據(jù)插入到表中,運行 UPDATE 語句,針對訂單金額超過 100000 美元的任何客戶,將 CustomerRank 這一列設為“Preferred”。

問題是,UPDATE 語句記入日志,這就意味著每次寫入到表中,要寫入兩次。

解決辦法:在 SQL 查詢中使用內(nèi)聯(lián) CASE 語句,這檢驗每一行的訂單金額條件,并向表寫入“Preferred”標記之前,設置該標記,這樣處理性能提升幅度很驚人。

不要盲目地重用代碼

這個問題也很常見,我們很容易拷貝別人編寫的代碼,因為你知道它能獲取所需的數(shù)據(jù)。

問題是,它常常獲取過多你不需要的數(shù)據(jù),而開發(fā)人員很少精簡,因此到頭來是一大堆數(shù)據(jù)。

這通常表現(xiàn)為 WHERE 子句中的一個額外外連接或額外條件。如果你根據(jù)自己的確切要求精簡重用的代碼,就能大幅提升性能。

需要幾列,就提取幾列

這個問題類似第 2 個問題,但這是列所特有的。很容易用 SELECT* 來編寫所有查詢代碼,而不是把列逐個列出來。

問題同樣是,它提取過多你不需要的數(shù)據(jù),這個錯誤我見過無數(shù)次了。開發(fā)人員對一個有 120 列、數(shù)百萬行的表執(zhí)行 SELECT* 查詢,但***只用到其中的三五列。

因此,你處理的數(shù)據(jù)比實際需要的多得多,查詢返回結(jié)果是個奇跡。你不僅處理過多不需要的數(shù)據(jù),還奪走了其他進程的資源。

不要查詢兩次(double-dip)

這是我看到好多人犯的另一個錯誤:寫入存儲過程,從一個有數(shù)億行的表中提取數(shù)據(jù)。

開發(fā)人員想提取住在加利福尼亞州,年收入高于 4 萬美元的客戶信息。于是,他查詢住在加利福尼亞州的客戶,把查詢結(jié)果放到一個臨時表中。

然后再來查詢年收入高于 4 萬美元的客戶,把那些結(jié)果放到另一個臨時表中。***他連接這兩個表,獲得最終結(jié)果。

你是在逗我吧?這應該用一次查詢來完成,相反你對一個超大表查詢兩次。別犯傻了:大表盡量只查詢一次,你會發(fā)現(xiàn)存儲過程執(zhí)行起來快多了。

一種略有不同的場景是,某個過程的幾個步驟需要大表的一個子集時,這導致每次都要查詢大表。

想避免這個問題,只需查詢這個子集,并將它持久化存儲到別處,然后將后面的步驟指向這個比較小的數(shù)據(jù)集。

知道何時使用臨時表

這個問題解決起來要麻煩一點,但效果顯著。在許多情況下可以使用臨時表,比如防止對大表查詢兩次。還可以使用臨時表,大幅減少連接大表所需的處理能力。

如果你必須將一個表連接到大表,該大表上又有條件,只需將大表中所需的那部分數(shù)據(jù)提取到臨時表中,然后再與該臨時表連接,就可以提升查詢性能。

如果存儲過程中有幾個查詢需要對同一個表執(zhí)行類似的連接,這同樣大有幫助。

預暫存數(shù)據(jù)

這是我***聊的話題之一,因為這是一種經(jīng)常被人忽視的老方法。

如果你有一個報表或存儲過程(或一組)要對大表執(zhí)行類似的連接操作,通過提前連接表,并將它們持久化存儲到一個表中來預暫存數(shù)據(jù),就可以對你大有幫助。

現(xiàn)在,報表可以針對該預暫存表來運行,避免大連接。你并非總是可以使用這個方法,但一旦用得上,你會發(fā)現(xiàn)這絕對是節(jié)省服務器資源的好方法。

請注意:許多開發(fā)人員避開這個連接問題的做法是,將注意力集中在查詢本身上,根據(jù)連接創(chuàng)建只讀視圖,那樣就不必一次又一次鍵入連接條件。

但這種方法的問題是,仍要為需要它的每個報表運行查詢。如果預暫存數(shù)據(jù),你只要運行一次連接(比如說報表前 10 分鐘),別人就可以避免大連接了。

你不知道我有多喜歡這一招,在大多數(shù)環(huán)境下,有些常用表一直被連接起來,所以沒理由不能先預暫存起來。

批量刪除和更新

這是另一個經(jīng)常被忽視的技巧,如果你操作不當,刪除或更新來自大表的大量數(shù)據(jù)可能是一場噩夢。

問題是,這兩種語句都作為單一事務來運行。如果你需要終結(jié)它們,或者它們在執(zhí)行時系統(tǒng)遇到了問題,系統(tǒng)必須回滾(roll back)整個事務,這要花很長的時間。

這些操作在持續(xù)期間還會阻塞其他事務,實際上給系統(tǒng)帶來了瓶頸,解決辦法就是,小批量刪除或更新。

這通過幾個方法來解決問題:

無論事務因什么原因而被終結(jié),它只有少量的行需要回滾,那樣數(shù)據(jù)庫聯(lián)機返回快得多。

小批量事務被提交到磁盤時,其他事務可以進來處理一些工作,因而大大提高了并發(fā)性。

同樣,許多開發(fā)人員一直固執(zhí)地認為:這些刪除和更新操作必須在同一天完成。事實并非總是如此,如果你在歸檔更是如此。

如果你需要延長該操作,可以這么做,小批量有助于實現(xiàn)這點;如果你花更長的時間來執(zhí)行這些密集型操作,切忌拖慢系統(tǒng)的運行速度。

使用臨時表來提高游標性能

如果可能的話,***避免游標。游標不僅存在速度問題,而速度問題本身是許多操作的一大問題,還會導致你的操作長時間阻塞其他操作,這大大降低了系統(tǒng)的并發(fā)性。

然而無法總是避免使用游標,避免不了使用游標時,可以改而對臨時表執(zhí)行游標操作,以此擺脫游標引發(fā)的性能問題。

不妨以查閱一個表,基于一些比較結(jié)果來更新幾個列的游標為例。你也許可以將該數(shù)據(jù)放入臨時表中,然后針對臨時表進行比較,而不是針對活動表進行比較。

然后你可以針對小得多,鎖定時間很短的活動表運行單一的 UPDATE 語句。

進行這樣的數(shù)據(jù)修改可大大提高并發(fā)性。***我要說,你根本不需要使用游標,總是會有一種基于集合的解決方法。

使用表值函數(shù)

這是一直以來我***用的技巧之一,因為它是只有專家才知道的那種秘訣。

在查詢的 SELECT 列表中使用標量函數(shù)時,該函數(shù)因結(jié)果集中的每一行而被調(diào)用,這會大幅降低大型查詢的性能。

然而可以將標量函數(shù)轉(zhuǎn)換成表值函數(shù),然后在查詢中使用 CROSS APPLY,就可以大幅提升性能,這個奇妙的技巧可以顯著提升性能。

不要對同一批次的許多表執(zhí)行大型操作

這個似乎很明顯,但實則不然。我會用另一個鮮活的例子,因為它更能說明問題。

我有一個系統(tǒng)存在大量的阻塞,眾多操作處于停滯狀態(tài)。結(jié)果查明,每天運行幾次的刪除例程在刪除顯式事務中 14 個表的數(shù)據(jù)。處理一個事務中的所有 14 個表意味著,鎖定每個表,直到所有刪除完成。

解決辦法就是,將每個表的刪除分解成單獨的事務,以便每個刪除事務只鎖定一個表。

這解放了其他表,緩解了阻塞,讓其他操作得以繼續(xù)運行。你總是應該把這樣的大事務分解成單獨的小事務,以防阻塞。

不要使用觸發(fā)器

這個與前一個大體一樣,但還是值得一提。觸發(fā)器的問題:無論你希望觸發(fā)器執(zhí)行什么,都會在與原始操作同一個的事務中執(zhí)行。

如果你寫一個觸發(fā)器,以便更新 Orders 表中的行時將數(shù)據(jù)插入到另一個表中,會同時鎖定這兩個表,直到觸發(fā)器執(zhí)行完畢。

如果你需要在更新后將數(shù)據(jù)插入到另一個表中,要將更新和插入放入到存儲過程中,并在單獨的事務中執(zhí)行。

如果你需要回滾,就很容易回滾,不必同時鎖定這兩個表。與往常一樣,事務要盡量短小,每次不要鎖定多個資源。

不要在 GUID 上聚類

這么多年后,我難以相信我們居然還在為這個問題而苦惱。但我仍然每年遇到至少兩次聚類 GUID。

GUID(全局唯一標識符)是一個 16 字節(jié)的隨機生成的數(shù)字。相比使用一個穩(wěn)定增加的值(比如 DATE 或 IDENTITY),按此列對你表中的數(shù)據(jù)進行排序?qū)е卤硭槠斓枚唷?/p>

幾年前我做過一項基準測試,我將一堆數(shù)據(jù)插入到一個帶聚類 GUID 的表中,將同樣的數(shù)據(jù)插入到另一個帶 IDENTITY 列的表中。

GUID 表碎片化極其嚴重,僅僅過了 15 分鐘,性能就下降了幾千個百分點。

5 小時后,IDENTITY 表的性能才下降了幾個百分點,這不僅僅適用于 GUID,它適用于任何易失性列。

如果只需查看數(shù)據(jù)是否存在,就不要計數(shù)行

這種情況很常見,你需要查看數(shù)據(jù)存在于表格中,根據(jù)這番檢查的結(jié)果,你要執(zhí)行某個操作。

我經(jīng)常見到有人執(zhí)行 SELECT COUNT(*)FROMdbo.T1來檢查該數(shù)據(jù)是否存在:

 

  1. SET @CT=(SELECT COUNT(*) FROM  
  2. dbo.T1);  
  3. If@CT>0  
  4. BEGIN  
  5. <Do something>  
  6. END 

這完全沒必要,如果你想檢查數(shù)據(jù)是否存在,只要這么做:

 

  1. If EXISTS (SELECT 1 FROM dbo.T1)  
  2. BEGIN  
  3. <Do something>  
  4. END 

 

不要計數(shù)表中的一切,只要取回你找到的***行。SQL Server 聰明得很,會正確使用 EXISTS,第二段代碼返回結(jié)果超快。

表越大,這方面的差距越明顯。在你的數(shù)據(jù)變得太大之前做正確的事情。調(diào)優(yōu)數(shù)據(jù)庫永不嫌早。

實際上,我只是在我的其中一個生產(chǎn)數(shù)據(jù)庫上運行這個例子,針對一個有 2.7 億行的表。

***次查詢用時 15 秒,包含 456197 個邏輯讀取,第二次查詢不到 1 秒就返回結(jié)果,只包含 5 個邏輯讀取。

然而如果你確實需要計數(shù)表的行數(shù),表又很大,另一種方法就是從系統(tǒng)表中提取,

SELECT rows fromsysindexes 將為你獲得所有索引的行數(shù)。

又由于聚類索引代表數(shù)據(jù)本身,所以只要添加 WHERE indid = 1,就能獲得表行,然后只需包含表名稱即可。

所以,***的查詢是:

 

  1. SELECT rows from sysindexes where object_name(id)='T1'and indexid =1 

在我 2.7 億行的表中,不到 1 秒就返回結(jié)果,只有 6 個邏輯讀取,現(xiàn)在性能不一樣了。

不要進行逆向搜索

以簡單的查詢 SELECT * FROMCustomers WHERE RegionID <> 3 為例。你不能將索引與該查詢結(jié)合使用,因為它是逆向搜索,需要借助表掃描來逐行比較。如果你需要執(zhí)行這樣的任務,可能發(fā)現(xiàn)如果重寫查詢以使用索引,性能會好得多。

該查詢很容易重寫,就像這樣:

 

  1. SELECT * FROM Customers WHERE RegionID<3 UNION ALL SELECT * FROM Customers WHERE RegionID 

這個查詢將使用索引,所以如果你的數(shù)據(jù)集很大,其性能會遠勝過表掃描版本。

當然,沒有什么是那么容易的,也許性能更糟,所以使用之前先試一下。它***管用,雖然涉及太多的因素。

***,我意識到這個查詢違反了第 4 條規(guī)則:不要查詢兩次,但這也表明沒有硬性規(guī)則。雖然我們在這里查詢兩次,但這么做是為了避免開銷很大的表掃描。

你無法一直運用所有這些技巧,但如果牢記它們,有一天你會用它們來解決一些大問題。

要記住的最重要一點是,別將我說的話當成教條。在你的實際環(huán)境中試一下,同樣的解決辦法不是在每種情況下都管用,不過我排查糟糕的性能時一直使用這些方法,而且屢試不爽。 

責任編輯:龐桂玉 來源: ITPUB
相關(guān)推薦

2011-08-09 17:24:21

SQL Server 數(shù)據(jù)庫日志

2010-07-06 09:44:51

SQL Server數(shù)

2018-06-21 10:05:07

數(shù)據(jù)庫管理SQL解析MySQL

2021-05-17 06:57:34

SQLServer數(shù)據(jù)庫

2010-06-29 17:10:22

SQL Server數(shù)

2010-07-15 17:28:50

SQL Server

2009-07-06 21:20:34

SQL Server數(shù)

2023-08-29 10:53:36

2010-07-13 11:47:47

2011-08-22 12:01:36

SQL Server代碼優(yōu)化

2010-07-08 11:05:14

SQL Server數(shù)

2018-10-16 16:00:39

數(shù)據(jù)庫鎖舞MySQL

2011-08-30 11:04:30

鏈接查詢內(nèi)連接外連接

2010-06-29 17:27:14

SQL Server

2009-11-18 16:16:51

Oracle數(shù)據(jù)庫

2010-10-26 15:54:02

連接oracle數(shù)據(jù)庫

2009-01-27 21:00:00

服務器數(shù)據(jù)庫SQL Server

2011-08-16 16:37:40

Oracle數(shù)據(jù)庫樹形查詢根節(jié)點

2009-04-30 09:28:05

SynonymOpenquerySQL Server

2011-04-18 09:03:36

數(shù)據(jù)庫查詢
點贊
收藏

51CTO技術(shù)棧公眾號