MySQL服務器邏輯架構
每個連接都會在mysql服務端產生一個線程(內部通過線程池管理線程),比如一個
select語句進入,mysql首先會在查詢緩存中查找是否緩存了這個select的結果集,如
果沒有則繼續執行 解析、優化、執行的過程;否則會之間從緩存中獲取結果集。
MySQL并發控制
共享鎖
共享鎖也稱為讀鎖,讀鎖允許多個連接可以同一時刻并發的讀取同一資源,互不干擾;
排他鎖
排他鎖也稱為寫鎖,一個寫鎖會阻塞其他的寫鎖或讀鎖,保證同一時刻只有一個連接可以寫入數據,同時防止其他用戶對這個數據的讀寫。
鎖策略
鎖的開銷是較為昂貴的,鎖策略其實就是保證了線程安全的同時獲取最大的性能之間的平衡策略。
Mysql鎖策略:talbe lock(表鎖)
表鎖是Mysql最基本的鎖策略,也是開銷最小的策略,它會鎖定整個表;具體情況是:若一個用戶正在執行寫操作,會獲取排他的“寫鎖”,這是會鎖定整個表,阻塞其他用戶的讀、寫操作;
若一個用戶正在執行讀操作,會先獲取共享鎖“讀鎖”,這個鎖運行其他讀鎖并發的對這個表進行讀取,互不干擾。只要沒有寫鎖的進入,讀鎖可以是并發讀取統一資源的。
Mysql鎖策略:row lock(行鎖)
行鎖可以最大限度的支持并發處理,當然也帶來了最大開銷,顧名思義,行鎖的粒度實在每一條行數據。
事務
事務就是一組原子性的sql,或者說一個獨立的工作單元。就是說要么mysql引擎會全部執行這一組sql語句,要么全部都不執行(比如其中一條語句失敗的話)。
比如,tim要給bill轉賬100塊錢:
1.檢查tim的賬戶余額是否大于100塊;
2.tim的賬戶減少100塊;
3.bill的賬戶增加100塊;
這三個操作就是一個事務,必須打包執行,要么全部成功,要么全部不執行,其中任何一個操作的失敗都會導致所有三個操作“不執行”——回滾。
一個良好的事務系統,必須滿足ACID特點:
ACID
A:atomiciy原子性 一個事務必須保證其中的操作要么全部執行,要么全部回滾,不可能存在只執行了一部分這種情況出現。
C:consistency一致性 數據必須保證從一種一致性的狀態轉換為另一種一致性狀態;比如上一個事務中執行了第二步時系統崩潰了,數據也不會出現bill的賬戶少了100塊,但是 tim的賬戶沒變的情況。要么維持原裝(全部回滾),要么bill少了100塊同時tim多了100塊,只有這兩種一致性狀態的
I:isolation隔離性
在一個事務未執行完畢時,通常會保證其他事務無法看到這個事務的執行結果
D:durability持久性事務一旦commit,則數據不會保存下來,即使提交完之后系統崩潰,數據也不會丟失。
隔離級別
1. READ UNCOMMITTED(未提交讀)
事務中的修改,即使沒有提交,對其他事務也是可見的。事務可以讀取未提交的數據——臟讀。臟讀會導致很多問題,一般不適用這個隔離級別。
2. READ COMMITTED(提交讀)
一般數據庫都默認使用這個隔離級別(Mysql不是),這個隔離級別保證了一個事務如果沒有完全成功(commit執行完),事務中的操作對其他事務是不可見的。-
3. REPEATABLE READ(可重復讀) 這個隔離級別解決了臟讀的問題,但會產生幻讀,問題。
臟讀與幻讀與不可重復讀
3.1) 臟讀:一個事務讀取到另一事務未提交的更新新據。當一個事務正在訪問數據,并且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時,另外一個事務也訪問這個數據,然后使用了這個數據。因為這個數據是還沒有提交的數據, 那么另外一個事務讀到的這個數據是臟數據,依據臟數據所做的操作也可能是不正確的。
3.2) 不可重復讀:在同一事務中,多次讀取同一數據返回的結果有所不同。換句話說就是,后續讀取可以讀到另一事務已提交的更新數據。相反,“可重復讀”在同一事務中多次讀取數據時,能夠保證所讀數據一樣,也就是,后續讀取不能讀到另一事務已提交的更新數據。
3.3) 幻讀:事務T1執行一次查詢,然后事務T2新插入一行記錄,這行記錄恰好可以滿足T1所使用的查詢的條件。然后T1又使用相同的查詢再次對表進行檢索,但是此時卻看到了事務T2剛才插入的新行。這個新行就稱為“幻像”,因為對T1來說這一行就像突然出現的一樣。
4. SERIALIZABLE(可串行化) 最強的隔離級別,通過給事務中每次讀取的行加鎖(行鎖),保證不產生幻讀問題,但是會導致大量超時以及鎖爭用問題。
MySQL死鎖問題
死鎖,就是產生了循環等待鏈條,我等待你的資源,你卻等待我的資源,我們都相互等待,誰也不釋放自己占有的資源,導致無線等待下去。
比如:
//Thread A
START TRANSACTION;
UPDATE account SET p_money=p_money-100 WHERE p_name="tim";
UPDATE account SET p_money=p_money-100 WHERE p_name="bill";
COMMIT;
//Thread B
START TRANSACTION;
UPDATE account SET p_money=p_money-100 WHERE p_name="bill";
UPDATE account SET p_money=p_money-100 WHERE p_name="tim";
COMMIT;
當線程A執行到第一條語句UPDATE account SET p_money=p_money-100 WHERE p_name=”tim”;鎖定了p_name=”tim”的行數據;并且試圖獲取p_name=”bill”的數據;
,此時,恰好,線程B也執行到第一條語句:UPDATE account SET p_money=p_money+100 WHERE p_name=”bill”;
鎖定了 p_name=”bill”的數據,同時試圖獲取p_name=”tim”的數據;
此時,兩個線程就進入了死鎖,誰也無法獲取自己想要獲取的資源,進入無線等待中,直到超時!
對于死鎖,數據庫一般通過死鎖監測、死鎖超時機制解決;通常會執行回滾,打破死鎖狀態,然后再次執行之前死鎖的事務即可。
MySQL中的事務
自動提交(AutoCommit)
mysql默認采用AutoCommit模式,也就是每個sql都是一個事務,并不需要顯示的執行事務
多版本并發控制-MVCC
MVCC是個行級鎖的變種,它在很多情況下避免了加鎖操作,因此開銷更低。雖然實現不同,但通常都是實現非阻塞讀,對于寫操作只鎖定必要的行。
通常MVCC實現有樂觀并發控制與悲觀并發控制,INNODB的MVCC通常是通過在每行數據后邊保存兩個隱藏的列來實現,一個保存了行的創建時 間,另一個保存了行的刪除時間。當然存儲的并不是實際的時間值,而是系統版本號,每個事務開始,系統版本號就會遞增!,每個事務開始時刻的版本號也會作為 這個事務的版本號,用來和查詢到的每行版本號做比較。下邊在Mysql默認的Repeatable Read隔離級別下,具體看看MVCC操作:
Select:
a.InnoDB只查找版本號早于當前版本號的數據行,這樣保證了讀取的數據要么實在這個事務開始之前就已經commit了的(早于當前版本號),要么是在這個事務自身中執行操作的數據(等于當前版本號)。
b.行的刪除版本號要么未定義,要么早于當前的版本號,這樣保證了事務讀取到的數據在事務開始之前未被刪除。
Insert
InnoDB為這個事務中新插入的行,保存當前事務版本號的行(作為行的版本號)。
Delete
InnoDB為每一個刪除的行保存當前事務版本號,最為行的刪除標記。
Update
InnoDB將保存當前版本號最為行版本號,同時保存當前版本號到原來行(更新前)的刪除版本號標識處。
最后編輯于 :2017.12.06 01:33:34
?著作權歸作者所有,轉載或內容合作請聯系作者 平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。