MySQL併發 共享鎖目的 mysql併發與鎖機制

2021-10-18 11:48:38 字數 4542 閱讀 3707

在innodb中,鎖是逐步獲得的,因此發生死鎖是可能的。發生死鎖後,innodb一般都能自動檢測到,並使乙個事務釋放鎖並回退,另外乙個事務獲得鎖,並繼續完成事務。但在涉及外部鎖,或涉及表鎖的情況下,innodb並不能完全自動檢測到死鎖,這需要通過設定鎖等待超時引數innodb_lock_wait_timeout來解決。

1. mysql鎖機制

innodb儲存引擎既支援行級鎖(row-level locking),也支援表級鎖,但預設情況下是採用行級鎖。

表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。  行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。

innodb 行級鎖 record-level  lock大致有三種:record lock, gap lock and next-keylocks。

record lock 鎖住某一行記錄  gap lock 鎖住某一段範圍中的記錄  next key lock 是前兩者效果的疊加。

nnodb實現了以下兩種型別的行鎖:

共享鎖:允許乙個事務去讀一行,阻止其他事務獲得相同資料集的排他鎖;

排他鎖:允許獲得排他鎖的事務更新資料,阻止其他事務取得相同資料集的共享讀鎖和排他寫鎖。

為了允許行鎖和表鎖共存,實現多粒度鎖機制,innodb還有兩種內部使用的意向鎖(意向共享鎖和意向排他鎖)。這兩種意向鎖都是表鎖。意向鎖是innodb自動加的,不需要使用者干預。  對於update、delete和insert語句,innodb會自動給涉及資料集加排他鎖;對於普通select語句,innodb不會加任意鎖。

事務可以通過以下語句顯示給記錄集加共享鎖或者排他鎖:

2select * from table_name where ... lock in share mode #共享鎖 select * from table_name where ... for update #排他鎖

innodb的行鎖實現的特點:只有通過索引條件檢索資料,innodb才會使用行級鎖,否則,innodb將會使用表鎖。因為mysql的行鎖是針對索引加的鎖,  而不是針對記錄加的鎖,所以雖然是訪問不同行的記錄,但是如果是使用相同的索引建,是會出現鎖衝突的。

對於鍵值在條件範圍內但並不存在的記錄,叫做間隙。innodb會對這個間隙加鎖,這種鎖機制就是所謂的間隙鎖(next-key鎖)。  innodb使用間隙鎖的目的:一是為了防止幻讀,二是為了滿足其恢復和複製的需要。

innodb如何解決死鎖問題的:

在innodb中,鎖是逐步獲得的,因此發生死鎖是可能的。發生死鎖後,innodb一般都能自動檢測到,並使乙個事務釋放鎖並回退,另外乙個事務獲得鎖,並繼續完成事務。但在涉及外部鎖,  或涉及表鎖的情況下,innodb並不能完全自動檢測到死鎖,這需要通過設定鎖等待超時引數innodb_lock_wait_timeout來解決。

2. 資料庫加鎖分析

mysql innodb儲存引擎,實現的是基於多版本的併發控制協議——mvcc (multi-version concurrency control) (注:與mvcc相對的,  是基於鎖的併發控制,lock-based concurrency control)。mvcc最大的好處,相信也是耳熟能詳:讀不加鎖,讀寫不衝突。  在讀多寫少的oltp應用中,讀寫不衝突是非常重要的,極大的增加了系統的併發效能,這也是為什麼現階段,幾乎所有的rdbms,都支援了mvcc。

在mvcc併發控制中,讀操作可以分成兩類:快照讀 (snapshot read)與當前讀 (current read)。快照讀,讀取的是記錄的可見版本 (有可能是歷史版本),  不用加鎖。當前讀,讀取的是記錄的最新版本,並且,當前讀返回的記錄,都會加上鎖,保證其他事務不會再併發修改這條記錄。

在乙個支援mvcc併發控制的系統中,哪些讀操作是快照讀?哪些操作又是當前讀呢?以mysql innodb為例:

快照讀:簡單的select操作,屬於快照讀,不加鎖。(當然,也有例外,下面會分析)

1select * from table where ?;

當前讀:特殊的讀操作,插入/更新/刪除操作,屬於當前讀,需要加鎖。

5select * from table where ? lock in share mode;

select * from table where ? for update;

insert into table values (…);

update table set ? where ?;

delete from table where ?;

所有以上的語句,都屬於當前讀,讀取記錄的最新版本。並且,讀取之後,還需要保證其他併發事務不能修改當前記錄,對讀取記錄加鎖。  其中,除了第一條語句,對讀取記錄加s鎖 (共享鎖)外,其他的操作,都加的是x鎖 (排它鎖)。

2.1 事務隔離級別

對鎖進行分析前必須要先了解事務隔離級別的關係

隔離級別

髒讀(dirty read)

不可重複讀(nonrepeatable read)

幻讀(phantom read)

未提交讀(read uncommitted)

可能可能

可能已提交讀(read committed)

不可能可能

可能可重複讀(repeatable read)

不可能不可能

可能可序列化(serializable)

不可能不可能

不可能- 未提交讀(read uncommitted):允許髒讀,也就是可能讀取到其他會話中未提交事務修改的資料

提交讀(read committed):只能讀取到已經提交的資料。oracle等多數資料庫預設都是該級別 (不重複讀)

可重複讀(repeated read):可重複讀。在同乙個事務內的查詢都是事務開始時刻一致的,innodb預設級別。在sql標準中,該隔離級別消除了不可重複讀,但是還存在幻象讀

序列讀(serializable):完全序列化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞

mysql innodb預設使用的級別是可重複讀級別(repeatable read),查詢命令如下

7mysql>select @@session.tx_isolation;

| @@session.tx_isolation |

| repeatable-read |

1 row in set

髒讀:當乙個事務進行的操作還未提交時,另外乙個事務讀到了修改的資料,這就是髒讀,但是rr級別事務避免了髒讀。

不可重複讀:是指在乙個事務內,多次讀同一資料。在這個事務還沒有結束時,另外乙個事務也訪問該同一資料。  那麼,在第乙個事務中的兩次讀資料之間,由於第二個事務的修改,那麼第乙個事務兩次讀到的的資料可能是不一樣的。  這樣就發生了在乙個事務內兩次讀到的資料是不一樣的,因此稱為是不可重複讀。但是,rr級別是不會出現不一樣的結果的,即使另乙個事務提交了修改他也查不到變化。

幻讀:第乙個事務對乙個表中的資料進行了修改,這種修改涉及到表中的全部資料行。同時,第二個事務也修改這個表中的資料,  這種修改是向表中插入一行新資料。那麼,以後就會發生操作第乙個事務的使用者發現表中還有沒有修改的資料行,就好象發生了幻覺一樣。

2.2 sql語句加鎖分析

6#sql語句1

select * from table where id = 1;

#sql語句2

update set age = age + 1 where id = 1;

#sql語句3

update set age = age + 1 where id = 1 and nickname = 'hello';

首先我們可以確定的是語句1,他是不加鎖的,屬於快照讀。語句2和語句3要複雜些,我們慢慢來分析。

下面我們預設事務級別為可重複讀(repeated read),因為這是mysql innodb預設級別。

語句2分析:

如果id是主鍵或者是索引的話,那麼鎖定的行只有符合條件的那幾行。

如果id非索引,那麼會鎖表。

語句3分析:

id或者nickname只要有乙個是索引或者是主鍵的話,那麼鎖住的行都是符合條件的行。

但是要注意乙個情況,如果你檢視索引資料值存在大量重複的資料的話(重複的數要是where條件值),那麼有可能條件是不會走索引,而是進行全表查詢,所以此時鎖住的也是全表

因為索引掃瞄數超過30%時,會進行全表掃瞄

2.35#統計出單個使用者領取該券的數量,上了悲觀鎖

select count(coup_id) as count_per from coupon_detail where coup_user_id = 10 and act_code = # for update;

if(# < #)

上面這條複雜的sql在高併發時會發生死鎖的情況,但是確能得到正確的結果。我們來分析一下死鎖的情形。

上面這條語句最裡面的select where coup-user-id = 10 and act-code = 『000000』 會鎖住這一行資料,但是當資料庫沒有值的時候,就上不了鎖,那麼另外乙個事務的select也能查詢,  但是兩個事務都對coup_user-id = 10 and act-code = 『000000』上鎖了,那麼insert的時候兩者都處於等待對方釋放鎖的狀態,所以就發生了死鎖,資料庫解決死鎖之後,只有一條資料  插入成功,這樣也就得到了我們需要的結果。

MySQL 共享排他鎖 mysql 共享排他鎖

1 基礎知識 共享鎖又叫s鎖 share locks 共享鎖就是多個事務對於同一資料可以共享一把鎖,都能訪問到資料,但是只能讀不能修改。排他鎖又叫x鎖 exclusive locks,記為x鎖 排他鎖就是不能與其他鎖並存,只有等待鎖釋放完成以後其他事務才能得到鎖。下面是共享鎖與排他鎖的互斥關係 s ...

Mysql共享鎖 排他鎖

我之前專程寫了mysql中myisam和innodb區別 和mysql儲存引擎 這裡主要寫一些影響鎖相關的內容 mysql 在5.5之前預設使用 myisam 儲存引擎,之後使用 innodb 檢視當前儲存引擎 show variables like storage engine myisam 運算...

mysql 併發 鎖表 mysql 併發插入鎖表

執行過程如下 set autocommit 0 session a insert into bpm pro status values 1 1 usertask1 使用者任務1 1 2015 11 12 00 00 00 test1 1 10000000490026 0 結果 sql insert ...