一文詳解MySQL的并發(fā)控制
1.概述
所謂的并發(fā)控制,就是規(guī)避多個會話并發(fā)訪問數(shù)據(jù)庫帶來的諸如臟數(shù)據(jù)之類的數(shù)據(jù)一致性問題,MySQL中提供了一系列的機制讓我們可以去進行并發(fā)控制。
本質(zhì)上來說MySQL就是用的兩種鎖來進行并發(fā)控制,一種是表鎖,鎖住整張表;一種是行鎖,鎖住某個數(shù)據(jù)行。
平時我們使用的時候,很少會直接去操作鎖,因為MySQL已經(jīng)幫我們封裝的很好了,直接用innodb引擎+事務就能很好的進行并發(fā)控制,事務底層其實依賴的就是行鎖。
本文會先聊事務、再聊表鎖、行鎖,但其實總的來說MySQL進行并發(fā)控制,就是行鎖和表鎖,事務的底層用的就是行鎖,只是事務太重要了所以單獨拎出來作為一個獨立的章節(jié)聊。
2.事務
2.1.什么是事務
注意:只有innodb引擎是支持事務的,所以本文與事務相關的討論,默認都是在innodb引擎下。
在實際使用中,會存在這樣一類場景,我們希望幾條SQL要么同時執(zhí)行成功,要么同時失敗,不能有的成功,有的失敗。
比如網(wǎng)購下單,生成訂單、扣減庫存兩條SQL,必須保證要么全部成功,要么全部失敗,不能說生成訂單成功,但是扣減庫存失敗了,或者說扣減庫存成功但是生成訂單失敗了,以上任何一種情況都是會產(chǎn)生臟數(shù)據(jù)的。如果兩者中有一者失敗,另外一個也需要跟著執(zhí)行不成功,從而保證數(shù)據(jù)的正確性。
事務就是為了滿足將多條捆綁在一起,同成功,同失敗而出現(xiàn)的。人們在實現(xiàn)事務過程中,發(fā)現(xiàn)事務要實現(xiàn)上面我們說的效果,那么就必須實現(xiàn)四點:
- 原子性(Atomicity)
- 一致性(Consistent)
- 隔離性(Isolation)
- 持久性(Durable)
也就是大名鼎鼎的ACID,很多地方稱其為事務的四大特性。
1.原子性:
事務是一個原子操作單元,其對數(shù)據(jù)的修改,要么全部執(zhí)行,要么全部不執(zhí)行。
2.一致性:
數(shù)據(jù)庫中的數(shù)據(jù)總是從一個狀態(tài)到另一個狀態(tài),不能存在中間狀態(tài)。繼續(xù)以網(wǎng)購下單為例,一開始的數(shù)據(jù)庫中的數(shù)據(jù)為A狀態(tài),執(zhí)行完事務,扣減庫存、新增訂單后的數(shù)據(jù)狀態(tài)為B狀態(tài),數(shù)據(jù)庫只能由A到B,不能出現(xiàn)諸如扣了庫存,沒生成訂單,或者生成了訂單,沒扣庫存這樣的中間狀態(tài)。
3.隔離性:
一個事務在提交之前,其所做的修改對其它事務來說是不可見的。不保證隔離性會產(chǎn)生臟數(shù)據(jù),這個很好理解,舉個例子:
A的銀行賬戶有400,有兩個事務,彼此之間數(shù)據(jù)可見,也就是一個事務修改數(shù)據(jù),不管提沒提交其他事務都看得見。
事務1,A向B轉200:
- A的賬戶扣減200
- B的賬戶新增200
事務2,A向C轉200:
- A的賬戶扣減200
- C的賬戶新增200
如果在事務1中的1、2步的時間間隙內(nèi)事務2間插執(zhí)行完畢,那么在事務1第2步執(zhí)行前A的賬戶中已經(jīng)被扣減了兩次200,余額為0,C的賬戶中多了200。
這時候如果在事務1的第2步中出錯了、回滾,那么A的賬戶又會回到事務1第一步執(zhí)行之前的狀態(tài),也就是A的賬戶又恢復成了400。最后C的賬戶平白無故多了200。
4.持久性:
事務完成后,其對數(shù)據(jù)的修改是永久的,即使系統(tǒng)斷電、重啟,也不會變。
2.2.事務的隔離級別
2.2.1.三種數(shù)據(jù)一致性問題
在沒有隔離性的情況下,事物之間會出現(xiàn)3種數(shù)據(jù)一致性問題:
- 臟讀:事務A讀取了事務B更新的數(shù)據(jù),然后B回滾操作,那么A讀取到的數(shù)據(jù)是臟數(shù)據(jù)
- 不可重復讀:事務 A 多次讀取同一數(shù)據(jù),事務 B 在事務A多次讀取的過程中,對數(shù)據(jù)作了更新并提交,導致事務A多次讀取同一數(shù)據(jù)時,結果 不一致。
- 幻讀:系統(tǒng)管理員A將數(shù)據(jù)庫中所有學生的成績從具體分數(shù)改為ABCDE等級,但是系統(tǒng)管理員B就在這個時候插入了一條具體分數(shù)的記錄,當系統(tǒng)管理員A改結束后發(fā)現(xiàn)還有一條記錄沒有改過來,就好像發(fā)生了幻覺一樣,這就叫幻讀。
2.2.2.四種隔離級別
隔離級別可理解為,隔離性的嚴格度,MySQL并不是固定死了各個事務間就是不可讀的,而是規(guī)定了各種強度的隔離級別。
觀察上面3種數(shù)據(jù)一致性問題就會發(fā)現(xiàn),解決它們需要的隔離性是遞增的,MySQL一共給出4種隔離級別,隔離性也是遞增的,對應解決以上3個問題,有3種,加上1種3種問題都能覆蓋解決的:
- Read Uncommitted(讀未提交):最低的隔離級別,允許一個事務讀取另一個事務尚未提交的數(shù)據(jù)變更??赡軐е屡K讀(Dirty Read)問題。
- Read Committed(讀已提交):確保一個事務只能讀取另一個事務已經(jīng)提交的數(shù)據(jù)變更。防止臟讀問題,但可能導致不可重復讀(Non-repeatable Read)問題。
- Repeatable Read(可重復讀):確保在同一個事務中多次讀取同一數(shù)據(jù)時,能夠得到一致的結果。防止臟讀和不可重復讀問題,但可能導致幻讀(Phantom Read)問題。
- Serializable(串行化):最高的隔離級別,強制事務串行執(zhí)行,確保不會發(fā)生臟讀、不可重復讀和幻讀問題。但是并發(fā)性能較差,通常不建議在高并發(fā)環(huán)境中使用。
2.3.如何設置隔離級別
可以在連接字符串中設置隔離級別:
jdbc:mysql://localhost/mydatabase?useSSL=false&characterEncoding=utf8&transactionIsolation=隔離級別
可以通過SQL在會話中設置隔離級別:
SET TRANSACTION ISOLATION LEVEL 隔離級別;
3.鎖
3.1.鎖與事務的關系
鎖是計算機協(xié)調(diào)多個進程或線程并發(fā)訪問某一資源的機制,用來解決并發(fā)訪問帶來的數(shù)據(jù)一致性問題。在數(shù)據(jù)庫中,除傳統(tǒng)的計算資源(如CPU、IO、RAM)的爭用以外,數(shù)據(jù)更是會被并發(fā)訪問的資源。所以MySQL數(shù)據(jù)庫也用鎖機制來保證數(shù)據(jù)并發(fā)訪問的一致性。
不同的隔離級別底層就是用不同的鎖來實現(xiàn)的。
3.2.分類
MySQL的鎖可以從兩種維度來分,
一個維度是按照鎖的是一行,還是鎖的是整張表,分為:
- 行鎖
- 表鎖
另一個維度是按照鎖的操作是讀操作,還是寫操作,分為:
- 讀鎖
- 寫鎖
3.3.表鎖
3.3.1.概述
MySQL中innodb引擎和myisam引擎均支持用lock tables指令來鎖表。
3.3.2.讀鎖
讀鎖,一種共享鎖,針對被鎖表,所有會話都可以進行讀操作,所有會話都無法進行寫操作。加鎖方和其他客戶端的區(qū)別是,加鎖方直接不允許進行寫操作,而其他會話的寫操作允許進行,只是會被阻塞掛起。鎖解開后,所有掛起的操作線程會去重新爭搶資源。
加鎖指令:
lock tables 表名 read;
釋放鎖指令:
unlock tanles;
加鎖方不允許進行寫操作:
其它客戶端的寫操作在加鎖方釋放鎖之前都被掛起:
3.3.3.寫鎖
寫鎖,排它鎖,針對被鎖表,加鎖方可以讀寫,其他會話的寫操作會直接失敗,讀操作會被阻塞掛起,解鎖以后,被掛起的線程會重新去爭搶資源。
加鎖指令:
lock tables 表名 write;
其它會話的讀操作、寫操作在加鎖方釋放鎖之前都被阻塞掛起:
3.3.4.保護機制
讀鎖、寫鎖中,加鎖方都只能讀當前被自己鎖定的表,這是MySQL的一個保護機制,為的就是強制要求加鎖方給出一個說法,到底準備鎖多久,不給說法不讓走。
3.4.行鎖
3.4.1.概述
innodb和myIsam最大的不同有兩點,一是支持事務,二是支持行級鎖。
3.4.2.什么是MVCC
行鎖沒有顯式的聲明辦法,而是藏在默認實現(xiàn)中,MVCC 是 MySQL InnoDB 存儲引擎的默認并發(fā)控制機制,其采用的就是表鎖。
并發(fā)控制有幾種處理方法,
第一種: 基于鎖的并發(fā)控制,程序員B開始修改數(shù)據(jù)時,給這些數(shù)據(jù)加上鎖,程序員A這時再讀,就發(fā)現(xiàn)讀取不了,處于等待情況,只能等B操作完才能讀數(shù)據(jù),這保證A不會讀到一個不一致的數(shù)據(jù),但是這個會影響程序的運行效率。
第二種:MVCC,每個用戶連接數(shù)據(jù)庫時,看到的都是某一特定時刻的數(shù)據(jù)庫快照,在B的事務沒有提交之前,A始終讀到的是某一特定時刻的數(shù)據(jù)庫快照,不會讀到B事務中的數(shù)據(jù)修改情況,直到B事務提交,才會讀取B的修改內(nèi)容。
MVCC其實就是實現(xiàn)事務的關鍵,后續(xù)會有文章專門深入聊事務的實現(xiàn),此處暫不展開。
3.4.3.mvcc的使用
首先有一個需要糾正的是,很多地方都說mvcc是通過手動提交來觸發(fā)的,這是個誤導,不管手動提交還是自動提交MVCC機制都是生效的,只是手動提交用來觀察mvcc過程更加直觀。
此處為了直觀,我們也以手動提交為例,首先通過set autocommit=0可以關閉自動提交。關閉后每次執(zhí)行sql以后,通過commit命令來手動提交,才會對數(shù)據(jù)庫產(chǎn)生影響,否則只會對當前操作方的數(shù)據(jù)快照有影響。innodb引擎中,其他客戶端想查看到最新的數(shù)據(jù)情況也必須通過commit指令來做一次同步(因為innodb默認隔離級別為可重復讀)。
當一個會話修改某行數(shù)據(jù),未commit前,其他會話對該行數(shù)據(jù)的修改會阻塞掛起,直到先改那個會話commit為止。
3.4.4.間隙鎖
使用范圍條件匹配時,innodb會給符合條件的已有數(shù)據(jù)記錄的索引加“范圍鎖”(范圍鎖是特殊的行鎖),對于鍵值在條件范圍內(nèi)但并不存在的記錄,叫做間隙(GAP),innodb也會對這個間隙加鎖,這種機制叫做“間隙鎖”。
3.4.5.行鎖變表鎖
任何需要全表掃描的情況時,行鎖都會升級為表鎖。
因為MySQL不知道到底該鎖哪行,所以會將整個表都鎖起來,然后再進行全表掃描。
全表掃描的情況無非兩種:
- 沒建索引。
- 索引失效。
總結
到此這篇關于MySQL并發(fā)控制的文章就介紹到這了,更多相關MySQL并發(fā)控制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
MySQL?優(yōu)化?index?merge引起的死鎖分析
這篇文章主要介紹了MySQL?優(yōu)化?index?merge引起的死鎖分析,MySQL通過優(yōu)化索引合并是遇到的死鎖問題,下面具體分析需要的小伙伴可以參考一下2022-04-04mysql數(shù)據(jù)庫操作_高手進階常用的sql命令語句大全
mysql數(shù)據(jù)庫操作sql命令語句大全:三表連表查詢、更新時批量替換字段部分字符、判斷某一張表是否存在、自動增長恢復從1開始、查詢重復記錄、更新時字段值等于原值加上一個字符串、更新某字段為隨機值、復制表數(shù)據(jù)到另一個表、創(chuàng)建表時拷貝其他表的數(shù)據(jù)和結構...2022-11-11在 Windows 10 上安裝 解壓縮版 MySql(推薦)
這篇文章主要介紹了在 Windows 10 上安裝 解壓縮版 MySql(推薦)的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-12-12Mysql 5.6使用配置文件my.ini來設置長時間連接數(shù)據(jù)庫的問題
這篇文章主要介紹了Mysql 5.6使用配置文件my.ini來設置長時間連接數(shù)據(jù)庫,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07MySQL數(shù)據(jù)庫中varchar類型的數(shù)字比較大小的方法
varchar類型的數(shù)據(jù)是不能直接比較大小的,那么MySQL數(shù)據(jù)庫中varchar類型如何進行數(shù)字比較大小的,本文就詳細的介紹一下2021-11-11