【CAP】數據庫的競態問題

有并發就有競態,就有鎖的問題。

最基本的是數據庫的競態問題。

一、臟讀、不可重復讀、幻讀

1、臟讀:臟讀就是指當一個事務正在訪問數據,并且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時,另外一個事務也訪問這個數據,然后使用了這個數據。

例如:

張三的工資為5000,事務A中把他的工資改為8000,但事務A尚未提交。

與此同時,事務B正在讀取張三的工資,讀取到張三的工資為8000。

隨后,事務A發生異常,而回滾了事務。張三的工資又回滾為5000。

最后,事務B讀取到的張三工資為8000的數據即為臟數據,事務B做了一次臟讀。

2、不可重復讀:是指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。那么,在第一個事務中的兩次讀數據之間,由于第二個事務的修改,那么第一個事務兩次讀到的的數據可能是不一樣的。這樣就發生了在一個事務內兩次讀到的數據是不一樣的,因此稱為是不可重復讀。

例如:

在事務A中,讀取到張三的工資為5000,操作沒有完成,事務還沒提交。

與此同時,事務B把張三的工資改為8000,并提交了事務。

隨后,在事務A中,再次讀取張三的工資,此時工資變為8000。在一個事務中前后兩次讀取的結果并不致,導致了不可重復讀。

3、幻讀:是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那么,以后就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。

例如:

目前工資為5000的員工有10人,事務A讀取所有工資為5000的人數為10人。

此時,事務B插入一條工資也為5000的記錄。

這是,事務A再次讀取工資為5000的員工,記錄為11人。此時產生了幻讀。

4、提醒

不可重復讀的重點是修改:

同樣的條件,你讀取過的數據,再次讀取出來發現值不一樣了

幻讀的重點在于新增或者刪除:

同樣的條件,第 1 次和第 2 次讀出來的記錄數不一樣

二、獨占鎖、共享鎖、更新鎖,樂觀鎖、悲觀鎖

1、鎖的兩種分類方式

###(1)從數據庫系統的角度來看,鎖分為以下三種類型:

獨占鎖(Exclusive Lock)

獨占鎖鎖定的資源只允許進行鎖定操作的程序使用,其它任何對它的操作均不會被接受。執行數據更新命令,即INSERT、 UPDATE 或DELETE 命令時,數據庫會自動使用獨占鎖。但當對象上有其它鎖存在時,無法對其加獨占鎖。獨占鎖一直到事務結束才能被釋放。

共享鎖(Shared Lock)

共享鎖鎖定的資源可以被其它用戶讀取,但其它用戶不能修改它。在SELECT 命令執行時,數據庫通常會對對象進行共享鎖鎖定。通常加共享鎖的數據頁被讀取完畢后,共享鎖就會立即被釋放。

更新鎖(Update Lock)

更新鎖是為了防止死鎖而設立的。當數據庫準備更新數據時,它首先對數據對象作更新鎖鎖定,這樣數據將不能被修改,但可以讀取。等到數據庫確定要進行更新數據操作時,它會自動將更新鎖換為獨占鎖。但當對象上有其它鎖存在時,無法對其作更新鎖鎖定。

###(2)從程序員的角度看,鎖分為以下兩種類型:

悲觀鎖(Pessimistic Lock)

悲觀鎖,正如其名,它指的是對數據被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此在整個數據處理過程中,將數據處于鎖定狀態。悲觀鎖的實現,往往依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據)。

樂觀鎖(Optimistic Lock)

相對悲觀鎖而言,樂觀鎖機制采取了更加寬松的加鎖機制。悲觀鎖大多數情況下依靠數據庫的鎖機制實現,以保證操作最大程度的獨占性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。

而樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基于數據版本( Version )記錄機制實現。何謂數據版本?即為數據增加一個版本標識,在基于數據庫表的版本解決方案中,一般是通過為數據庫表增加一個 “version” 字段來實現。讀取出數據時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大于數據庫表當前版本號,則予以更新,否則認為是過期數據。

三、事務五種隔離級別

在競態數據對不同事務的可見性。

Isolation 屬性一共支持五種事務設置,具體介紹如下:

(1)DEFAULT

使用數據庫設置的隔離級別(默認),由DBA 默認的設置來決定隔離級別。

(2)READ_UNCOMMITTED

這是事務最低的隔離級別,它充許別外一個事務可以看到這個事務未提交的數據。

會出現臟讀、不可重復讀、幻讀 (隔離級別最低,并發性能高)。

(3)READ_COMMITTED

保證一個事務修改的數據提交后才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據。

可以避免臟讀,但會出現不可重復讀、幻讀問題(鎖定正在讀取的行)。

(4)REPEATABLE_READ

可以防止臟讀、不可重復讀,但會出幻讀(鎖定所讀取的所有行)。

(5)SERIALIZABLE

這是花費最高代價但是最可靠的事務隔離級別,事務被處理為順序執行。

保證所有的情況不會發生(鎖表)。

MYSQL悲觀鎖的使用

要使用悲觀鎖,我們必須關閉mysql數據庫的自動提交屬性。

1
2
3
4
5
6
7
8
9
10
11
12
set autocommit=0;  
//設置完autocommit后,我們就可以執行我們的正常業務了。具體如下:
//0.開始事務
begin;/begin work;/start transaction; (三者選一就可以)
//1.查詢出商品信息
select status from t_goods where id=1 for update;
//2.根據商品信息生成訂單
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status為2
update t_goods set status=2;
//4.提交事務
commit;/commit work;

上面的第一步我們執行了一次查詢操作:select status from t_goods where id=1 for update;與普通查詢不一樣的是,我們使用了select…for update的方式,這樣就通過數據庫實現了悲觀鎖。此時在t_goods表中,id為1的那條數據就被我們鎖定了,其它的事務必須等本次事務提交之后才能執行。這樣我們可以保證當前的數據不會被其它事務修改。

使用select…for update會把數據給鎖住,不過我們需要注意一些鎖的級別,MySQL InnoDB默認Row-Level Lock,所以只有「明確」地指定主鍵,MySQL 才會執行Row lock (只鎖住被選取的數據) ,否則MySQL 將會執行Table Lock (將整個數據表單給鎖住)。

体彩25选5走势图