Mysql加鎖過程詳解(3)-select for update/lock in share mode 對事務併發性影響

事務併發性理解

事務併發性,粗略的理解就是單位時間內可以執行的事務數量,常見的單位是 TPS( transactions per second).mysql

那在數據量和業務操做量必定的狀況下,常見的提升事務併發性主要考慮的有哪幾點呢?程序員

1.提升服務器的處理能力,讓事務的處理時間變短。sql

這樣不只加快了這個事務的執行時間,也下降了其餘等待該事務執行的事務執行時間。數據庫

2.儘可能將事務涉及到的 sql 操做語句控制在合理範圍,換句話說就是不要讓一個事務包含的操做太多或者太少。服務器

在業務繁忙狀況下,若是單個事務操做的表或者行數據太多,其餘的事務可能都在等待該事務 commit或者 rollback,這樣會致使總體上的 TPS 下降。可是,若是每一個 sql 語句都是一個事務也是不太現實的。一來,有些業務自己須要多個sql語句來構成一個事務(好比匯款這種多個表的操做);二來,每一個 sql 都須要commit,若是在 mysql 裏 innodb_flush_log_at_trx_commit=1 的狀況下,會致使 redo log 的刷新過於頻繁,也不利於總體事務數量的提升(IO限制也是須要考慮的重要因素)。併發

3.在操做的時候,儘可能控制鎖的粒度,能用小的鎖粒度就儘可能用鎖的粒度,用完鎖資源後要記得當即釋放,避免後面的事務等待。post

可是有些狀況下,因爲業務須要,或者爲了保證數據的一致性的時候,必需要增長鎖的粒度,這個時候就是下面所說的幾種狀況。性能

 

select for update 理解

select col from t where where_clause for update 的目的是在執行這個 select 查詢語句的時候,會將對應的索引訪問條目進行上排他鎖(X 鎖),也就是說這個語句對應的鎖就至關於update帶來的效果。spa

那這種語法爲何會存在呢?確定是有須要這種方式的存在啦!!請看下面的案例描述:code

 

案例1 

設置事務隔離級別爲RR,並關閉自動提交。

在 t1 中,咱們採用普通讀:

mysql> select * from amount ;
+----+-------+
| id | money |
+----+-------+
| A  |   100 |
| B  |   600 |
| C  |  1000 |
| D  |  1000 |
+----+-------+
4 rows in set (0.00 sec)

 

而後在 t2 中,咱們更新一條數據:

mysql> update amount set money = 200 where id = 'B';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

  mysql> commit;
  Query OK, 0 rows affected (0.03 sec)

 

此時,因爲是RR隔離級別,t1 是察覺不了 t2 的更新狀況的,可是,此時id=B的money應爲200:

mysql> select * from amount ;
+----+-------+
| id | money |
+----+-------+
| A  |   100 |
| B  |   600 |
| C  |  1000 |
| D  |  1000 |
+----+-------+
4 rows in set (0.00 sec)

 

這時,咱們在 t1 中,將 id=B 的記錄加200,咱們發現,這時B的money變成了400,而不是800:

mysql> update amount set money = money + 200 where id = 'B';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from amount ;
+----+-------+
| id | money |
+----+-------+
| A  |   100 |
| B  |   400 |
| C  |  1000 |
| D  |  1000 |
+----+-------+
4 rows in set (0.00 sec)

 

結果是:咱們在 t1 中明明發現B的money是600,加200應該是800,可是實際上確實200+200!。

這在有些業務狀況下是不容許的,由於有些業務但願我經過 select * from lockt; 查詢到的數據是此時數據庫裏面真正存儲的最新數據,而且不容許其餘的事務來修改只容許我來修改。

 

案例2:

在 t1 中,

mysql> select * from amount where id = 'B' for update;
+----+-------+
| id | money |
+----+-------+
| B  |   400 |
+----+-------+

在 t2 中,更新B的數據時,發現被阻塞了。

由於事務1 的 select * from lockt where id='B' for update; 語句會將 id='B' 這個索引的入口給鎖住了,(其實有些時候是範圍的索引條目也被鎖住了,暫時不討論。),那麼事務2雖然看到了全部的數據,可是想去修改 id='B' 的行數據的時候, 事務1 只能說 「不可能也不容許」。

後面只有事務1 commit或者rollback 之後,事務2 的纔可以修改  id='B' 的這個行數據。

總結:

這就是 select for update 的使用場景,爲了不本身看到的數據並非數據庫存儲的最新數據而且看到的數據只能由本身修改,須要用 for update 來限制。

 

select lock in share mode 理解

若是看了前面的 select *** for update ,就能夠很好的理解 select lock in share mode ,in share mode 子句的做用就是將查找到的數據加上一個 share 鎖,這個就是表示其餘的事務只能對這些數據進行簡單的select 操做,並不可以進行 DML 操做

那它和 for update 在引用場景上究竟有什麼實質上的區別呢?

lock in share mode 沒有 for update 那麼霸道,因此它有時候也會遇到問題,請看案例3

案例3:

t1,查詢全部數據,並在 id = 'B' 記錄加了 share 鎖:

mysql> select * from amount;
+----+-------+
| id | money |
+----+-------+
| A  |   100 |
| B  |   400 |
| C  |  1000 |
| D  |  1000 |
+----+-------+
4 rows in set (0.00 sec)

mysql> select * from amount where id = 'B' lock in share mode;
+----+-------+
| id | money |
+----+-------+
| B  |   400 |
+----+-------+
1 row in set (0.00 sec)

 

t2,一樣的操做:

mysql> select * from amount;
+----+-------+
| id | money |
+----+-------+
| A  |   100 |
| B  |   400 |
| C  |  1000 |
| D  |  1000 |
+----+-------+
4 rows in set (0.00 sec)

mysql> select * from amount where id = 'B' lock in share mode;
+----+-------+
| id | money |
+----+-------+
| B  |   400 |
+----+-------+
1 row in set (0.00 sec)

 

此時,t1 更新 id = 'B' 的記錄,發現被阻塞了:

解釋:由於事務1 和事務2 都對該行上了一個 share 鎖,事務1 覺得就只有本身一我的上了 S 鎖,因此當事務一想修改的時候發現無法修改,這種狀況下,事務1 須要使用 for update 子句來進行約束了,而不是使用 for share 來使用。

lock in share mode 會出現死鎖,當兩個事務同時對同一條記錄上了share鎖,又想更新該數據時,會出現死鎖。此時,其中的一方檢測的死鎖並丟棄,另外一方纔能夠更新成功。

而 for update 不會出現死鎖,由於它是排他鎖,只容許一個事務獲取該記錄上的一把鎖。

 

可能用到的情景和對性能的影響

使用情景:

1. select *** for update 的使用場景

爲了讓本身查到的數據確保是最新數據,而且查到後的數據只容許本身來修改的時候,須要用到 for update 子句。

2. select *** lock in share mode 使用場景

爲了確保本身查到的數據沒有被其餘的事務正在修改,也就是說確保查到的數據是最新的數據,而且不容許其餘人來修改數據。可是本身不必定可以修改數據(好比a,b都拿了鎖,a更改了數據,由於b還拿着鎖,a提交不了,直到超時),由於有可能其餘的事務也對這些數據 使用了 in share mode 的方式上了 S 鎖。


性能影響:

select for update 語句,至關於一個 update 語句。在業務繁忙的狀況下,若是事務沒有及時的commit或者rollback 可能會形成其餘事務長時間的等待,從而影響數據庫的併發使用效率。

select lock in share mode 語句是一個給查找的數據上一個共享鎖(S 鎖)的功能,它容許其餘的事務也對該數據上 S鎖,可是不可以容許對該數據進行修改。若是不及時的commit 或者rollback 也可能會形成大量的事務等待。

for update 和 lock in share mode 的區別:前一個上的是排他鎖(X 鎖),一旦一個事務獲取了這個鎖,其餘的事務是無法在這些數據上執行 for update ;後一個是共享鎖,多個事務能夠同時的對相同數據執行 lock in share mode。

 

PS:意向鎖。

innodb的意向鎖有什麼做用?
mysql官網上對於意向鎖的解釋中有這麼一句話
「The main purpose of IX and IS locks is to show that someone is locking a row, or going to lock a row in the table.」
意思是說加意向鎖的目的是爲了代表某個事務正在鎖定一行或者將要鎖定一行。
①在mysql中有表鎖,LOCK TABLE my_tabl_name READ;  用讀鎖鎖表,會阻塞其餘事務修改表數據。LOCK TABLE my_table_name WRITe; 用寫鎖鎖表,會阻塞其餘事務讀和寫。
②Innodb引擎又支持行鎖,行鎖分爲共享鎖,一個事務對一行的共享只讀鎖。排它鎖,一個事務對一行的排他讀寫鎖。
③這兩中類型的鎖共存的問題考慮這個例子:
事務A鎖住了表中的一行,讓這一行只能讀,不能寫。以後,事務B申請整個表的寫鎖。若是事務B申請成功,那麼理論上它就能修改表中的任意一行,這與A持有的行鎖是衝突的。
數據庫須要避免這種衝突,就是說要讓B的申請被阻塞,直到A釋放了行鎖。

數據庫要怎麼判斷這個衝突呢?
step1:判斷表是否已被其餘事務用表鎖鎖表
step2:判斷表中的每一行是否已被行鎖鎖住。
注意step2,這樣的判斷方法效率實在不高,由於須要遍歷整個表。
因而就有了意向鎖。在乎向鎖存在的狀況下,事務A必須先申請表的意向共享鎖,成功後再申請一行的行鎖。在乎向鎖存在的狀況下,
上面的判斷能夠改爲
step1:不變
step2:發現表上有意向共享鎖,說明表中有些行被共享行鎖鎖住了,所以,事務B申請表的寫鎖會被阻塞。

注意:申請意向鎖的動做是數據庫完成的,就是說,事務A申請一行的行鎖的時候,數據庫會自動先開始申請表的意向鎖,不須要咱們程序員使用代碼來申請。

總結:爲了實現多粒度鎖機制(白話:爲了表鎖和行鎖都能用)
相關文章
相關標籤/搜索