資料庫併發學習總結
阿新 • • 發佈:2019-02-14
資料庫併發控制
1. 在資料庫中為什麼要併發控制?
答:資料庫是共享資源,通常有許多個事務同時在執行。當多個事務併發地存取資料庫時就會產生同時讀取和/或修改同一資料的情況。若對併發操作不加控制就可能會存取和儲存不正確的資料,破壞資料庫的一致性。所以資料庫管理系統必須提供併發控制機制。
2 .併發操作可能會產生哪幾類資料不一致?用什麼方法能避免各種不一致的情況?
答:併發操作帶來的資料不一致性包括三類:丟失修改、不可重複讀和讀“髒’夕資料。 ( l )丟失修改(lost update ) 兩個事務 Tl 和T2讀入同一資料並修改,T2提交的結果破壞了(覆蓋了) Tl 提交的結果,導致 Tl 的修改被丟失。 ( 2 )不可重複讀( Non 一 Repeatable Read ) 不可重複讀是指事務 Tl 讀取資料後,事務幾執行更新操作,使 Tl 無法再現前一次讀取結果。( 3 )讀“髒”資料( Dirty Read ) 讀“髒’夕資料是指事務 Tl 修改某一資料,並將其寫回磁碟,事務幾讀取同一資料後, Tl 由於某種原因被撤銷,這時 Tl 已修改過的資料恢復原值,幾讀到的資料就與資料庫中的資料不一致,則幾讀到的資料就為“髒”資料,即不正確的資料。避免不一致性的方法和技術就是併發控制。最常用的技術是封鎖技術。也可以用其他技術,例如在分散式資料庫系統中可以採用時間戳方法來進行併發控制。
3 .什麼是封鎖?基本的封鎖型別有幾種?試述它們的含義。
答:封鎖就是事務 T 在對某個資料物件例如表、記錄等操作之前,先向系統發出請求,對其加鎖。加鎖後事務 T 就對該資料物件有了一定的控制,在事務 T 釋放它的鎖之前,其他的事務不能更新此資料物件。封鎖是實現併發控制的一個非常重要的技術。
基本的封鎖型別有兩種:排它鎖( Exclusive Locks ,簡稱 x 鎖)和共享鎖 ( Share Locks,簡稱 S 鎖)。排它鎖又稱為寫鎖。若事務 T 對資料物件 A 加上 X 鎖,則只允許 T 讀取和修改 A ,其他任何事務都不能再對 A 加任何型別的鎖,直到 T 釋放 A 上的鎖。這就保證了其他事務在 T 釋放 A 上的鎖之前不能再讀取和修改 A 。共享鎖又稱為讀鎖。若事務 T 對資料物件 A 加上 S 鎖,則事務 T 可以讀 A但不能修改 A ,其他事務只能再對 A 加 S 鎖,而不能加 X 鎖,直到 T 釋放 A 上的 S 鎖。這就保證了其他事務可以讀 A ,但在 T 釋放 A 上的 S 鎖之前不能對 A 做任何修改。
4 .如何用封鎖機制保證資料的一致性?
答: DBMS 在對資料進行讀、寫操作之前首先對該資料執行封鎖操作,例如下圖中事務 Tl 在對 A 進行修改之前先對 A 執行 xock ( A ) ,即對 A 加 x 鎖。這樣,當幾請求對 A 加 x 鎖時就被拒絕,幾隻能等待 Tl 釋放 A 上的鎖後才能獲得對 A 的 x 鎖,這時它讀到的 A 是 Tl 更新後的值,再按此新的 A 值進行運算。這樣就不會丟失 Tl 的更新。
DBMS 按照一定的封鎖協議,對併發操作進行控制,使得多個併發操作有序地執行,就可以避免丟失修改、不可重複讀和讀“髒’夕資料等資料不一致性。
5 .什麼是活鎖?什麼是死鎖?
答:
如果事務 Tl 封鎖了資料 R ,事務幾又請求封鎖 R ,於是幾等待。幾也請求封鎖 R ,當 Tl 釋放了 R 上的封鎖之後系統首先批准了幾的請求,幾仍然等待。然後幾又請求封鎖 R ,當幾釋放了 R 上的封鎖之後系統又批准了幾的請求 … … 幾有可能永遠等待,這就是活鎖的情形。活鎖的含義是該等待事務等待時間太長,似乎被鎖住了,實際上可能被啟用。如果事務 Tl 封鎖了資料 Rl ,幾封鎖了資料凡,然後 Tl 又請求封鎖幾,因幾已封鎖了幾,於是 Tl 等待幾釋放几上的鎖。接著幾又申請封鎖 Rl ,因 Tl 已封鎖了 Rl ,幾也只能等待 Tl 釋放 Rl 上的鎖。這樣就出現了 Tl 在等待幾,而幾又在等待 T }的局面, T }和幾兩個事務永遠不能結束,形成死鎖。
6 .試述活鎖的產生原因和解決方法。
答:活鎖產生的原因:當一系列封鎖不能按照其先後順序執行時,就可能導致一些事務無限期等待某個封鎖,從而導致活鎖。避免活鎖的簡單方法是採用先來先服務的策略。當多個事務請求封鎖同一資料物件時,封鎖子系統按請求封鎖的先後次序對事務排隊,資料物件上的鎖一旦釋放就批准申請佇列中第一個事務獲得鎖。
11 .請給出檢測死鎖發生的一種方法,當發生死鎖後如何解除死鎖?
答:資料庫系統一般採用允許死鎖發生, DBMS 檢測到死鎖後加以解除的方法。 DBMS 中診斷死鎖的方法與作業系統類似,一般使用超時法或事務等待圖法。超時法是:如果一個事務的等待時間超過了規定的時限,就認為發生了死鎖。超時法實現簡單,但有可能誤判死鎖,事務因其他原因長時間等待超過時限時,系統會誤認為發生了死鎖。若時限設定得太長,又不能及時發現死鎖發生。 DBMS 併發控制子系統檢測到死鎖後,就要設法解除。通常採用的方法是選擇一個處理死鎖代價最小的事務,將其撤消,釋放此事務持有的所有鎖,使其他事務得以繼續執行下去。當然,對撤銷的事務所執行的資料修改操作必須加以恢復。
12 .什麼樣的併發排程是正確的排程?
答:可序列化( Serializable )的排程是正確的排程。可序列化的排程的定義:多個事務的併發執行是正確的,當且僅當其結果與按某一次序序列執行它們時的結果相同,稱這種排程策略為可序列化的排程。
9 .設 Tl ,幾,幾是如下的 3 個事務:
Tl :A : = A + 2 ;
T2:A : = A * 2 ;
T3:A : = A **2 ; ( A <-A*A)
設 A 的初值為 0 。
( l )若這 3 個事務允許並行執行,則有多少可能的正確結果,請一一列舉出來。
答 :A 的最終結果可能有 2 、 4 、 8 、 16 。因為序列執行次序有 Tl T2T3、 Tl T3T2、T2T1T3、T2T3Tl 、T3T1T2、T3T2 Tl 。對應的執行結果是 16 、 8 · 4 · 2 · 4 · 2 。
( 2 )請給出一個可序列化的排程,並給出執行結果
答:
最後結果 A 為 16 ,是可序列化的排程。
( 3 )請給出一個非序列化的排程,並給出執行結果。
答:最後結果 A 為 0 ,為非序列化的排程。
( 4 )若這 3 個事務都遵守兩段鎖協議,請給出一個不產生死鎖的可序列化排程。
答:
( 5 )若這 3 個事務都遵守兩段鎖協議,請給出一個產生死鎖的排程。
答:
11.試證明,若併發事務遵守兩段鎖協議,則對這些事務的併發排程是可序列化的。
證明:首先以兩個併發事務 Tl 和T2為例,存在多個併發事務的情形可以類推。根據可序列化定義可知,事務不可序列化只可能發生在下列兩種情況:
( l )事務 Tl 寫某個資料物件 A ,T2讀或寫 A ;
( 2 )事務 Tl 讀或寫某個資料物件 A ,T2寫 A 。
下面稱 A 為潛在衝突物件。
設 Tl 和T2訪問的潛在衝突的公共物件為{A1,A2 … , An }。不失一般性,假設這組潛在衝突物件中 X =(A 1 , A2 , … , Ai }均符合情況 1 。 Y ={A i + 1 , … , An }符合所情況( 2 )。
VX ∈ x , Tl 需要 XlockX ①
T2 需要 Slockx 或 Xlockx ②
1 )如果操作 ① 先執行,則 Tl 獲得鎖,T2等待
由於遵守兩段鎖協議, Tl 在成功獲得 x 和 Y 中全部物件及非潛在衝突物件的鎖後,才會釋放鎖。
這時如果存在 w ∈ x 或 Y ,T2已獲得 w 的鎖,則出現死鎖;否則, Tl 在對 x 、 Y 中物件全部處理完畢後,T2才能執行。這相當於按 Tl 、T2的順序序列執行,根據可序列化定義, Tl 和幾的排程是可序列化的。
2 )操作 ② 先執行的情況與( l )對稱因此,若併發事務遵守兩段鎖協議,在不發生死鎖的情況下,對這些事務的併發排程一定是可序列化的。證畢。
12 .舉例說明,對併發事務的一個排程是可序列化的,而這些併發事務不一定遵守兩段鎖協議。
答:
13 .為什麼要引進意向鎖?意向鎖的含義是什麼?
答:引進意向鎖是為了提高封鎖子系統的效率。該封鎖子系統支援多種封鎖粒度。原因是:在多粒度封鎖方法中一個數據物件可能以兩種方式加鎖 ― 顯式封鎖和隱式封鎖。因此係統在對某一資料物件加鎖時不僅要檢查該資料物件上有無(顯式和隱式)封鎖與之衝突,還要檢查其所有上級結點和所有下級結點,看申請的封鎖是否與這些結點上的(顯式和隱式)封鎖衝突,顯然,這樣的檢查方法效率很低。為此引進了意向鎖。意向鎖的含義是:對任一結點加鎖時,必須先對它的上層結點加意向鎖。例如事務 T 要對某個元組加 X 鎖,則首先要對關係和資料庫加 ix 鎖。換言之,對關係和資料庫加 ix 鎖,表示它的後裔結點 ― 某個元組擬(意向)加 X 鎖。引進意向鎖後,系統對某一資料物件加鎖時不必逐個檢查與下一級結點的封鎖衝突了。例如,事務 T 要對關係 R 加 X 鎖時,系統只要檢查根結點資料庫和 R 本身是否已加了不相容的鎖(如發現已經加了 ix ,則與 X 衝突),而不再需要搜尋和檢查 R 中的每一個元組是否加了 X 鎖或 S 鎖。
14 .試述常用的意向鎖: IS 鎖、 ix 鎖、 SIX 鎖,給出這些鎖的相容矩陣。
答: IS鎖:如果對一個數據物件加 IS 鎖,表示它的後裔結點擬(意向)加 S 鎖。例如,要對某個元組加 S 鎖,則要首先對關係和資料庫加 IS 鎖
IX 鎖:如果對一個數據物件加 ix 鎖,表示它的後裔結點擬(意向功口 X 鎖。例如,要對某個元組加 X 鎖,則要首先對關係和資料庫加 ix 鎖。
SIX 鎖:如果對一個數據物件加 SIX 鎖,表示對它加 S 鎖,再加 IX 鎖,即 SIX = S + IX 。
相容矩陣:
15 .理解並解釋下列術語的含義:封鎖、活鎖、死鎖、排它鎖、共享鎖、併發事務的排程、可序列化的排程、兩段鎖協議。
答:(略,已經在上面有關習題中解答)
16 .試述你瞭解的某一個實際的 DBMS 產品的併發控制機制。
答:(略,參見簡單介紹了有關 Oracle 的併發控制機制。
========
大資料量高併發的資料庫優化
如果不能設計一個合理的資料庫模型,不僅會增加客戶端和伺服器段程式的程式設計和維護的難度,而且將會影響系統實際執行的效能。所以,在一個系統開始實施之前,完備的資料庫模型的設計是必須的。
在一個系統分析、設計階段,因為資料量較小,負荷較低。我們往往只注意到功能的實現,而很難注意到效能的薄弱之處,等到系統投入實際執行一段時間後,才發現系統的效能在降低,這時再來考慮提高系統性能則要花費更多的人力物力,而整個系統也不可避免的形成了一個打補丁工程。
所以在考慮整個系統的流程的時候,我們必須要考慮,在高併發大資料量的訪問情況下,我們的系統會不會出現極端的情況。(例如:對外統計系統在7月16日出現的資料異常的情況,併發大資料量的的訪問造成,資料庫的響應時間不能跟上資料重新整理的速度造成。具體情況是:在日期臨界時(00:00:00),判斷資料庫中是否有當前日期的記錄,沒有則插入一條當前日期的記錄。在低併發訪問的情況下,不會發生問題,但是當日期臨界時的訪問量相當大的時候,在做這一判斷的時候,會出現多次條件成立,則資料庫裡會被插入多條當前日期的記錄,從而造成資料錯誤。),資料庫的模型確定下來之後,我們有必要做一個系統內資料流向圖,分析可能出現的瓶頸。
為了保證資料庫的一致性和完整性,在邏輯設計的時候往往會設計過多的表間關聯,儘可能的降低資料的冗餘。(例如使用者表的地區,我們可以把地區另外存放到一個地區表中)如果資料冗餘低,資料的完整性容易得到保證,提高了資料吞吐速度,保證了資料的完整性,清楚地表達資料元素之間的關係。而對於多表之間的關聯查詢(尤其是大資料表)時,其效能將會降低,同時也提高了客戶端程式的程式設計難度,因此,物理設計需折衷考慮,根據業務規則,確定對關聯表的資料量大小、資料項的訪問頻度,對此類資料表頻繁的關聯查詢應適當提高資料冗餘設計但增加了表間連線查詢的操作,也使得程式的變得複雜,為了提高系統的響應時間,合理的資料冗餘也是必要的。設計人員在設計階段應根據系統操作的型別、頻度加以均衡考慮。
另外,最好不要用自增屬性欄位作為主鍵與子表關聯。不便於系統的遷移和資料恢復。對外統計系統對映關係丟失(******************)。
原來的表格必須可以通過由它分離出去的表格重新構建。使用這個規定的好處是,你可以確保不會在分離的表格中引入多餘的列,所有你建立的表格結構都與它們的實際需要一樣大。應用這條規定是一個好習慣,不過除非你要處理一個非常大型的資料,否則你將不需要用到它。(例如一個通行證系統,我可以將USERID,USERNAME,USERPASSWORD,單獨出來作個表,再把USERID作為其他表的外來鍵)
表的設計具體注意的問題:
1、資料行的長度不要超過8020位元組,如果超過這個長度的話在物理頁中這條資料會佔用兩行從而造成儲存碎片,降低查詢效率。
2、能夠用數字型別的欄位儘量選擇數字型別而不用字串型別的(電話號碼),這會降低查詢和連線的效能,並會增加儲存開銷。這是因為引擎在處理查詢和連接回逐個比較字串中每一個字元,而對於數字型而言只需要比較一次就夠了。
3、對於不可變字元型別char和可變字元型別varchar 都是8000位元組,char查詢快,但是耗儲存空間,varchar查詢相對慢一些但是節省儲存空間。在設計欄位的時候可以靈活選擇,例如使用者名稱、密碼等長度變化不大的欄位可以選擇CHAR,對於評論等長度變化大的欄位可以選擇VARCHAR。
4、欄位的長度在最大限度的滿足可能的需要的前提下,應該儘可能的設得短一些,這樣可以提高查詢的效率,而且在建立索引的時候也可以減少資源的消耗。
二、查詢的優化
保證在實現功能的基礎上,儘量減少對資料庫的訪問次數;通過搜尋引數,儘量減少對錶的訪問行數,最小化結果集,從而減輕網路負擔;能夠分開的操作儘量分開處理,提高每次的響應速度;在資料視窗使用SQL時,儘量把使用的索引放在選擇的首列;演算法的結構儘量簡單;在查詢時,不要過多地使用萬用字元如SELECT * FROM T1語句,要用到幾列就選擇幾列如:SELECT COL1,COL2 FROM T1;在可能的情況下儘量限制儘量結果集行數如:SELECT TOP 300 COL1,COL2,COL3 FROM T1,因為某些情況下使用者是不需要那麼多的資料的。
在沒有建索引的情況下,資料庫查詢某一條資料,就必須進行全表掃描了,對所有資料進行一次遍歷,查找出符合條件的記錄。在資料量比較小的情況下,也許看不出明顯的差別,但是當資料量大的情況下,這種情況就是極為糟糕的了。
SQL語句在SQL SERVER中是如何執行的,他們擔心自己所寫的SQL語句會被SQL SERVER誤解。比如:
select * from table1 where name='zhangsan' and tID > 10000
和執行:
select * from table1 where tID > 10000 and name='zhangsan'
一些人不知道以上兩條語句的執行效率是否一樣,因為如果簡單的從語句先後上看,這兩個語句的確是不一樣,如果tID是一個聚合索引,那麼後一句僅僅從表的10000條以後的記錄中查詢就行了;而前一句則要先從全表中查詢看有幾個name='zhangsan'的,而後再根據限制條件條件tID>10000來提出查詢結果。
事實上,這樣的擔心是不必要的。SQL SERVER中有一個“查詢分析優化器”,它可以計算出where子句中的搜尋條件並確定哪個索引能縮小表掃描的搜尋空間,也就是說,它能實現自動優化。雖然查詢優化器可以根據where子句自動的進行查詢優化,但有時查詢優化器就會不按照您的本意進行快速查詢。
在查詢分析階段,查詢優化器檢視查詢的每個階段並決定限制需要掃描的資料量是否有用。如果一個階段可以被用作一個掃描引數(SARG),那麼就稱之為可優化的,並且可以利用索引快速獲得所需資料。
SARG的定義:用於限制搜尋的一個操作,因為它通常是指一個特定的匹配,一個值的範圍內的匹配或者兩個以上條件的AND連線。形式如下:
列名 操作符 <常數 或 變數> 或 <常數 或 變數> 操作符 列名
列名可以出現在操作符的一邊,而常數或變量出現在操作符的另一邊。如:
Name=’張三’
價格>5000
5000<價格
Name=’張三’ and 價格>5000
如果一個表示式不能滿足SARG的形式,那它就無法限制搜尋的範圍了,也就是SQL SERVER必須對每一行都判斷它是否滿足WHERE子句中的所有條件。所以一個索引對於不滿足SARG形式的表示式來說是無用的。
所以,優化查詢最重要的就是,儘量使語句符合查詢優化器的規則避免全表掃描而使用索引查詢。
具體要注意的:
1.應儘量避免在 where 子句中對欄位進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num is null
可以在num上設定預設值0,確保表中num列沒有null值,然後這樣查詢:
select id from t where num=0
2.應儘量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進行全表掃描。優化器將無法通過索引來確定將要命中的行數,因此需要搜尋該表的所有行。
3.應儘量避免在 where 子句中使用 or 來連線條件,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num=10 or num=20
可以這樣查詢:
select id from t where num=10
union all
select id from t where num=20
4.in 和 not in 也要慎用,因為IN會使系統無法使用索引,而只能直接搜尋表中的資料。如:
select id from t where num in(1,2,3)
對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
5.儘量避免在索引過的字元資料中,使用非打頭字母搜尋。這也使得引擎無法利用索引。
見如下例子:
SELECT * FROM T1 WHERE NAME LIKE ‘%L%’
SELECT * FROM T1 WHERE SUBSTING(NAME,2,1)=’L’
SELECT * FROM T1 WHERE NAME LIKE ‘L%’
即使NAME欄位建有索引,前兩個查詢依然無法利用索引完成加快操作,引擎不得不對全表所有資料逐條操作來完成任務。而第三個查詢能夠使用索引來加快操作。
6.必要時強制查詢優化器使用某個索引,如在 where 子句中使用引數,也會導致全表掃描。因為SQL只有在執行時才會解析區域性變數,但優化程式不能將訪問計劃的選擇推遲到執行時;它必須在編譯時進行選擇。然而,如果在編譯時建立訪問計劃,變數的值還是未知的,因而無法作為索引選擇的輸入項。如下面語句將進行全表掃描:
select id from t where
可以改為強制查詢使用索引:
select id from t with(index(索引名)) where [email protected]
7.應儘量避免在 where 子句中對欄位進行表示式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
SELECT * FROM T1 WHERE F1/2=100
應改為:
SELECT * FROM T1 WHERE F1=100*2
SELECT * FROM RECORD WHERE SUBSTRING(CARD_NO,1,4)=’5378’
應改為:
SELECT * FROM RECORD WHERE CARD_NO LIKE ‘5378%’
SELECT member_number, first_name, last_name FROM members
WHERE DATEDIFF(yy,datofbirth,GETDATE()) > 21
應改為:
SELECT member_number, first_name, last_name FROM members
WHERE dateofbirth < DATEADD(yy,-21,GETDATE())
即:任何對列的操作都將導致表掃描,它包括資料庫函式、計算表示式等等,查詢時要儘可能將操作移至等號右邊。
8.應儘量避免在where子句中對欄位進行函式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where substring(name,1,3)='abc'--name以abc開頭的id
select id from t where datediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的id
應改為:
select id from t where name like 'abc%'
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
9.不要在 where 子句中的“=”左邊進行函式、算術運算或其他表示式運算,否則系統將可能無法正確使用索引。
10.在使用索引欄位作為條件時,如果該索引是複合索引,那麼必須使用到該索引中的第一個欄位作為條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應儘可能的讓欄位順序與索引順序相一致。
11.很多時候用 exists是一個好的選擇:
elect num from a where num in(select num from b)
用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num)
SELECT SUM(T1.C1)FROM T1 WHERE(
(SELECT COUNT(*)FROM T2 WHERE T2.C2=T1.C2>0)
SELECT SUM(T1.C1) FROM T1WHERE EXISTS(
SELECT * FROM T2 WHERE T2.C2=T1.C2)
兩者產生相同的結果,但是後者的效率顯然要高於前者。因為後者不會產生大量鎖定的表掃描或是索引掃描。
如果你想校驗表裡是否存在某條紀錄,不要用count(*)那樣效率很低,而且浪費伺服器資源。可以用EXISTS代替。如:
IF (SELECT COUNT(*) FROM table_name WHERE column_name = 'xxx')
可以寫成:
IF EXISTS (SELECT * FROM table_name WHERE column_name = 'xxx')
經常需要寫一個T_SQL語句比較一個父結果集和子結果集,從而找到是否存在在父結果集中有而在子結果集中沒有的記錄,如:
SELECT a.hdr_key FROM hdr_tbl a---- tbl a 表示tbl用別名a代替
WHERE NOT EXISTS (SELECT * FROM dtl_tbl b WHERE a.hdr_key = b.hdr_key)
SELECT a.hdr_key FROM hdr_tbl a
LEFT JOIN dtl_tbl b ON a.hdr_key = b.hdr_key WHERE b.hdr_key IS NULL
SELECT hdr_key FROM hdr_tbl
WHERE hdr_key NOT IN (SELECT hdr_key FROM dtl_tbl)
三種寫法都可以得到同樣正確的結果,但是效率依次降低。
12.儘量使用表變數來代替臨時表。如果表變數包含大量資料,請注意索引非常有限(只有主鍵索引)。
13.避免頻繁建立和刪除臨時表,以減少系統表資源的消耗。
14.臨時表並不是不可使用,適當地使用它們可以使某些例程更有效,例如,當需要重複引用大型表或常用表中的某個資料集時。但是,對於一次性事件,最好使用匯出表。
15.在新建臨時表時,如果一次性插入資料量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果資料量不大,為了緩和系統表的資源,應先create table,然後insert。
16.如果使用到了臨時表,在儲存過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table ,這樣可以避免系統表的較長時間鎖定。
17.在所有的儲存過程和觸發器的開始處設定 SET NOCOUNT ON ,在結束時設定 SET NOCOUNT OFF 。無需在執行儲存過程和觸發器的每個語句後向客戶端傳送 DONE_IN_PROC 訊息。
18.儘量避免大事務操作,提高系統併發能力。
19.儘量避免向客戶端返回大資料量,若資料量過大,應該考慮相應需求是否合理。
20. 避免使用不相容的資料型別。例如float和int、char和varchar、binary和varbinary是不相容的。資料型別的不相容可能使優化器無法執行一些本來可以進行的優化操作。例如:
SELECT name FROM employee WHERE salary > 60000
在這條語句中,如salary欄位是money型的,則優化器很難對其進行優化,因為60000是個整型數。我們應當在程式設計時將整型轉化成為錢幣型,而不要等到執行時轉化。
21.充分利用連線條件,在某種情況下,兩個表之間可能不只一個的連線條件,這時在 WHERE 子句中將連線條件完整的寫上,有可能大大提高查詢速度。
例:
SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO
SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO AND A.ACCOUNT_NO=B.ACCOUNT_NO
第二句將比第一句執行快得多。
22、使用檢視加速查詢
把表的一個子集進行排序並建立檢視,有時能加速查詢。它有助於避免多重排序 操作,而且在其他方面還能簡化優化器的工作。例如:
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
AND cust.postcode>“98000”
ORDER BY cust.name
如果這個查詢要被執行多次而不止一次,可以把所有未付款的客戶找出來放在一個檢視中,並按客戶的名字進行排序:
CREATE VIEW DBO.V_CUST_RCVLBES
AS
SELECT cust.name,rcvbles.balance,……other columns
FROM cust,rcvbles
WHERE cust.customer_id = rcvlbes.customer_id
AND rcvblls.balance>0
ORDER BY cust.name
然後以下面的方式在檢視中查詢:
SELECT * FROM V_CUST_RCVLBES
WHERE postcode>“98000”
檢視中的行要比主表中的行少,而且物理順序就是所要求的順序,減少了磁碟I/O,所以查詢工作量可以得到大幅減少。
23、能用DISTINCT的就不用GROUP BY
SELECT OrderID FROM Details WHERE UnitPrice > 10 GROUP BY OrderID
可改為:
SELECT DISTINCT OrderID FROM Details WHERE UnitPrice > 10
24.能用UNION ALL就不要用UNION
UNION ALL不執行SELECT DISTINCT函式,這樣就會減少很多不必要的資源
35.儘量不要用SELECT INTO語句。
SELECT INOT 語句會導致表鎖定,阻止其他使用者訪問該表。
上面我們提到的是一些基本的提高查詢速度的注意事項,但是在更多的情況下,往往需要反覆試驗比較不同的語句以得到最佳方案。最好的方法當然是測試,看實現相同功能的SQL語句哪個執行時間最少,但是資料庫中如果資料量很少,是比較不出來的,這時可以用檢視執行計劃,即:把實現相同功能的多條SQL語句考到查詢分析器,按CTRL+L看查所利用的索引,表掃描次數(這兩個對效能影響最大),總體上看詢成本百分比即可。
三、演算法的優化
儘量避免使用遊標,因為遊標的效率較差,如果遊標操作的資料超過1萬行,那麼就應該考慮改寫。.使用基於遊標的方法或臨時表方法之前,應先尋找基於集的解決方案來解決問題,基於集的方法通常更有效。與臨時表一樣,遊標並不是不可使用。對小型資料集使用 FAST_FORWARD 遊標通常要優於其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的資料時。在結果集中包括“合計”的例程通常要比使用遊標執行的速度快。如果開發時間允許,基於遊標的方法和基於集的方法都可以嘗試一下,看哪一種方法的效果更好。
遊標提供了對特定集合中逐行掃描的手段,一般使用遊標逐行遍歷資料,根據取出的資料不同條件進行不同的操作。尤其對多表和大表定義的遊標(大的資料集合)迴圈很容易使程式進入一個漫長的等特甚至宕機。
在有些場合,有時也非得使用遊標,此時也可考慮將符合條件的資料行轉入臨時表中,再對臨時表定義遊標進行操作,可時效能得到明顯提高。
(例如:對內統計第一版)
封裝儲存過程
四、建立高效的索引
建立索引一般有以下兩個目的:維護被索引列的唯一性和提供快速訪問表中資料的策略。大型資料庫有兩種索引即簇索引和非簇索引,一個沒有簇索引的表是按堆結構儲存資料,所有的資料均新增在表的尾部,而建立了簇索引的表,其資料在物理上會按照簇索引鍵的順序儲存,一個表只允許有一個簇索引,因此,根據B樹結構,可以理解新增任何一種索引均能提高按索引列查詢的速度,但會降低插入、更新、刪除操作的效能,尤其是當填充因子(Fill Factor)較大時。所以對索引較多的表進行頻繁的插入、更新、刪除操作,建表和索引時因設定較小的填充因子,以便在各資料頁中留下較多的自由空間,減少頁分割及重新組織的工作。
索引是從資料庫中獲取資料的最高效方式之一。95% 的資料庫效能問題都可以採用索引技術得到解決。作為一條規則,我通常對邏輯主鍵使用唯一的成組索引,對系統鍵(作為儲存過程)採用唯一的非成組索引,對任何外來鍵列[欄位]採用非成組索引。不過,索引就象是鹽,太多了菜就鹹了。你得考慮資料庫的空間有多大,表如何進行訪問,還有這些訪問是否主要用作讀寫。
實際上,您可以把索引理解為一種特殊的目錄。微軟的SQL SERVER提供了兩種索引:聚集索引(clustered index,也稱聚類索引、簇集索引)和非聚集索引(nonclustered index,也稱非聚類索引、非簇集索引)。下面,我們舉例來說明一下聚集索引和非聚集索引的區別:
其實,我們的漢語字典的正文字身就是一個聚集索引。比如,我們要查“安”字,就會很自然地翻開字典的前幾頁,因為“安”的拼音是“an”,而按照拼音排序漢字的字典是以英文字母“a”開頭並以“z”結尾的,那麼“安”字就自然地排在字典的前部。如果您翻完了所有以“a”開頭的部分仍然找不到這個字,那麼就說明您的字典中沒有這個字;同樣的,如果查“張”字,那您也會將您的字典翻到最後部分,因為“張”的拼音是“zhang”。也就是說,字典的正文部分本身就是一個目錄,您不需要再去查其他目錄來找到您需要找的內容。
我們把這種正文內容本身就是一種按照一定規則排列的目錄稱為“聚集索引”。
如果您認識某個字,您可以快速地從自動中查到這個字。但您也可能會遇到您不認識的字,不知道它的發音,這時候,您就不能按照剛才的方法找到您要查的字,而需要去根據“偏旁部首”查到您要找的字,然後根據這個字後的頁碼直接翻到某頁來找到您要找的字。但您結合“部首目錄”和“檢字表”而查到的字的排序並不是真正的正文的排序方法,比如您查“張”字,我們可以看到在查部首之後的檢字表中“張”的頁碼是672頁,檢字表中“張”的上面是“馳”字,但頁碼卻是63頁,“張”的下面是“弩”字,頁面是390頁。很顯然,這些字並不是真正的分別位於“張”字的上下方,現在您看到的連續的“馳、張、弩”三字實際上就是他們在非聚集索引中的排序,是字典正文中的字在非聚集索引中的對映。我們可以通過這種方式來找到您所需要的字,但它需要兩個過程,先找到目錄中的結果,然後再翻到您所需要的頁碼。
我們把這種目錄純粹是目錄,正文純粹是正文的排序方式稱為“非聚集索引”。
進一步引申一下,我們可以很容易的理解:每個表只能有一個聚集索引,因為目錄只能按照一種方法進行排序。
(一)何時使用聚集索引或非聚集索引
下面的表總結了何時使用聚集索引或非聚集索引(很重要)。
動作描述 使用聚集索引 使用非聚集索引
列經常被分組排序 應 應
返回某範圍內的資料 應 不應
一個或極少不同值 不應 不應
小數目的不同值 應 不應
大數目的不同值 不應 應
頻繁更新的列 不應 應
外來鍵列 應 應
主鍵列 應 應
頻繁修改索引列 不應 應
事實上,我們可以通過前面聚集索引和非聚集索引的定義的例子來理解上表。如:返回某範圍內的資料一項。比如您的某個表有一個時間列,恰好您把聚合索引建立在了該列,這時您查詢2004年1月1日至2004年10月1日之間的全部資料時,這個速度就將是很快的,因為您的這本字典正文是按日期進行排序的,聚類索引只需要找到要檢索的所有資料中的開頭和結尾資料即可;而不像非聚集索引,必須先查到目錄中查到每一項資料對應的頁碼,然後再根據頁碼查到具體內容。
(二)結合實際,談索引使用的誤區
理論的目的是應用。雖然我們剛才列出了何時應使用聚集索引或非聚集索引,但在實踐中以上規則卻很容易被忽視或不能根據實際情況進行綜合分析。下面我們將根據在實踐中遇到的實際問題來談一下索引使用的誤區,以便於大家掌握索引建立的方法。
1、主鍵就是聚集索引
這種想法筆者認為是極端錯誤的,是對聚集索引的一種浪費。雖然SQL SERVER預設是在主鍵上建立聚集索引的。
通常,我們會在每個表中都建立一個ID列,以區分每條資料,並且這個ID列是自動增大的,步長一般為1。我們的這個辦公自動化的例項中的列Gid就是如此。此時,如果我們將這個列設為主鍵,SQL SERVER會將此列預設為聚集索引。這樣做有好處,就是可以讓您的資料在資料庫中按照ID進行物理排序,但筆者認為這樣做意義不大。
顯而易見,聚集索引的優勢是很明顯的,而每個表中只能有一個聚集索引的規則,這使得聚集索引變得更加珍貴。
從我們前面談到的聚集索引的定義我們可以看出,使用聚集索引的最大好處就是能夠根據查詢要求,迅速縮小查詢範圍,避免全表掃描。在實際應用中,因為ID號是自動生成的,我們並不知道每條記錄的ID號,所以我們很難在實踐中用ID號來進行查詢。這就使讓ID號這個主鍵作為聚集索引成為一種資源浪費。其次,讓每個ID號都不同的欄位作為聚集索引也不符合“大數目的不同值情況下不應建立聚合索引”規則;當然,這種情況只是針對使用者經常修改記錄內容,特別是索引項的時候會負作用,但對於查詢速度並沒有影響。
在辦公自動化系統中,無論是系統首頁顯示的需要使用者簽收的檔案、會議還是使用者進行檔案查詢等任何情況下進行資料查詢都離不開欄位的是“日期”還有使用者本身的“使用者名稱”。
通常,辦公自動化的首頁會顯示每個使用者尚未簽收的檔案或會議。雖然我們的where語句可以僅僅限制當前使用者尚未簽收的情況,但如果您的系統已建立了很長時間,並且資料量很大,那麼,每次每個使用者開啟首頁的時候都進行一次全表掃描,這樣做意義是不大的,絕大多數的使用者1個月前的檔案都已經瀏覽過了,這樣做只能徒增資料庫的開銷而已。事實上,我們完全可以讓使用者開啟系統首頁時,資料庫僅僅查詢這個使用者近3個月來未閱覽的檔案,通過“日期”這個欄位來限制表掃描,提高查詢速度。如果您的辦公自動化系統已經建立的2年,那麼您的首頁顯示速度理論上將是原來速度8倍,甚至更快。
2、只要建立索引就能顯著提高查詢速度
事實上,我們可以發現上面的例子中,第2、3條語句完全相同,且建立索引的欄位也相同;不同的僅是前者在fariqi欄位上建立的是非聚合索引,後者在此欄位上建立的是聚合索引,但查詢速度卻有著天壤之別。所以,並非是在任何欄位上簡單地建立索引就能提高查詢速度。
從建表的語句中,我們可以看到這個有著1000萬資料的表中fariqi欄位有5003個不同記錄。在此欄位上建立聚合索引是再合適不過了。在現實中,我們每天都會發幾個檔案,這幾個檔案的發文日期就相同,這完全符合建立聚集索引要求的:“既不能絕大多數都相同,又不能只有極少數相同”的規則。由此看來,我們建立“適當”的聚合索引對於我們提高查詢速度是非常重要的。
3、把所有需要提高查詢速度的欄位都加進聚集索引,以提高查詢速度
上面已經談到:在進行資料查詢時都離不開欄位的是“日期”還有使用者本身的“使用者名稱”。既然這兩個欄位都是如此的重要,我們可以把他們合併起來,建立一個複合索引(compound index)。
很多人認為只要把任何欄位加進聚集索引,就能提高查詢速度,也有人感到迷惑:如果把複合的聚集索引欄位分開查詢,那麼查詢速度會減慢嗎?帶著這個問題,我們來看一下以下的查詢速度(結果集都是25萬條資料):(日期列fariqi首先排在複合聚集索引的起始列,使用者名稱neibuyonghu排在後列)
我們可以看到如果僅用聚集索引的起始列作為查詢條件和同時用到複合聚集索引的全部列的查詢速度是幾乎一樣的,甚至比用上全部的複合索引列還要略快(在查詢結果集數目一樣的情況下);而如果僅用複合聚集索引的非起始列作為查詢條件的話,這個索引是不起任何作用的。當然,語句1、2的查詢速度一樣是因為查詢的條目數一樣,如果複合索引的所有列都用上,而且查詢結果少的話,這樣就會形成“索引覆蓋”,因而效能可以達到最優。同時,請記住:無論您是否經常使用聚合索引的其他列,但其前導列一定要是使用最頻繁的列。
(三)其他注意事項
“水可載舟,亦可覆舟”,索引也一樣。索引有助於提高檢索效能,但過多或不當的索引也會導致系統低效。因為使用者在表中每加進一個索引,資料庫就要做更多的工作。過多的索引甚至會導致索引碎片。
所以說,我們要建立一個“適當”的索引體系,特別是對聚合索引的建立,更應精益求精,以使您的資料庫能得到高效能的發揮
好像沒有提到高併發的解決方案
索引的使用,學習了
高併發都用NoSQL了,記憶體資料庫是解決高併發實時效能的方向,
這裡有國人開源的高併發框架 www. 8088net .com
========
怎麼解決資料庫併發的問題
1.用一個標識,在選擇那張票的時候先用
(Update 表 set 票flag=‘佔用了!’ where 票flag=‘未佔用’ and ........)
這樣是保險的,不可能存在併發問題,這就牽扯到sql鎖機制問題了,你可以測試一下,其實sql中update是先查詢出然後刪除再新增,但由於使用了update,過程中就自動加鎖了,很方便吧
2.加鎖。
Microsoft® SQL Server™ 2000 使用鎖定確保事務完整性和資料庫一致性。鎖定可以防止使用者讀取正在由其他使用者更改的資料,並可以防止多個使用者同時更改相同資料。如果不使用鎖定,則資料庫中的資料可能在邏輯上不正確,並且對資料的查詢可能會產生意想不到的結果。
雖然 SQL Server 自動強制鎖定,但可以通過了解鎖定並在應用程式中自定義鎖定來設計更有效的應用程式。
========
關於資料庫的併發性的一個解決方案
我先把流程說出來,,比如修改一個記錄:
當用戶點修改時,從資料庫讀出資料並顯示到編輯選單中,然後再編輯資料,再點確定儲存到資料庫中。如果多個使用者,當A使用者點修改到儲存該資料這一時間段,,B使用者不能修改,,這個好像不能用事務來做吧,,,大家給個解決方案,,但是我覺得還有點矛盾。當這一時間段使用者能修改,那麼對於A使用者來說資料就已經不一致了,A使用者看到的是B使用者沒有修改前的資料,如果不能修改,也不現實,,如果B使用者點了修改還沒點確定,,然後使用者出去辦急事或WC,,那A使用者不是一直等,,,
網上經常舉賣車票的例子,當售票員A聽到買票人後開始查詢,發現有2張,,買票人正好要2張,,在查詢和售出這一時間段,如果其它視窗不能賣,,好像不太現實,,有時這一時間段還是很長的,,一分鐘左右吧,,其它10多個視窗視窗不能賣這一車次的不是很要命,並且每個視窗都會差不多佔一車次的查詢,,如果在查詢到確定出售這一價段其它視窗可以賣,,也出現問題了,,售票員A查詢發現剩兩張,,告訴買票的人,正好剩兩張,,這時客戶確定要,如果在這一時間段,其它視窗賣一張,,那在確定賣出去時將會失敗,難道要跟買票人解釋。,,這問題其它也困我很久了,,謝謝大家給我一個解決方案,,也許是我想錯了,,線上等
SQL的鎖機制
一. 為什麼要引入鎖
多個使用者同時對資料庫的併發操作時會帶來以下資料不一致的問題:
丟失更新
A,B兩個使用者讀同一資料並進行修改,其中一個使用者的修改結果破壞了另一個修改的結果,比如訂票系統
髒讀
A使用者修改了資料,隨後B使用者又讀出該資料,但A使用者因為某些原因取消了對資料的修改,資料恢復原值,此時B得到的資料就與資料庫內的資料產生了不一致
不可重複讀
A使用者讀取資料,隨後B使用者讀出該資料並修改,此時A使用者再讀取資料時發現前後兩次的值不一致
併發控制的主要方法是封鎖,鎖就是在一段時間內禁止使用者做某些操作以避免產生資料不一致
二 鎖的分類
鎖的類別有兩種分法:
1. 從資料庫系統的角度來看:分為獨佔鎖(即排它鎖),共享鎖和更新鎖
MS-SQL Server 使用以下資源鎖模式。
鎖模式 描述
共享 (S) 用於不更改或不更新資料的操作(只讀操作),如 SELECT 語句。
更新 (U) 用於可更新的資源中。防止當多個會話在讀取、鎖定以及隨後可能進行的資源更新時發生常見形式的死鎖。
排它 (X) 用於資料修改操作,例如 INSERT、UPDATE 或 DELETE。確保不會同時同一資源進行多重更新。
意向鎖 用於建立鎖的層次結構。意向鎖的型別為:意向共享 (IS)、意向排它 (IX) 以及與意向排它共享 (SIX)。
架構鎖 在執行依賴於表架構的操作時使用。架構鎖的型別為:架構修改 (Sch-M) 和架構穩定性 (Sch-S)。
大容量更新 (BU) 向表中大容量複製資料並指定了 TABLOCK 提示時使用。
共享鎖
共享 (S) 鎖允許併發事務讀取 (SELECT) 一個資源。資源上存在共享 (S) 鎖時,任何其它事務都不能修改資料。一旦已經讀取資料,便立即釋放資源上的共享 (S) 鎖,除非將事務隔離級別設定為可重複讀或更高級別,或者在事務生存週期內用鎖定提示保留共享 (S) 鎖。
更新鎖
更新 (U) 鎖可以防止通常形式的死鎖。一般更新模式由一個事務組成,此事務讀取記錄,獲取資源(頁或行)的共享 (S) 鎖,然後修改行,此操作要求鎖轉換為排它 (X) 鎖。如果兩個事務獲得了資源上的共享模式鎖,然後試圖同時更新資料,則一個事務嘗試將鎖轉換為排它 (X) 鎖。共享模式到排它鎖的轉換必須等待一段時間,因為一個事務的排它鎖與其它事務的共享模式鎖不相容;發生鎖等待。第二個事務試圖獲取排它 (X) 鎖以進行更新。由於兩個事務都要轉換為排它 (X) 鎖,並且每個事務都等待另一個事務釋放共享模式鎖,因此發生死鎖。
若要避免這種潛在的死鎖問題,請使用更新 (U) 鎖。一次只有一個事務可以獲得資源的更新 (U) 鎖。如果事務修改資源,則更新 (U) 鎖轉換為排它 (X) 鎖。否則,鎖轉換為共享鎖。
排它鎖
排它 (X) 鎖可以防止併發事務對資源進行訪問。其它事務不能讀取或修改排它 (X) 鎖鎖定的資料。
意向鎖
意向鎖表示 SQL Server 需要在層次結構中的某些底層資源上獲取共享 (S) 鎖或排它 (X) 鎖。例如,放置在表級的共享意向鎖表示事務打算在表中的頁或行上放置共享 (S) 鎖。在表級設定意向鎖可防止另一個事務隨後在包含那一頁的表上獲取排它 (X) 鎖。意向鎖可以提高效能,因為 SQL Server 僅在表級檢查意向鎖來確定事務是否可以安全地獲取該表上的鎖。而無須檢查表中的每行或每頁上的鎖以確定事務是否可以鎖定整個表。
意向鎖包括意向共享 (IS)、意向排它 (IX) 以及與意向排它共享 (SIX)。
鎖模式 描述
意向共享 (IS) 通過在各資源上放置 S 鎖,表明事務的意向是讀取層次結構中的部分(而不是全部)底層資源。
意向排它 (IX) 通過在各資源上放置 X 鎖,表明事務的意向是修改層次結構中的部分(而不是全部)底層資源。IX 是 IS 的超集。
與意向排它共享 (SIX) 通過在各資源上放置 IX 鎖,表明事務的意向是讀取層次結構中的全部底層資源並修改部分(而不是全部)底層資源。允許頂層資源上的併發 IS 鎖。例如,表的 SIX 鎖在表上放置一個 SIX 鎖(允許併發 IS 鎖),在當前所修改頁上放置 IX 鎖(在已修改行上放置 X 鎖)。雖然每個資源在一段時間內只能有一個 SIX 鎖,以防止其它事務對資源進行更新,但是其它事務可以通過獲取表級的 IS 鎖來讀取層次結構中的底層資源。
獨佔鎖:只允許進行鎖定操作的程式使用,其他任何對他的操作均不會被接受。執行資料更新命令時,SQL Server會自動使用獨佔鎖。當物件上有其他鎖存在時,無法對其加獨佔鎖。
共享鎖:共享鎖鎖定的資源可以被其他使用者讀取,但其他使用者無法修改它,在執行Select時,SQL Server會對物件加共享鎖。
更新鎖:當SQL Server準備更新資料時,它首先對資料物件作更新鎖鎖定,這樣資料將不能被修改,但可以讀取。等到SQL Server確定要進行更新資料操作時,他會自動將更新鎖換為獨佔鎖,當物件上有其他鎖存在時,無法對其加更新鎖。
2. 從程式設計師的角度看:分為樂觀鎖和悲觀鎖。
樂觀鎖:完全依靠資料庫來管理鎖的工作。
悲觀鎖:程式設計師自己管理資料或物件上的鎖處理。
MS-SQLSERVER 使用鎖在多個同時在資料庫內執行修改的使用者間實現悲觀併發控制
三 鎖的粒度
鎖粒度是被封鎖目標的大小,封鎖粒度小則併發性高,但開銷大,封鎖粒度大則併發性低但開銷小
SQL Server支援的鎖粒度可以分為為行、頁、鍵、鍵範圍、索引、表或資料庫獲取鎖
資源 描述
RID 行識別符號。用於單獨鎖定表中的一行。
鍵 索引中的行鎖。用於保護可序列事務中的鍵範圍。
頁 8 千位元組 (KB) 的資料頁或索引頁。
擴充套件盤區 相鄰的八個資料頁或索引頁構成的一組。
表 包括所有資料和索引在內的整個表。
DB 資料庫。
四 鎖定時間的長短
鎖保持的時間長度為保護所請求級別上的資源所需的時間長度。
用於保護讀取操作的共享鎖的保持時間取決於事務隔離級別。採用 READ COMMITTED 的預設事務隔離級別時,只在讀取頁的期間內控制共享鎖。在掃描中,直到在掃描內的下一頁上獲取鎖時才釋放鎖。如果指定 HOLDLOCK 提示或者將事務隔離級別設定為 REPEATABLE READ 或 SERIALIZABLE,則直到事務結束才釋放鎖。
根據為遊標設定的併發選項,遊標可以獲取共享模式的滾動鎖以保護提取。當需要滾動鎖時,直到下一次提取或關閉遊標(以先發生者為準)時才釋放滾動鎖。但是,如果指定 HOLDLOCK,則直到事務結束才釋放滾動鎖。
用於保護更新的排它鎖將直到事務結束才釋放。
如果一個連線試圖獲取一個鎖,而該鎖與另一個連線所控制的鎖衝突,則試圖獲取鎖的連線將一直阻塞到:
將衝突鎖釋放而且連接獲取了所請求的鎖。
連線的超時間隔已到期。預設情況下沒有超時間隔,但是一些應用程式設定超時間隔以防止無限期等待
五 SQL Server 中鎖的自定義
1 處理死鎖和設定死鎖優先順序
死鎖就是多個使用者申請不同封鎖,由於申請者均擁有一部分封鎖權而又等待其他使用者擁有的部分封鎖而引起的無休止的等待
可以使用SET DEADLOCK_PRIORITY控制在發生死鎖情況時會話的反應方式。如果兩個程序都鎖定資料,並且直到其它程序釋放自己的鎖時,每個程序才能釋放自己的鎖,即發生死鎖情況。
2 處理超時和設定鎖超時持續時間。
@@LOCK_TIMEOUT 返回當前會話的當前鎖超時設定,單位為毫秒
SET LOCK_TIMEOUT 設定允許應用程式設定語句等待阻塞資源的最長時間。當語句等待的時間大於 LOCK_TIMEOUT 設定時,系統將自動取消阻塞的語句,並給應用程式返回"已超過了鎖請求超時時段"的 1222 號錯誤資訊
示例
下例將鎖超時期限設定為 1,800 毫秒。
SET LOCK_TIMEOUT 1800
3) 設定事務隔離級別。
參見 http://expert.csdn.net/Expert/topic/1785/1785314.xml?temp=.3050501
4 ) 對 SELECT、INSERT、UPDATE 和 DELETE 語句使用表級鎖定提示。
5) 配置索引的鎖定粒度
可以使用 sp_indexoption 系統儲存過程來設定用於索引的鎖定粒度
六 檢視鎖的資訊
1 執行 EXEC SP_LOCK 報告有關鎖的資訊
2 查詢分析器中按Ctrl+2可以看到鎖的資訊
七 使用注意事項
如何避免死鎖
1 使用事務時,儘量縮短事務的邏輯處理過程,及早提交或回滾事務;
2 設定死鎖超時引數為合理範圍,如:3分鐘-10分種;超過時間,自動放棄本次操作,避免程序懸掛;
3 優化程式,檢查並避免死鎖現象出現;
4 .對所有的指令碼和SP都要仔細測試,在正是版本之前。
5 所有的SP都要有錯誤處理(通過@error)
6 一般不要修改SQL SERVER事務的預設級別。不推薦強行加鎖
解決問題 如何對行 表 資料庫加鎖
八 幾個有關鎖的問題
1 如何鎖一個表的某一行
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT * FROM table ROWLOCK WHERE id = 1
2 鎖定資料庫的一個表
SELECT * FROM table WITH (HOLDLOCK)
加鎖語句:
sybase:
update 表 set col1=col1 where 1=0 ;
MSSQL:
select col1 from 表 (tablockx) where 1=0 ;
oracle:
LOCK TABLE 表 IN EXCLUSIVE MODE ;
加鎖後其它人不可操作,直到加鎖使用者解鎖,用commit或rollback解鎖
幾個例子幫助大家加深印象
設table1(A,B,C)
A B C
a1 b1 c1
a2 b2 c2
a3 b3 c3
1)排它鎖
新建兩個連線
在第一個連線中執行以下語句
begin tran
update table1
set A='aa'
where B='b2'
waitfor delay '00:00:30' --等待30秒
commit tran
在第二個連線中執行以下語句
begin tran
select * from table1
where B='b2'
commit tran
若同時執行上述兩個語句,則select查詢必須等待update執行完畢才能執行即要等待30秒
2)共享鎖
在第一個連線中執行以下語句
begin tran
select * from table1 holdlock -holdlock人為加鎖
where B='b2'
waitfor delay '00:00:30' --等待30秒
commit tran
在第二個連線中執行以下語句
begin tran
select A,C from table1
where B='b2'
update table1
set A='aa'
where B='b2'
commit tran
若同時執行上述兩個語句,則第二個連線中的select查詢可以執行
而update必須等待第一個事務釋放共享鎖轉為排它鎖後才能執行 即要等待30秒
3)死鎖
增設table2(D,E)
D E
d1 e1
d2 e2
在第一個連線中執行以下語句
begin tran
update table1
set A='aa'
where B='b2'
waitfor delay '00:00:30'
update table2
set D='d5'
where E='e1'
commit tran
在第二個連線中執行以下語句
begin tran
update table2
set D='d5'
where E='e1'
waitfor delay '00:00:10'
update table1
set A='aa'
where B='b2'
commit tran
同時執行,系統會檢測出死鎖,並中止程序
老大,你這我也懂,,但你這是技術,,我問的是解決方案,,車票系統到底用的哪種方案
SQL code
樓主參考下這個...悲觀鎖
悲觀鎖定解決方案
我們只要對上邊的程式碼做微小的改變就可以實現悲觀的鎖定.
declare @CardNo varchar(20)
Begin Tran
-- 選擇一張未使用的卡
select top 1 @CardNo=F_CardNo
from Card with (UPDLOCK) where F_Flag=0
-- 延遲50秒,模擬併發訪問.
waitfor delay '000:00:50'
-- 把剛才選擇出來的卡進行註冊.
update Card
set F_Name=user,
F_Time=getdate(),
F_Flag=1
where [email protected]
commit
注意其中的區別了嗎?with(updlock),是的,我們在查詢的時候使用了with (UPDLOCK)選項,在查詢記錄的時候我們就對記錄加上了更新鎖,表示我們即將對次記錄進行更新.注意更新鎖和共享鎖是不衝突的,也就是其他使用者還可以查詢此表的內容,但是和更新鎖和排它鎖是衝突的.所以其他的更新使用者就會阻塞.如果我們在另外一個視窗執行此程式碼,同樣不加waifor delay子句.兩邊執行完畢後,我們發現成功的註冊了兩張卡.可能我們已經發現了悲觀鎖定的缺點:當一個使用者進行更新的事務的時候,其他更新使用者必須排隊等待,即使那個使用者更新的不是同一條記錄.
到底什麼時候開始其它使用者不能操作,A使用者點選修改時還是使用者點確定重新去讀一次資料庫時,
SQL code
--這裡還有一個~ 之前儲存的 - -出處在哪沒有記錄...很不好意思 作者請罵我吧...
樂觀鎖定解決方案
-- 首先我們在Card表裡邊加上一列F_TimeStamp 列,該列是varbinary(8)型別.但是在更新的時候這個值會自動增長.
alter table Card add F_TimeStamp timestamp not null
-- 悲觀鎖定
declare @CardNo varchar(20)
declare @timestamp varbinary(8)
declare @rowcount int
Begin Tran
-- 取得卡號和原始的時間戳值
select top 1 @CardNo=F_CardNo,
@timestamp=F_TimeStamp
from Card
where F_Flag=0
-- 延遲50秒,模擬併發訪問.
waitfor delay '000:00:50'
-- 註冊卡,但是要比較時間戳是否發生了變化.如果沒有發生變化.更新成功.如果發生變化,更新失敗.
update Card
set F_Name=user,
F_Time=getdate(),
F_Flag=1
where [email protected] and [email protected]
set @[email protected]@rowcount
if @rowcount=1
begin
print '更新成功!'
commit
end
else if @rowcount=0
begin
if exists(select 1 from Card where [email protected])
begin
print '此卡已經被另外一個使用者註冊!'
rollback tran
end
else
begin
print '並不存在此卡!'
rollback tran
end
end
在另外一個窗口裡邊執行沒有waitfor的程式碼,註冊成功後,返回原來的視窗,我們就會發現到時間後它顯示的提示是此卡以被另外一個使用者註冊的提示.很明顯,這樣我們也可以避免兩個使用者同時註冊一張卡的現象的出現.同時,使用這種方法的另外一個好處是沒有使用更新鎖,這樣增加的系統的併發處理能力.
加個rowversion欄位,每當資料被修改時,此欄位會被UPDATE,
儲存時,判斷記錄是否已經被修改。
看來LZ沒有做過實際案例啊,現在我給你透露下實際售票系統中的一個方案例子:
首先沒有那麼複雜的鎖,實際應用會盡量從業務角度考慮避免衝突:
實際售票系統是這樣:
1.售票中,"座位號" 才是競爭資源;
2.售票中,檢視票是不發生鎖號的.
3.售票中,有個選票(選座位號)的動作,選座位號確定時,才發生鎖號(即鎖住改作為號,即使這鎖號,也只是修改標記,表示自己暫時鎖住);
4.等客戶交錢後,就確定提交交易完成,這時候,就成為售出該票了(當然,被鎖的號,要修改為對應的已售標記,及其他流程操作).
從這個過程看,幾乎沒有那麼多衝突出現(只有選號時,有可能已被別人選了,這也應該知道的,可以另選號),這就是方案.
樂觀鎖定解決方案
-- 首先我們在Card表裡邊加上一列F_TimeStamp 列,該列是varbinary(8)型別.但是在更新的時候這個值會自動增長.
alter table Card add F_TimeStamp timestamp not null
-- 悲觀鎖定
declare @CardNo varchar(20)
declare @timestamp varbinary(8)
declare @rowcount int
Begin Tran
--…
你這是當點確定後再去判斷是否卡否已被註冊了嘛,,我想知道車票出售系統或大型網路版的管理系統的解決方案
利用小粒度鎖可以儘量避免衝突,分析好你的業務,沒多少問題。如7樓所言,一般的情況下,鎖使用並
看來LZ沒有做過實際案例啊,現在我給你透露下實際售票系統中的一個方案例子:
首先沒有那麼複雜的鎖,實際應用會盡量從業務角度考慮避免衝突:
實際售票系統是這樣:
1.售票中,"座位號" 才是競爭資源;
2.售票中,檢視票是不發生鎖號的.
3.售票中,有個選票(選座位號)的動作,選座位號確定時,才發生鎖號(即鎖住改作為號,即使這鎖號,也只是修改標記,表示自己暫時鎖住);
4.等客戶交錢後,就確定提交交易完成,這時候,就成為售出該…
同意,就是這個意思
看來LZ沒有做過實際案例啊,現在我給你透露下實際售票系統中的一個方案例子:
首先沒有那麼複雜的鎖,實際應用會盡量從業務角度考慮避免衝突:
實際售票系統是這樣:
1.售票中,"座位號" 才是競爭資源;