MySQL性能優化(九)-- 鎖機制之行鎖

1、行鎖概念及特色

1.概念:給單獨的一行記錄加鎖,主要應用於innodb表存儲引擎mysql

2.特色:在innodb存儲引擎中應用比較多,支持事務、開銷大、加鎖慢;會出現死鎖;鎖的粒度小,併發狀況下,產生鎖等待的機率比較低,因此支持的併發數比較高。sql

2、數據庫事務

1.概念:事務是一系列操做組成的工做單元,該工做單元內的操做是不可分割的,也就是說要麼所有都執行,要麼所有不執行。數據庫

2.特性:ACIDbash

  • 原子性:事務是最小的工做單元,不可分割,要麼都作,要麼都不作
  • 一致性:事務執行前和執行後的數據要保證正確性,數據完整性沒有被破壞。
  • 隔離性:在併發事務執行的時候,一個事務對其餘事務不會產生影響。
  • 持久性:一個事務一旦提交,它對數據庫中的數據的改變就應該是永久性的

3、多個事務併發執行 問題及解決方案

1.問題session

  • 丟失更新:在沒有事務隔離的狀況下,兩個事務同時更新一條數據,後一個事務 會 覆蓋前面事務的更新,致使前面的事務丟失更新。
  • 髒讀:事務A先更新數據,可是沒有提交,事務B讀到了事務A沒有提交的數據。
  • 不可重複讀:事務A中,先讀到一條數據,事務A尚未結束,此時,事務B對該條數據進行了修改操做,事務A又讀到了這條數據,事務A兩次讀到的數據不一樣。
  • 幻讀:事務A先讀到一批數據,假設讀到10條,事務B插入了一條數據,此時,事務A又讀這一批數據,發現多了一條,好像幻覺同樣。

注:不可重複讀的重點是修改,一樣的條件,你讀取過的數據,再次讀取出來發現值不同。併發

幻讀的重點在於新增或者刪除,一樣的條件,第 1 次和第 2 次讀出來的記錄數不同。spa

2.解決方案--數據庫隔離機制code

  1. 未提交讀(read uncommitted):這是數據庫最低的隔離級別,容許一個事務讀另外一個事務未提交的數據。

解決了丟失更新,可是會出現髒讀、不可重複讀、幻讀。cdn

  1. 提交讀(read committed):一個事務更新的數據 在提交以後 才能夠被另外一個事務讀取,即一個事務不能夠讀取到另外一個事務未提交的數據。

解決了丟失更新和髒讀,可是會出現不可重複讀和幻讀。blog

  1. 可重複讀(repeatale read):這是數據庫默認的事務隔離級別,保證一個事務在相同條件下先後兩次讀取的數據是一致的。

解決了丟失更新、髒讀和不可重複讀,可是會出現幻讀。

  1. 序列化(serializable):這是數據庫最高的隔離級別。事務串行執行,不會交叉執行。

解決了全部的問題。

注:樂觀所能夠解決幻讀。

4、行鎖的特性

查看mysql事務隔離級別:show variables like 'tx_iso%';

前提:set autocommit=0; // 設置自動提交事務爲手動提交 

/* 行鎖案例*/
create table lock_two(
    id int,
    col int
)engine=innodb;

insert into lock_two(id,col) values (1,1);
insert into lock_two(id,col) values (2,2);
insert into lock_two(id,col) values (3,3);
複製代碼

1.在session1中執行update : update lock_two set col=11 where id=1;

(1)分別在session1和session2中查詢lock_two,看id爲1的記錄的col是否修改了。

img

發現session1 的記錄修改了,session2中的記錄沒有被修改。

(2)在session1中執行commite後,而後再在session2中查詢:

img

發現session2中的表數據改變了。

2.在session1中執行update:update lock_two set col=11 where id=1,不執行commit;

在session2中執行uodate :update lock_two set col=11 where id=1,不執行commit;

img

發現session2中的update發生阻塞,而且超過一段時間報錯。

3.在session1中執行update:update lock_two set col=22 where id = 2; 不執行commit

在session2中執行另外一條update:update lock_two set col=33 where id = 3;

img
 此時,session2中的update發生阻塞,在沒發生錯誤的狀況下,session1執行commit,session2中的update會立刻執行。

4.在lock_two中建立索引,

create index idx_id on lock_two(id);
create index idx_col on lock_two(col);
複製代碼

而後重複第3步,

img

發現session2能夠更新,不會產生阻塞。由於用上了索引,至關於行鎖。

結論:若是沒有用上索引,行鎖變成表鎖

5、手動鎖一行記錄格式

begin;

select * from lock_two where id=2 for update;
複製代碼
  1. 在session1中執行上面語句,在ssesion2中能夠查看,可是不能夠修改 sesion1中的for update 的記錄。
  2. 當session1中執行commit後,seesion2中的update馬上執行。

6、間隙鎖

1.定義

在範圍查找的狀況下, innodb會給範圍條件中的數據加上鎖,不管數據是不是否真實存在。

2.例子

在session1中update:update lock_two set col=666 where id>2 and id<8;

1) 在session2中執行insert:insert into lock_two values(9,99);

插入執行成功!

2) 在session2中執行insert:insert into lock_two values(7,77);

插入阻塞,一段時間後報錯!

執行select:select * from lock_two where id=4;

查詢成功!

建議:在innodb中,由於有間隙鎖的存在,最好在where中少使用這種範圍查找。

7、查看行鎖的信息

show status like 'innodb_row_lock%';

img

說明:

  • Innodb_row_lock_current_waits :當前正在等待的數量
  • Innodb_row_lock_time: 從啓動到如今鎖定的總時長,單位是ms
  • Innodb_row_lock_time_avg :鎖等待的平均時長
  • Innodb_row_lock_time_max:等待鎖時間最長的一個時間
  • Innodb_row_lock_waits:總共的等待次數

歡迎關注個人公衆號,第一時間接收最新文章~ 搜索公衆號: 碼咖 或者 掃描下方二維碼:

img
相關文章
相關標籤/搜索