1. 程式人生 > >Sybase中的鎖及死鎖問題

Sybase中的鎖及死鎖問題

   一般來說,資料庫都會有兩種鎖:記憶體鎖和物件鎖。Oracle中有latch和lock,sybase中有spinlock和lock。記憶體鎖實際上就是資料庫系統將自己管理的記憶體區按單元加鎖,以防止一個任務在使用時被另一個任務修改。用完這個記憶體單元后,記憶體鎖被立即釋放。不過這篇文章只會論及sybase資料庫的物件鎖lock。

1鎖型別:

在sybase中的lock總共有10個型別:

1-      排他表鎖

2-      共享表鎖

3-      排他意圖鎖

4-      共享意圖鎖

5-      排他頁鎖

6-      共享頁鎖

7-      更新頁鎖

8-      排它行鎖

9-      共享行鎖

10-   更新行鎖

這十類鎖,從作用上來說,其實就是排他、共享和更新。其他的限定詞無非說明鎖的作用範圍和作用時機。後面會詳細說明,這裡先說一下排他、共享和更新之間的相容性問題。如下表所示:

共享鎖

更新鎖

排他鎖

共享鎖

相容

相容

不相容

更新鎖

相容

不相容

不相容

排他鎖

不相容

不相容

不相容

簡單說明一下這張表:

比如共享鎖和更新鎖相容,就是說加了共享鎖,還可以加更新鎖;

排他鎖和更新鎖不相容,就是說加了排他鎖,就不可以再加更新鎖。

這裡可以看到,加了排他鎖後,就不能再加任何鎖了。

2鎖方案:

鎖方案有三類:Allpages、Datapages、Datarows。

       Allpages:全頁鎖,這裡全頁的意思是包括索引頁和資料頁。

       Datapages:資料頁鎖,設定這種鎖方案後,只會對資料頁加鎖。

       Datarows:資料行鎖,設定這種鎖方案後,只會對資料行加鎖。

       這裡可以看到,Datapages和Datarows只會對資料加鎖,不會對資料相對應的索引加鎖。    

有了鎖方案的概念,就好理解排他表鎖、排他頁鎖、排他行鎖的含義了。

比如:鎖方案為Datarows,則會對錶加XX行鎖(XX可以是排他、共享或者更新);

鎖方案為Allpages或Datapages,則會對錶加XX頁鎖(XX可以是排他、共享或者更新)。

3意圖鎖和更新鎖:

這裡將意圖鎖和更新鎖單獨列出來,原因是這兩種鎖只在某一個時機下才有,過了那個時機就沒有了。

意圖鎖:它是一種表級鎖,表示在一個數據頁上獲得一個共享鎖或排他鎖的意圖。意圖鎖可以防止其他事務在該資料頁的表上獲得排它鎖。它分為排他意圖鎖和共享意圖鎖。

更新鎖:它是一種頁級鎖,它在一個更新操作開始時獲得,當要修改這些頁時,更新鎖會升級為排它鎖。

4各種操作的加鎖過程:

其實上面說的那麼多,主要就是為了說明最開始列舉的10個鎖型別。下面就談談這10個種類型的鎖在實際操作中是如何加鎖的。

4.1 假定鎖方案為Allpages:

4.1.1 insert操作:

       當向表中執行Insert操作時,會先在表上加一個排他意圖鎖,然後加上排他頁鎖。可以用下面的示意圖表示。

排他意圖鎖->排他頁鎖

4.1.2 select操作:

       Select操作時,會先在表上加一個共享意圖鎖,然後加上共享頁鎖。示意圖如下:

共享意圖鎖->共享頁鎖

4.1.3 update操作:

       Update操作時,加鎖過程稍微複雜一點,首先會在表上加一個共享意圖鎖,然後加上共享頁鎖,接著會加更新頁鎖,最後將更新頁鎖升級為排他頁鎖。

       之所以有這麼鎖,實際上和update的機制有關。因為update操作在資料庫中分兩步執行:先select,然後update。示意圖如下:

共享意圖鎖->共享頁鎖->更新頁鎖->排他頁鎖

4.1.4 delete操作:

       Delete操作時,首先會在表上加一個共享意圖鎖,然後加上共享頁鎖,最後加上排他頁鎖。

       和update操作相比,少了一個更新頁鎖,這個鎖只在update過程中存在。示意圖如下:

共享意圖鎖->共享頁鎖->排他頁鎖

4.2 假定鎖方案為Datapages:

加鎖方式和allpages類似,只是allpages會對需要的索引頁加鎖,

而datapages則不會對索引頁加鎖,所以叫“資料頁鎖”。

4.3 假定鎖方案為Datarows:

4.3.1 insert操作:

       當向表中執行Insert操作時,會先在表上加一個排他意圖鎖,然後加上排他行鎖。可以用下面的示意圖表示。

排他意圖鎖->排他行鎖

4.3.2 select操作:

       Select操作時,會先在表上加一個共享意圖鎖,然後加上共享行鎖。示意圖如下:

共享意圖鎖->共享行鎖

4.3.3 update操作:

       Update操作時,加鎖過程稍微複雜一點,首先會在表上加一個共享意圖鎖,然後加上共享行鎖,接著會加更新行鎖,最後將更新行鎖升級為排他行鎖。示意圖如下:

共享意圖鎖->共享行鎖->更新行鎖->排他行鎖

4.3.4 delete操作:

       Delete操作時,首先會在表上加一個共享意圖鎖,然後加上共享行鎖,最後加上排他行鎖。示意圖如下:

共享意圖鎖->共享行鎖->排他行鎖

5鎖升級:

       在上面的加鎖過程中,有兩類鎖沒有用到,排他表鎖和共享表鎖。這兩類鎖什麼時候會用到呢?在兩種情況下用到。

5.1 影響所有記錄數:

       在執行update或delete語句時,如果影響的是整張表的記錄(即不帶where條件的執行語句),則會產生排他表鎖。比如:

       Deletefrom Tablename

       UpdateTablename set Columnname = value

5.2 鎖升級:

       鎖升級的概念是指當細粒度的鎖達到一定數量時,就會升級為粗粒度的鎖。

5.2.1 行鎖向表鎖升級:

       當一個表上的行鎖達到一定數量時,就會將行鎖升級為表鎖,原來的行鎖全部釋放。

Sybase ASE有三個引數作為行鎖升級到表鎖的閾值,行鎖超過這個數,就會升級為表鎖。根據lockschema 如果設定的是Datarows則,設定如下三個引數:

row lock promotion HWM

row lock promotion LWM

row lock promotion PCT

       其中,HWM為最大值,LWM為最小值,PCT是一個可調的百分比。

       SybaseASE 根據PCT值按公式PCT*TAB_SZ/100得出計算閥限V ,如果V < LWM, 鎖升級發生在LWM值;如果V > HWM,鎖升級發生在HWM值。如果 LWM < V

< HWM ,鎖升級發生在PCT*TAB_SZ/100值。

其中,TAB_SZ即是number ofrows in table , 表示表的大小,以行數表示。

5.2.2 頁鎖向表鎖升級:

       當一個表上的頁鎖達到一定數量時,就會將頁鎖升級為表鎖,原來的頁鎖全部釋放。

       SybaseASE有三個引數作為頁鎖升級到表鎖的閾值,頁鎖超過這個數,就會升級為表鎖。根據lock schema 如果設定的是Datapages則,設定如下三個引數:

page lock promotion HWM

page lock promotion LWM

page lock promotion PCT

       其中,HWM為最大值,LWM為最小值,PCT是一個可調的百分比。

Sybase ASE 根據PCT值按公式PCT*TAB_SZ/100得出計算閥限V ,如果V < LWM,鎖升級發生在LWM值;如果V > HWM,鎖升級發生在HWM值。如果 LWM < V

< HWM ,鎖升級發生在PCT*TAB_SZ/100值。

其中,TAB_SZ即是number of pagesin table , 表示表的大小,以頁數表示。

6死鎖:

       說了那麼多,主要就是要對資料庫中的鎖機制有一定了解,因為資料庫的效能問題在很大一部分是因為鎖造成的。比如:死鎖和鎖阻塞。

鎖阻塞會讓等待該鎖的所有事務處於等待狀態,直到得到鎖為止。它對效能的影響很大。

死鎖發生時,系統會自動釋放鎖,並回滾持有鎖的事務。看起來對系統的效能沒有鎖阻塞大,但是邏輯問題往往造成不斷的死鎖發生,這對效能的影響也是很大的。

前面可以看到,鎖的粒度有表級、頁級和行級,同樣,死鎖的發生也可能在表級、頁級和行級;死鎖可以發生在兩個表之間,也可能發生在一個表內部。

6.1 兩個表之間的死鎖:

       首先,新建一個會話,宣告一個事務開始,對錶1進行update操作。如下圖:

再建立一個會話,然後也宣告一個事務開始,對錶2進行update操作。如下圖:

然後,在會話1中,對錶2進行update操作。如下圖:

在會話2中,再對錶1執行update操作。如下圖:

最後,回頭看看會話1,報死鎖錯誤了。

6.2 一個表內部的死鎖:

首先,新建一個會話,宣告一個事務開始,對錶1的第一條記錄進行update操作。如下圖:

再建立一個會話,然後也宣告一個事務開始,對錶1的第二條記錄進行update操作。如下圖:

然後,在會話1中,對錶1的第二條記錄進行update操作。如下圖:

在會話2中,再對錶1的第一條記錄執行update操作。如下圖:

最後,回頭看看會話1,報死鎖錯誤了。

6.3 一個儲存過程引起的死鎖:

       儲存過程的內容如下:

CREATE PROCEDURE S_update_exception

      @device_idchar(60),

@time datetime,

@type int,

@state int      

AS    

BEGIN    

              updatedevice_exception set [email protected],[email protected]     

              [email protected]_id and

[email protected]  and exception_state=1    

              insertinto device_exception_history    

                     selectdevice_id, occur_time, clear_time,exception_type,

exception_level, confirm_time,confirm_user,@state,0

                         fromdevice_exception where exception_state=1    

              deletefrom device_exception where exception_state=0

              updateencoder set [email protected] where [email protected]_id       

END

       再看看系統記錄的當時的死鎖告警日誌。如下:

       00:00000:00031:2007/08/10 21:48:49.70server  Deadlock Id 1 detected

DeadlockId 1: detected. 1 deadlock chain(s) involved.

DeadlockId 1: Process (Familyid 0, Spid 23, Suid 1) was executing a DELETE command inthe procedure 'S_update_exception'.

SQL Text:S_update_exception '160201111000013','2007-8-10 21:47:44',100,0

DeadlockId 1: Process (Familyid 0, Spid 31, Suid 1) was executing a UPDATE command inthe procedure 'S_update_exception'.

SQL Text:S_update_exception '160201111000015','2007-8-10 21:47:44',100,0

DeadlockId 1: Process (Familyid 0, Spid 31) was waiting for a 'update page' lock onpage 1024021 of the 'device_exception' table in database 4 but process(Familyid 0, Spid 23) already held a 'update page' lock on it.

DeadlockId 1: Process (Familyid 0, Spid 23) was waiting for a 'exclusive page' lock onpage 1280264 of th e 'device_exception' table in database 4 but process(Familyid 0, Spid 31) already held a 'update page' lock on it.

DeadlockId 1: Process (Familyid 0, Spid 23) was chosen as the victim. End of deadlockinformation.

       通過這個日誌,可以看出,程序23對錶device_exception進行DELETE操作時,程序31正在對錶device_exception進行UPDATE操作,產生死鎖,死鎖產生在1024021號頁和1280264號頁上。

       在分析之前,我們先看一下device_exception表的定義。如下:

CREATETABLE dbo.device_exception

(

    device_id       char(50)    NOT NULL,

    occur_time      datetime    NULL,

    clear_time      datetime    NULL,

    exception_type  int        NULL,

    exception_level int         NULL,

    confirm_time    datetime   NULL,

    confirm_user    varchar(50) NULL,

    exception_state int         NULL

)

通過上面的日誌看到一個update操作,涉及到兩個頁,但是這個表一行記錄的總資料量只有144個位元組,不可能超過一頁(一頁的預設大小2k),為什麼是兩頁呢?並且分別持有了1024021頁和1280264頁的更新鎖。

會不會一個spid中同時執行了兩個儲存過程呢?答案是否定的。在聯絡後面出現的死鎖,都集中在這兩個頁上,而表device_exception的資料量本身不大,一個數據頁應該夠了。所以可能的情況是其中有一頁是索引頁。因為結合前面的鎖方案,Allpages情況下,是會對索引頁和資料頁同時加鎖的。

下面便是這個死鎖的示意圖:

       通過這個死鎖案例可以看出,在一個併發性很高的表上,鎖方案不能使用Allpages。