mysql - 表鎖,行鎖

MyISAM存儲引擎:開銷小,加鎖快,無死鎖。鎖定粒度大,併發度低,容易發生鎖衝突。   不支持事務。            採用表鎖 (操做時對操做的表上鎖) 基本不用了java

innoDB存儲引擎:開銷大,有死鎖,鎖定粒度小,不容易發生衝突。                                                                       採用行鎖(每句sql執行時對操做行上鎖),可是也支持表鎖     mysql默認引擎mysql

 

 

問題:庫存超賣linux

庫存1,結果兩我的同時購買,第一我的判斷後還有1個剩餘,因而執行 update 操做, 這時第二我的開始判斷,此時update還沒結束,剩餘仍是1,因而也開始進update操做,致使庫存變爲-1.sql

1. 悲觀鎖,在判斷的select語句中加入行鎖,與update語句互斥,保證第1我的提交事務前,第2我的不能操做這個貨物。數據庫

 

2.樂觀鎖,添加版本字段,查詢時更新版本字段,版本字段變化時,不能更新。安全

select num, version from warehouse where id = 商品編號;   查詢數量同時查出版本,好比111併發

update warehouse set num = num - 1, version = version + 1 where version = 111; 這樣假如warhouse在查詢後這個商品被更改過,這條將不會更改任何字段。若是成功了版本同時改變爲112。編輯器

 

reference: http://www.zsythink.net/archives/1233/測試

事務隔離:spa

讀未提交: read-uncommitted     可能問題:髒讀,幻讀,不可重複讀           A修改操做後沒有提交,B也能看到改動

讀已提交:read-commited           可能問題:幻讀,不可重複讀                      A修改操做後提交了,B能夠看到改動

可重複讀: repeatable-read        可能問題:不可重複讀                                 A修改操做後,B的操做也用到了相同的表後能夠看到A的改動

可序列化:serialiazable               三種都不會出現                                           A修改操做時,B連讀取都作不到。    這樣最安全,可是沒法併發,效率過低。

 

髒讀:             讀到了別人未提交的改動  (表的數據實際沒有變化,可是你覺得變了)        B修改了數據還沒提交,A查到了,B的數據提交失敗回滾了,A覺得B改完了。

幻讀:             數量不對                           ( 表的數據數量變化)                                           A在查詢後, B添加或者刪除了數據, A覺得數據沒變。

不可重複讀:  讀取和修改時狀態不一致  (數據值發生變化)                                               A在查詢後,B修改了數據值,A仍是以舊數據做爲判斷條件。

 

 不可重複讀  和 幻讀差很少,不過一個是莫名其妙多了一行,一個是莫名其妙值變了。 因此庫存超賣是不可重複讀。

 

1.

查詢數據庫事務隔離等級:

SHOW VARIABLES LIKE '%isolation';  #網上查的版本是tx_isolation, 個人版本是 transaction_isolation,因此直接用模糊查詢

通常都是 可重複讀級別

2.修改隔離等級:

SET transaction_isolation = 'read-uncommitted';

 

mysql的鎖:   表名 index_test

1. 查看錶存儲引擎

SHOW TABLE STATUS LIKE 'index_test';

 

 

 2. 修改表存儲引擎

ALTER TABLE index_test ENGINE = MYISAM;   #修改表引擎
SHOW TABLE STATUS LIKE 'index_test';

 

3. 表鎖

表鎖有讀鎖,和寫鎖

讀鎖:不讓其餘鏈接修改表, 是能夠查詢的!

寫鎖:不讓其餘鏈接查詢和修改表。

- -|||注意別隻憑字面意思理解,有時候容易形成讀鎖是不讓讀,寫鎖是不讓寫的錯覺(好比我。。。)

 

1).對index_test 添加表寫鎖   (這時在當前數據庫鏈接會話下是能夠修改表的。)

LOCK TABLE index_test WRITE;

2). 新建一個數據庫鏈接(文件 -> 新連接     不是新打開一個查詢編輯器或者窗口= =sqlyog打開新窗口仍是用的同一個連接。。。)

而後修改index_test表  (查詢也是同樣的結果)

能夠看到 會一直顯示處理中。

3).這時在第一個鏈接中解鎖表

UNLOCK TABLES;

發現第二個鏈接的修改語句馬上執行完畢。

 

我的測試(我有一個大膽的想法):

創建鏈接a,b,c

a鎖表

b修改 - 進入等待

c輸入解鎖

b依舊等待

c鎖表 - 進入等待

a解鎖 - b執行完畢,c執行完畢

a鎖表 - 進入等待。。。由於c把表鎖了

c解鎖

能夠得出結論:不一樣的鏈接的鎖表和解鎖是獨立的。。。

 

4).對index_test 添加表讀鎖   (這時在當前數據庫鏈接會話下是能夠修改表的。)

LOCK TABLE index_test READ;

這時第二個鏈接,修改時會進入等待狀態,可是能夠查詢。

 

4. 行鎖

myisam 只有表級鎖,因此要切換回innodb

行鎖:執行語句時,只鎖住相關數據行,而不是整個表。 這也是innodb能支持事務的主要緣由之一。(要是鎖表,那麼一次事務中用到多個表的時候,會致使數據庫不少表被鎖,大幅拖慢效率)

innodb會自動給修改語句添加行鎖。

1. 修改表引擎 

ALTER TABLE index_test ENGINE = INNODB;

2.取消自動提交(innodb會把單獨的sql語句做爲事務直接提交,取消後必須commit才能提交一次事務)

SET autocommit = 0;

3. 進行一次修改

UPDATE index_test SET key1 = 1 WHERE t_id = 1; 

新建鏈接2,對錶進行修改

UPDATE index_test SET key1 = 3 WHERE t_id = 1; 

鏈接2 進入等待

 

過一段時間後提示超時

 

 

 

鏈接1 提交修改

COMMIT;

 

鏈接2 再次嘗試修改,成功

 

我的測試:既然是行鎖,那麼嘗試在鏈接2修改不一樣行的數據看看結果-。-

鏈接1:

UPDATE index_test SET key1 = 1 WHERE t_id = 1; 

鏈接2:

UPDATE index_test SET key1 = 3 WHERE t_id = 2; 

鏈接2直接修改爲功。 

 

5.爲查詢語句添加行鎖

悲觀鎖解決庫存超賣問題:

悲觀鎖,在判斷的select語句中加入行鎖,保證第1我的提交事務前,第2我的不能操做這個貨物。

 

用index_test 表做爲例子

 

 t_id = 1這一行中, key = 1。假設爲key1 是一種貨物,剩餘1個

a 進行查詢,同時上行鎖,準備更新

SELECT * FROM index_test WHERE t_id = 1 FOR UPDATE;

b此時開始查詢

SELECT * FROM index_test WHERE t_id = 1 FOR UPDATE;

產生讀讀互斥,b進入等待,等待a提交。

a更新並提交

UPDATE index_test SET key1 = key1 - 1 WHERE t_id = 1; 
COMMIT;

a提交後,b查出結果

 

 key1已經變成了0

 

從而避免了庫存超賣。

 

 

 

死鎖:

和java的差很少。。。

a:查詢A,上鎖A, 等查到後修改B,  而後提交

b:查詢B,上鎖B, 等查到後修改A,  而後提交

 

對於a,須要修改B才能提交事務,而後解鎖A。

對於b,須要修改A才能提交事務,而後解鎖B。

造成死鎖。

 

 

彼此佔用鑰匙。

解決:kill掉其中一個線程。(圖形界面好比sqlyog中直接點個x就完事了。。。不過linux開發有時要用名命令行的那種 - -)

相關文章
相關標籤/搜索