下面介紹的是鎖的並發問題和解決方法
在實際情況中
通常會因為lock發生四中情況
下面用個定座系統舉例
所有的語句都工作在默認隔離級別下
丟失的更新
user
run
begin tran
select 空座位 得到
update
定座位
commit
如果user
在user
update之前運行同樣的程序
都會得到空座位是
的信息
當然user
也可以定座位
結果就是user
的定座是最終有效的
user
的定座被覆蓋
為了避免這種情況
看看用隔離級別能不能解決
這四種隔離級別最多在表上施加S
由於S是兼容的
因此user
照樣能得到空座位是
的信息
丟失的更新也照樣會發生
SQL提供了表級鎖定選項TABLOCKX
能避免這種情況的發生
但喪失了並發
髒讀
user
run
begin tran
update row A
commit
如果user
能在user
update的同時讀出表中的數據(包含 row A) 那麼就產生了髒讀
因為user
可能commit也可能rollback
update並沒有被確認
默認情況下是不會產生髒讀的
因為update將產生行的X
表的IX
而表IX和S是不兼容的
那麼在什麼情況下我們需要髒讀呢
如果我們不需要確切的數據
我們需要的是一個大概的趨勢
個別數據的不確定沒有關系
想想一個股票系統
同一時刻的改寫會很多
也就是表上會有許多IX鎖
如果我們需要查詢表獲得大致情況
那麼只有等改寫結束
為了提高並發
此時我們需要髒讀
解決方法就是在user
的會話環境中設置事務隔離級別為read uncommitted
不可重復讀
user
run
begin tran
select tab
where
select tab
where
commit
當user
在交易中執行兩次select
所得到的結果不一樣
這便是不可重復讀
發生的原因是user
在user
第二次select之前對tab
做了修改
select語句雖然對表有讀鎖
但在默認情況下
讀鎖在select語句執行完就被釋放
而不是保持到交易結束
為了避免不可重復讀的發生
只要select產生的鎖保持到交易結束就可以了
將user
的會話環境中設置事務隔離級別為repeatable read
幻影
user
run
begin tran
select tab
where
select tab
where
commit
雖然將user
的會話環境中設置事務隔離級別為repeatable read
但當user
在交易中執行兩次select
所得到的結果還會不一樣
這便是幻影
因為user
僅僅將select出來的行加了S鎖
但user
可以insert滿足where條件的新行
使得兩次select的結果不一樣
解決方法是將user
的會話環境中設置事務隔離級別為serializable
或者在表級鎖定選項中選holdlock
鎖定超時
前面說過
當某個語句因為鎖而不能立即執行時
會等待
直到鎖被釋放
但如果持有鎖的語句執行時間過長(未優化)
那麼就會等待過長
影響響應
因此
我們通過lock_timeout來將等待過長的語句rollback
需要注意的是
當數據量增加時
可能發生不希望的lock_timeout
原因是SQL需要更多的時間來處理數據
為此我們也需要適當的增加lock_timeout
索引在鎖中的應用
大家都知道索引可以提高查詢速度
其實索引還有一個很重要的功能就是提高並發
例
當user
run update tab
set col
=xxx時
user
對tab
run select * from tab
將會等待
直到user
執行完
但如果user
執行select * from tab
where col
<>xxx也就是user
要查詢的行並不是user
改寫的行
結果如何呢?同樣
user
也要等到user
執行完
這樣就沒有什麼並發可言
如果我們在col
上建立index
就可以讓user
無須等待
立即執行
更常用的是父表和子表的情況
通常為了保證引用完整性
SQL在修改數據時
都會將依賴表加鎖
但如果有index
情況就好的多
From:http://tw.wingwit.com/Article/program/SQLServer/201311/22171.html