首先說一下數據庫事務的四大特性html
事務的四大特性是ACID(不是"酸"....)mysql
原子性指的是事務要麼徹底執行,要麼徹底不執行.算法
事務完成時,數據必須處於一致的狀態.若事務執行途中出錯,會回滾到以前的事務沒有執行前的狀態,這樣數據就處於一致的狀態.若事務出錯後沒有回滾,部分修改的內容寫入到了數據庫中,這時數據就是不一致的狀態.sql
同時處理多個事務時,一個事務的執行不能被另外一個事務所幹擾,事務的內部操做與其餘併發事務隔離.數據庫
事務提交後,對數據的修改是永久性的.segmentfault
Mysql的鎖其實能夠按不少種形式分類:微信
這裏主要討論S鎖,X鎖,樂觀鎖與悲觀鎖.session
S鎖與X鎖是InnoDB引擎實現的兩種標準行鎖機制.查看默認引擎可以使用併發
show variables like '%storage_engine%';
做者的mysql版本爲8.0.17,結果以下:post
先建好測試庫與測試表,很簡單,表就兩個字段.
create database test; use test; create table a ( id int primary key auto_increment, money int );
S鎖也叫共享鎖,讀鎖,數據只能被讀取不能被修改.
玩一下,上鎖!
lock table a read;
而後.....
只能讀不能改,刪,也不能增.
X鎖也叫排他鎖,寫鎖,一個事務對錶加鎖後,其餘事務就不能對其進行加鎖與增刪查改操做.
設置手動提交,開啓事務,上X鎖.
set autocmmmit=0; start transaction; lock table a write;
在開啓另外一個事務,使用select語句.
set autocommit=0; start transaction; select * from a;
這裏是阻塞select操做,由於一直都沒釋放X鎖.
一樣也不能再加鎖,也是阻塞中.
回到原來那個加鎖的事務,嗯,什麼事也沒有,正常讀寫.
釋放鎖後:
unlock table;
在另外一個事務中能夠看到中斷時間.
樂觀鎖就是老是假設是最好的狀況,每次去操做的時候都不會上鎖,但在更新時會判斷有沒有其餘操做去更新這個數據,是一種寬鬆的加鎖機制.
mysql自己沒有提供樂觀鎖的支持,須要本身來實現,經常使用的方法有版本控制和時間戳控制兩種.
舉個例子,假設此時version=1,A進行操做,更新數據後version=2,與此同時B也進行操做,更新數據後version=2,A先完成操做,率先將數據庫中的version設置爲2,此時B提交,B的version與數據庫中的version同樣,不接受B的提交.
還有一種實現方法叫CAS算法,這個做者不怎麼了解,有興趣能夠自行搜索.
悲觀鎖就是老是假設最壞的狀況,在整個數據處理狀態中數據處於鎖定狀態,悲觀鎖的實現每每依靠數據庫的鎖機制.每次在拿到數據前都會上鎖.
mysql在調用一些語句時會上悲觀鎖,如(先關閉自動提交,開啓事務):
set autocommit=0; start transaction;
兩個事務都這樣操做,而後其中一個事務輸入:
select * from a where xxx for update;
在另外一事務也這樣輸入:
這時語句會被阻塞,直到上鎖的那個事務commit(解開悲觀鎖).
在另外一事務中能夠看到這個事務被阻塞了2.81s.
*** lock in share mode.
也會加上悲觀鎖.
髒讀是指一個事務讀取到了另外一事務未提交的數據,形成select先後數據不一致.
好比事務A修改了一些數據,但沒有提交,此時事務B卻讀取了,這時事務B就造成了髒讀,通常事務A的後續操做是回滾,事務B讀取到了臨時數值.
事務A | 事務B |
---|---|
開始事務 | 開始事務 |
更新X,舊值X=1,新值X=2 | |
讀取X,X=2(髒讀) | |
回滾X=1 | |
結束事務(X=1) | 結束事務 |
幻讀是指並非指同一個事務執行兩次相同的select語句獲得的結果不一樣,而是指select時不存在某記錄,但準備插入時發現此記錄已存在,沒法插入,這就產生了幻讀.
事務A | 事務B |
---|---|
開始事務 | 開始事務 |
select某個數據爲空,準備插入一個新數據 | |
插入一個新數據 | |
提交,結束事務 | |
插入數據,發現插入失敗,因爲事務B已插入相同數據 | |
結束事務 |
不可重複讀指一個事務讀取到了另外一事務已提交的數據,形成select先後數據不一致.
好比事務A修改了一些數據而且提交了,此時事務B卻讀取了,這時事務B就造成了不可重複讀.
事務A | 事務B |
---|---|
開始事務 | 開始事務 |
讀取X=1 | 讀取X=1 |
更新X=2 | |
提交,結束事務 | |
讀取X=2 | |
結束事務 |
第一類丟失更新就是兩個事務同時更新一個數據,一個事務更新完畢並提交後,另外一個事務回滾,形成提交的更新丟失.
事務A | 事務B |
---|---|
開始事務 | 開始事務 |
讀取X=1 | 讀取X=1 |
修改X=2 | 修改X=3 |
提交,結束事務 | |
回滾 | |
結束事務(X=1) | X=1,X本應爲提交的3 |
第二類丟失更新就是兩個事務同時更新一個數據,先更新的事務提交的數據會被後更新的事務提交的數據覆蓋,即先更新的事務提交的數據丟失.
事務A | 事務B |
---|---|
開始事務 | 開始事務 |
讀取X=1 | 讀取X=1 |
更新X=2 | |
提交事務,X=2,結束 | |
更新X=3 | |
提交事務,X=3,事務A的更新丟失,結束 |
封鎖協議就是在用X鎖或S鎖時制定的一些規則,好比鎖的持續時間,鎖的加鎖時間等.不一樣的封鎖協議對應不一樣的隔離級別.事務的隔離級別一共有4種,由低到高分別是Read uncommitted,Read committed,Repeatable read,Serializable,分別對應的相應的封鎖協議等級.
一級封鎖協議對應的是Read uncommitted隔離級別,Read uncommitted,讀未提交,一個事務能夠讀取另外一個事務未提交的數據,這是最低的級別.一級封鎖協議本質上是在事務修改數據以前加上X鎖,直到事務結束後才釋放,事務結束包括正常結束(commit)與非正常結束(rollback).
**一級封鎖協議不會形成更新丟失,但可能引起髒讀,幻讀,不可重複讀.
設置手動提交與事務隔離等級爲read uncommited,並開啓事務(注意要先設置事務等級再開啓事務).**
set autocommit=0; set session transaction isolation level read uncommitted; start transaction;
(中間有一行打多了一個t能夠忽略.....)
在一個事務中修改表中的值,不提交,另外一個事務能夠select到未提交的值.
出現了髒讀.
在一個事務中插入一條數據,提交.
另外一事務中select時沒有,準備insert,可是insert時卻提示已經存在.引起幻讀.
未操做提交前:
另外一事務修改並提交:
再次讀:
引起不可重複讀.
二級封鎖協議本質上在一級協議的基礎上(在修改數據時加X鎖),在讀數據時加上S鎖,讀完後當即釋放S鎖,能夠避免髒讀.但有可能出現不可重複讀與幻讀.二級封鎖協議對應的是Read committed與Repeatable Read隔離級別.
先設置隔離等級
set session transaction isolation level read committed;
Read committed,讀提交,讀提交能夠避免髒讀,但可能出現幻讀與不可重複讀.
開啓一個事務並更新值,在這個事務中money=100(更新後)
另外一事務中money爲未更新前的值,這就避免了髒讀.
注意,事實上髒讀在read committed隔離級別下是不被容許的,可是mysql不會阻塞查詢,而是返回未修改以前數據的備份,這種機制叫MVCC機制(多版本併發控制).
在一個事務中插入數據並提交.
另外一事務中不能插入"不存在"的數據,出現幻讀.
事務修改並提交前:
事務修改並提交:
出現不可重複讀.
Repeatable read比Read committed嚴格一點,是Mysql的默認級別,讀取過程更多地受到MVCC影響,可防止不可重複讀與髒讀,但仍有可能出現幻讀.
在一個事務中修改數據,不提交.
另外一事務中兩次select的結果都不變,沒有出現髒讀.
一個事務修改數據並提交.
另外一事務中select的結果沒有發生改變,即沒有出現不可重複讀.
同理,一個事務插入一條數據並提交.
另外一個事務插入時出現幻讀.
三級封鎖協議,在一級封鎖協議的基礎上(修改時加X鎖),讀數據時加上S鎖(與二級相似),可是直到事務結束後才釋放S鎖,能夠避免幻讀,髒讀與不可重複讀.三級封鎖協議對應的隔離級別是Serializable.
先設置Serializable隔離級別
set session transaction isolation level serializable
設置事務隔離等級後開啓事務並update,發現堵塞.從而避免了髒讀.
插入時直接阻塞,避免了幻讀.
在髒讀的例子中能夠知道,update會被堵塞,都不能提交事務,所以也避免了不可重複讀.
事務必須分爲兩個階段對數據進行加鎖與解鎖,兩端鎖協議叫2PL(不是2PC),全部的加鎖都在解鎖以前進行.
加鎖會在更新或者
select *** for update *** lock in share mode
時進行
解鎖在事務結束時進行,事務結束包括rollback與commit.
最後,如下是做者的微信公衆號,裏面有更多精彩文章,歡迎關注.一塊兒學習,一塊兒成長.
參考連接
1:ACID1
2:ACID2
3:mysql的鎖1
4:樂觀鎖與悲觀鎖1
5:樂觀鎖與悲觀鎖2
6:樂觀鎖與悲觀鎖3
9:數據庫封鎖協議
10:mysql事務隔離機制1
11:mysql事務隔離機制2
12:mysql幻讀
14:mysql兩段鎖1
15:mysql兩段鎖2