問題引入
本文介紹的是最常用的也是mysql默認的innoDB引擎 Read committed隔離級別下事物的並發。這種情況下的事物特點是
讀:在一個事物里面的select語句 不會受到其他事物(不管其他事物有沒有commit)的影響。
寫:對一條記錄而言,一個事物一旦update一條記錄,其他事物只能等待這個事物commit才能update那條記錄
舉例並發分析:
比如表中num字段=10 需要根據num字段的數值做一些業務處理
事物A begin | 事物B begin |
select num 得到10 | |
根據結果10進行業務處理; | |
處理完之后 num減一 update table set num = num-1; | |
A 事物提交 commit; 此時num是9 | 此時A已經commit了 |
select num 得到10 (此時的查詢不受其它事物的影響) | |
根據結果10進行業務處理; | |
處理完之后 num減一 update table set num = num-1; | |
會把num更新成8。 | |
結論: B以num是10的基礎上 處理了眾多邏輯,但是到update的時候 用到的b確是9 |
表格中 update table set num = num-1;會受到其他commit事物的影響(B事物把num從9更新成了8) 而select不會(查到的還是10)。
1利用樂觀鎖處理事物的並發
數據庫增加一個鎖的處理列(版本號),查詢的時候多查一個版本號, update的時候 where條件附加一個版本號條件並且更新時候並且把版本號+1,處理流程
1)select num,version from table;
2)update table set num=num-1 ,version =version+1 where condition=? version=#{version}
3)Query OK, 1 rows affected (0.04 sec) | 注意:1 rows affected
update的時候 where條件附加一個版本號條件並且更新時候並且把版本號+1: update table set num=num-1 ,version =version+1 where condition=? version=#{version}
如果出現了查詢時候的版本號和where條件的版本號不一致 說明其他事物並發影響到了版本號 version,此時update語句的影響行數(Query OK, 1 rows affected (0.04 sec))是0 然后做異常處理。()
2把計算放到SQL中
當我們把計算邏輯(比如錢的增量)寫在SQL里面的時候 可以不用樂觀鎖,因為SQL增量時會讀到其他已經提交的事物。
注意:一條記錄,一個事物update還未提交的時候,其他時候只能等待這個事物commit完成才能update
樂觀鎖處理並發的原理 重點
當事物的隔離級別是REPEATABLE_READ情況下:當前事物不能select到並發事物中已經提交的事物。
當事物的隔離級別是READ COMMITTED情況下:當前事物能select到並發事物中已經提交的事物。
1)一個事物中的select語句可能不會收到其他事物的影響,也就是可能查不到其他並發中未提交的事物(即使提交了 如果是REPEATABLE_READ也查不到)。所以兩個事物共同執行時產生了並發的沖突。
2)雖然這個被並發的字段通過select查不出來,但是在where條件語句中 這個字段會受到其他事物的影響 。所以可以利用這點 可以讀到並發事物影響的數據 ,從而做出判斷,防止並發。
補充:共享鎖與排他鎖
--共享鎖:對某一資源加共享鎖,自身可以讀該資源,其他人也可以讀該資源
(也可以再繼續加共享鎖,即 共享鎖可多個共存),但無法修改。要想修改就必須等所有共享鎖都釋放完之后。語法為:
select * from table lock in share mode
--排他鎖:對某一資源加排他鎖,自身可以進行增刪改查,其他人無法進行任何操作。語法為:
select * from table for update (A select for update的時候 B可以select 但不能select for update 排它鎖只有一把)
數據庫的增刪改操作默認都會加排他鎖,而查詢不會加任何鎖。