浅谈MySQL数据库中的锁与事务
在MySQL中,為了應(yīng)對并發(fā)場景下的讀寫,鎖通常分為兩類:共享鎖以及排他鎖。其中,共享鎖允許多個連接在同一時間并發(fā)的讀取相同的資源,彼此之間互不影響,所以又稱為讀鎖。排他鎖則會阻塞其他嘗試獲取共享鎖或者排他鎖的操作,確保同一時間只有一個連接可以寫入數(shù)據(jù),并禁止其他用戶的讀寫,又稱寫鎖。
在實際使用下,加鎖往往意味著高昂的開銷,MySQL為了平衡鎖的開銷以及并發(fā)的線程之間的安全,采用了兩種不同的鎖策略:
?
表鎖會鎖定整張表,如果當前有用戶正在執(zhí)行寫操作并且獲取了寫鎖,這可能導(dǎo)致整張表被鎖定,阻塞其他用戶的讀寫操作。如果用戶執(zhí)行的是讀操作,則會獲取讀鎖,此時其他用戶的并發(fā)讀操作將被接受,寫操作會被阻塞。
舉個例子,執(zhí)行語句
:
如果b字段不存在索引,那么會鎖住所有的記錄,即鎖上了表鎖。
?
行鎖的粒度是在每一條行數(shù)據(jù),這意味行鎖可以盡可能的支持并發(fā)處理,相應(yīng)的行鎖
開銷也會比較大。并且,在InnoDB中的行鎖是針對索引加的鎖,不是針對記錄加的鎖,并且該索引不能失效,否則行鎖將會自動升級為表鎖。
?
?????? 相比較而言,表鎖的優(yōu)勢在于開銷小,加鎖快,無死鎖,劣勢是鎖的粒度大,發(fā)生鎖沖突的概率較高,并發(fā)能力較弱。而行鎖則相反。實際使用中,兩者都會由MySQL自動加鎖。行鎖沖突可以通過執(zhí)行 show status like 'innodb_row_lock%'語句進行分析,表鎖沖突則可通過執(zhí)行show status like 'table_locks%' 進行查看。
?
?
?
事務(wù)就是一組原子性的sql,要么MySQL引擎會全部執(zhí)行這一組sql語句,要么全部不
行(不允許任何一條失敗)。失敗的語句將導(dǎo)致事務(wù)的整個回滾。事務(wù)系統(tǒng)通常滿足四個特性,分別為原子性(要么全部執(zhí)行、要么全部回滾)、一致性(數(shù)據(jù)必須從一個一致性狀態(tài)轉(zhuǎn)換為另一種一致性狀態(tài))、隔離性(事務(wù)未執(zhí)行成功,其他人無法看到結(jié)果)、持久性(事務(wù)在commit之后,數(shù)據(jù)不會丟失)。
?????? 由上述概念可知,事務(wù)是用來保障數(shù)據(jù)的一致性以及完整性的。也是MySQL中用來平衡效率與安全之間的一種手段,所以,InnoDB引擎下的事務(wù)通常提供了四種事務(wù)的隔離級別,方便用戶自己在效率和安全之間做出權(quán)衡。
?
事務(wù)中的修改,即使該事務(wù)未提交,對其他的事務(wù)也是可見的。可以讀取到其他事務(wù)
中的數(shù)據(jù),又稱為臟讀,在實際數(shù)據(jù)庫事務(wù)中,臟讀會破壞數(shù)據(jù)的一致性,對業(yè)務(wù)產(chǎn)生極大影響,所以一般不推薦采用READ UNCOMMITED作為數(shù)據(jù)庫事務(wù)的隔離級別。
在提交讀級別中,數(shù)據(jù)庫將保證如果一個事務(wù)沒有完全執(zhí)行成功(commit完成),事務(wù)中的操作對其他的事務(wù)是不可見的。在該隔離級別下,雖然杜絕了臟讀的發(fā)生,但是還是存在著不可重復(fù)讀以及幻讀的問題。不可重復(fù)讀發(fā)生在事務(wù)T1讀取了一行數(shù)據(jù),事務(wù)T2接著修改或者刪除了該行數(shù)據(jù)(已提交),當T1事務(wù)再次讀取同一行數(shù)據(jù)的時候,發(fā)現(xiàn)數(shù)據(jù)已經(jīng)被修改或者被刪除。示例如下圖:
幻讀則發(fā)生在事務(wù)T1讀取了滿足某條件的一個數(shù)據(jù)集,事務(wù)T2此時插入了一行或者多行滿足T1查詢條件的的數(shù)據(jù)并提交,當T1再次采用相同的條件進行讀取時,得到了與第一次不同的結(jié)果集。示例如下:
REPEATEABLE READ是MySQL的默認隔離級別,它確保同一個事務(wù)的多個實例在并
發(fā)讀取數(shù)據(jù)時,會看到同樣的數(shù)據(jù)。按照該隔離級別定義,還是會存在幻讀的問題。MySQL通過InnoDB存儲引擎的多版本并發(fā)控制機制(MVCC)解決了部分該問題的場景,但仍然存在,此處后文會另有分析。
?
串行化是最嚴格的隔離級別,通過給事務(wù)中的每次讀寫操作都加鎖,保證了不產(chǎn)生任何
臟讀、不可重復(fù)讀以及幻讀問題,但是隨之引入的是大量的讀超時以及鎖競爭,導(dǎo)致數(shù)據(jù)庫性能的嚴重下降。
?
另外來看看ANSI SQL STANDARD中,對于數(shù)據(jù)庫隔離級別以及相應(yīng)問題的規(guī)定:
所以,對于REPEATABLE READ隔離級別下,是允許出現(xiàn)幻讀的。
?
?
MVCC(multiple-version-concurrency-control)是個行級鎖的變種,它在普通讀情況下避
免了加鎖操作,因此開銷更低。其原理具體為,在InnoDB存儲引擎中,每行數(shù)據(jù)會加入一些隱藏字段DATA_TRX_ID,DATA_ROLL_PTR,DB_ROW_ID,DELETE_BIT。DATA_TRX_ID 字段記錄了數(shù)據(jù)的創(chuàng)建和刪除時間,這個時間指的是對數(shù)據(jù)進行操作的事務(wù)的id, DATA_ROLL_PTR 指向當前數(shù)據(jù)的undo log記錄,回滾數(shù)據(jù)就是通過這個指針,DELETE BIT位用于標識該記錄是否被刪除,這里的不是真正的刪除數(shù)據(jù),而是標志出來的刪除。真正意義的刪除是在mysql進行數(shù)據(jù)的GC,清理歷史版本數(shù)據(jù)的時候。
?????? 相應(yīng)的,其DML的處理方式也發(fā)生了變化:
SELECT語句先查找DATA_TRX_ID早于當前事務(wù)ID的數(shù)據(jù)行。這樣就保證了讀取的數(shù)據(jù)要么是在這個事務(wù)開始之前就已經(jīng)commit了的(早于當前事務(wù)ID),要么是在這個事務(wù)中自身創(chuàng)建的數(shù)據(jù)(等于當前事務(wù)ID)。查找行的DELETE_BIT為1時,查找刪除事務(wù)ID對應(yīng)的事務(wù),確定此條記錄在當前事務(wù)開始之前,行沒有被刪除。
INSERT語句會在新插入行數(shù)據(jù)之后,保存當前事務(wù)ID作為行的DATA_TRX_ID。
DELETE語句為每一條刪除的記錄保存當前的事務(wù)ID作為行的刪除標記。
UPDATE語句將復(fù)制變更的記錄,并把新記錄的DATA_TRX_ID置為當前事務(wù)ID,同時更新老記錄中的DB_ROLL_PT指向了上一個版本。
所以在并發(fā)讀的時候,不需要等到訪問行上的鎖釋放,只需要讀取一個行的快照即可。既然是多版本的讀取,就肯定讀取不到其他事務(wù)中的新插入的數(shù)據(jù)了,也就避免了上述場景中提到的幻讀。
從上述信息我們已經(jīng)知道,在REPEATABLE READ級別下,InnoDB采取多版本策略成功
避免了部分幻讀現(xiàn)象,但是實際使用中,還是會有幻讀產(chǎn)生,先看場景:
通過MVCC,在事務(wù)中的多次讀取不會出現(xiàn)幻讀,但是此時的插入操作依舊會發(fā)生主鍵重復(fù)的錯誤,并且因為MVCC機制,在上圖中的會話1無論讀取多少次都不會讀到導(dǎo)致沖突產(chǎn)生的數(shù)據(jù),確實就如“幻影”一般詭異。
?
?
為了解決上述場景中的幻讀,需要簡單提一下InnoDB的行鎖機制,在InnoDB引擎下存在三種行鎖,分別為:
?
通常情況下,INSERT/UPDATE/DELETE默認會在操作的記錄上加上Next-Key Lock,而
普通的SELECT因為MVCC的關(guān)系反而只需要讀取快照即可,所以如果業(yè)務(wù)需要再REPEATABLE READ場景下保證絕對不產(chǎn)生幻讀,需要手動給SELECT加鎖,在類似SELECT…WHERE加入FOR UPDATE(排它鎖)或者LOCK IN SHARE MODE(共享鎖)。
?
最后,本文只是就網(wǎng)易云信業(yè)務(wù)中數(shù)據(jù)庫的一些使用場景和問題作了總結(jié),數(shù)據(jù)庫的實
際使用還存在諸多學(xué)問,希望能拋磚引玉,讓更多人分享出自己的心得。
邀請好友使用網(wǎng)易云信,好友下單成功即可獲得500元網(wǎng)易考拉/嚴選無門檻現(xiàn)金券,點擊立即推薦>>
網(wǎng)易云信(NeteaseYunXin)是集網(wǎng)易19年IM以及音視頻技術(shù)打造的PaaS服務(wù)產(chǎn)品,來自網(wǎng)易核心技術(shù)架構(gòu)的通信與視頻云服務(wù),穩(wěn)定易用且功能全面,致力于提供全球領(lǐng)先的技術(shù)能力和場景化解決方案。開發(fā)者通過集成客戶端SDK和云端OPEN API,即可快速實現(xiàn)包含IM、音視頻通話、直播、點播、互動白板、短信等功能。
?
總結(jié)
以上是生活随笔為你收集整理的浅谈MySQL数据库中的锁与事务的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 延迟开学不停学|网易云信在线教育解决方案
- 下一篇: linux cmake编译源码,linu