MySQL學(xué)習(xí)之事務(wù)與并發(fā)控制
事務(wù)
概念
一個(gè)事務(wù)可以理解為一組操作,這一組操作要么全部執(zhí)行,要么全部不執(zhí)行。
特性
原子性
一個(gè)事務(wù)是一個(gè)獨(dú)立的原子單元,一個(gè)事務(wù)內(nèi)所有的操作,要么全部執(zhí)行,要么全部不執(zhí)行。關(guān)注的是一組操作的執(zhí)行結(jié)果(全部成功or全部失敗)。是通過(guò)undo log實(shí)現(xiàn)的。
一致性
看了網(wǎng)上很多博客對(duì)一致性的講解,總覺(jué)得沒(méi)有說(shuō)到點(diǎn)子上,我就從我的個(gè)人角度來(lái)說(shuō)說(shuō)對(duì)一致性的理解:
一個(gè)事務(wù)使得數(shù)據(jù)庫(kù)從一個(gè)狀態(tài)A0,轉(zhuǎn)換到另一個(gè)狀態(tài)A1。相鄰的兩個(gè)狀態(tài)轉(zhuǎn)換之間,只能有一個(gè)事務(wù)起作用。如果當(dāng)前狀態(tài)A0轉(zhuǎn)換到下一個(gè)狀態(tài)A1,之間包括了2個(gè)事務(wù),則說(shuō)明有一個(gè)事務(wù)的effect被覆蓋了。如事務(wù)并發(fā)問(wèn)題中的丟失更新,典型的例子是轉(zhuǎn)賬問(wèn)題,A向B轉(zhuǎn)賬,同時(shí)C也向B轉(zhuǎn)賬,最后會(huì)發(fā)現(xiàn)A或者C轉(zhuǎn)過(guò)去的錢(qián)不見(jiàn)了,這種例子隨便一搜就有,不贅述。用轉(zhuǎn)賬問(wèn)題來(lái)解釋一致性,比較好理解,因?yàn)殄X(qián)的總數(shù)應(yīng)該是確定的,比如B本來(lái)有1000,A向B轉(zhuǎn)100,C向B轉(zhuǎn)200,那么B賬戶(hù)上最后應(yīng)該有1300,然而最終我們可能看到是1100或1200,因?yàn)橛幸粋€(gè)人的操作被覆蓋掉了。其實(shí)這也就是說(shuō),B賬戶(hù)的錢(qián),從一個(gè)狀態(tài),到另一個(gè)狀態(tài),中間有2個(gè)事務(wù)起了effect,這樣是不對(duì)的。事務(wù)具有隔離性,數(shù)據(jù)庫(kù)的每一個(gè)狀態(tài),應(yīng)該都有且僅有一個(gè)與之對(duì)應(yīng)的事務(wù)在take effect。
隔離性
不同的事務(wù)之間,應(yīng)該是不能互相影響的。
4種隔離級(jí)別
- Read Uncommit
- Read Commit
- Repetable Read
- Serializable
RU相當(dāng)于沒(méi)有隔離,RC和RR是用MVCC實(shí)現(xiàn)(具體是通過(guò)undo log 和 read view),Serializable是用鎖實(shí)現(xiàn),事務(wù)串行執(zhí)行。
查看當(dāng)前的隔離級(jí)別
select @@tx_isolation; -- mysql 8的變量名變?yōu)槿缦碌男问? select @@transaction_isolation; -- 設(shè)置隔離級(jí)別 set transacation_isolation = '隔離級(jí)別名'
數(shù)據(jù)庫(kù)隔離級(jí)別是一種需求,不同的隔離級(jí)別有對(duì)應(yīng)的實(shí)現(xiàn)方式。
持久性
事務(wù)執(zhí)行成功后(提交后),對(duì)數(shù)據(jù)庫(kù)的更改是永久性的(即寫(xiě)到磁盤(pán))。是通過(guò)redo log + Force Log at Commit機(jī)制實(shí)現(xiàn)的
事務(wù)并發(fā)問(wèn)題
丟失更新
第一類(lèi)丟失更新
針對(duì)同一行數(shù)據(jù),事務(wù)A先開(kāi)始,事務(wù)B后開(kāi)始,事務(wù)B提交,隨后事務(wù)A回滾,則回滾會(huì)導(dǎo)致將事務(wù)B已提交的修改給覆蓋掉。這個(gè)問(wèn)題在現(xiàn)在的數(shù)據(jù)庫(kù)軟件中已不會(huì)產(chǎn)生
第二類(lèi)丟失更新
針對(duì)同一行數(shù)據(jù),事務(wù)A先開(kāi)始,事務(wù)B后開(kāi)始,事務(wù)A提交,隨后事務(wù)B提交,則事務(wù)B將事務(wù)A的修改給覆蓋掉了
臟讀
事務(wù)A讀到了事務(wù)B未提交的數(shù)據(jù),事務(wù)B后回滾,則事務(wù)A讀到的是臟數(shù)據(jù)
不可重復(fù)讀
事務(wù)A連續(xù)2次讀一行記錄,讀取到的是不一樣的。這是由于A連續(xù)2次讀的中間,事務(wù)B對(duì)這行記錄做了更新。
幻讀
表現(xiàn)為兩次讀取的數(shù)據(jù)數(shù)量不一致,發(fā)現(xiàn)變多了,或者變少了。
比如我讀取age > 10 的學(xué)生數(shù)據(jù),第一次讀發(fā)現(xiàn)有10個(gè)學(xué)生,第二次讀發(fā)現(xiàn)有20個(gè)學(xué)生,就好像出現(xiàn)了幻覺(jué)一樣,同樣的查詢(xún)條件,兩次讀取發(fā)現(xiàn)有學(xué)生增加或減少。
事務(wù)命令
MySQL命令行下默認(rèn)是autocommit的,即事務(wù)會(huì)自動(dòng)提交。要顯示開(kāi)啟一個(gè)事務(wù),需使用命令
BEGIN
或START TRANSACTION
BEGIN
或START TRANSACTION
開(kāi)啟事務(wù)COMMIT
提交事務(wù)ROLLBACK
回滾事務(wù)
并發(fā)控制
兩種并發(fā)控制策略
MVCC
Multi-Version Concurrency Control
核心理念是快照,InnoDB主要通過(guò)undo log 和 read view來(lái)實(shí)現(xiàn)MVCC。
**讀不加鎖,讀寫(xiě)不互斥。**讀會(huì)從多個(gè)版本的數(shù)據(jù)中挑選一個(gè)合適的版本返回。寫(xiě)操作會(huì)產(chǎn)生一個(gè)新的版本。
每一行的記錄,會(huì)包含3個(gè)隱藏字段:row_id,tx_id,roll_ptr
其中tx_id表示最近操作該行記錄的事務(wù)id,roll_ptr則是回滾指針,指向一條undo log記錄,即指向該次改動(dòng)之前的數(shù)據(jù)
undo log
insert undo log
由insert操作產(chǎn)生,可在事務(wù)提交后直接刪除。因?yàn)閕nsert操作只對(duì)當(dāng)前事務(wù)本身可見(jiàn),其他事務(wù)不可見(jiàn)
update undo log
由update/delete產(chǎn)生。是對(duì)已有記錄的修改,為了提供MVCC機(jī)制,該undo log不能在事務(wù)提交后就刪除,而需要等待purge線(xiàn)程來(lái)進(jìn)行最后的刪除
使用update修改當(dāng)前行時(shí),首先用X鎖鎖定,然后將該行當(dāng)前值復(fù)制到undo log,然后再執(zhí)行修改,最后填寫(xiě)事務(wù)id,并使回滾指針指向undo log中修改前的行
read view
用于判斷數(shù)據(jù)可見(jiàn)性的一個(gè)數(shù)據(jù)結(jié)構(gòu),里面存儲(chǔ)了
- 當(dāng)前活躍事務(wù)的最小id:min_id
- 當(dāng)前活躍事務(wù)的最大id:max_id
- 當(dāng)前活躍事務(wù)id list:ids
若讀取到的某一行的某個(gè)版本tx_id < min_id,則說(shuō)明此行的該版本在本次事務(wù)開(kāi)啟之前就已經(jīng)提交,故這個(gè)數(shù)據(jù)對(duì)本次事務(wù)可見(jiàn)。
若讀取到的某一行的某個(gè)版本tx_id >= max_id,說(shuō)明此行的該版本在本次事務(wù)開(kāi)啟之后才開(kāi)始進(jìn)行修改,故這個(gè)數(shù)據(jù)對(duì)本次事務(wù)不可見(jiàn)。
若讀取到的某一行的某個(gè)版本tx_id在min_id和max_id之間,則判斷此行的tx_id是否在ids內(nèi),若是,表明此行的事務(wù)還在活躍中,此行數(shù)據(jù)不可見(jiàn),否則,說(shuō)明此行的事務(wù)已經(jīng)提交,此行數(shù)據(jù)可見(jiàn)
簡(jiǎn)單來(lái)說(shuō),若在某一時(shí)刻開(kāi)啟了一個(gè)事務(wù)A,則會(huì)記錄下事務(wù)A開(kāi)啟時(shí),還活躍著的其他事務(wù)(記下這些活躍事務(wù)的id,保存為一個(gè)set,比如叫ids),這些事務(wù)按照開(kāi)始的時(shí)間先后,會(huì)有從小到大的事務(wù)id(tx_id),tx_id小的事務(wù),說(shuō)明是先開(kāi)啟的,tx_id大的事務(wù),說(shuō)明是后開(kāi)啟的。若在事務(wù)A中,讀取到某一行數(shù)據(jù),這一行數(shù)據(jù)的tx_id小于ids中最小的id(min_id),說(shuō)明這一行數(shù)據(jù)對(duì)應(yīng)的事務(wù),已提交過(guò)了(已不活躍了),這一行數(shù)據(jù)的修改已經(jīng)持久化,故該行數(shù)據(jù)對(duì)事務(wù)A來(lái)說(shuō)是可見(jiàn)的。若這一行數(shù)據(jù)的tx_id大于或等于ids中的最大id,說(shuō)明有一個(gè)事務(wù),在事務(wù)A開(kāi)始之后,才開(kāi)始對(duì)這一行數(shù)據(jù)進(jìn)行修改,故該行數(shù)據(jù)對(duì)事務(wù)A不可見(jiàn)。若這一行數(shù)據(jù)的tx_id,在min_id和max_id之間,那么就判斷這個(gè)tx_id是不是在ids中,即對(duì)這行數(shù)據(jù)進(jìn)行修改的那個(gè)事務(wù),還在不在活躍的事務(wù)列表中,若在,說(shuō)明修改這行數(shù)據(jù)的事務(wù)還沒(méi)提交,這行數(shù)據(jù)還沒(méi)持久化,故不可見(jiàn),反之,說(shuō)明這行數(shù)據(jù)的修改已經(jīng)持久化,故可見(jiàn)。
RC隔離級(jí)別下,在一個(gè)事務(wù)中,每次讀取數(shù)據(jù)都會(huì)新建一個(gè)ReadView。所以可能會(huì)產(chǎn)生不可重復(fù)讀的問(wèn)題,因?yàn)樵趦纱巫x之間,有其他事務(wù)對(duì)數(shù)據(jù)進(jìn)行了修改,而兩次讀時(shí)都新建了ReadView,故第二次讀的時(shí)候,修改后的數(shù)據(jù)是可見(jiàn)的。
RR隔離級(jí)別下,在一個(gè)事務(wù)中,第一次讀取時(shí)會(huì)新建一個(gè)ReadView,后序讀取都使用這個(gè)ReadView。所以哪怕在兩次讀之間,有其他事務(wù)修改了數(shù)據(jù),也不會(huì)產(chǎn)生不可重復(fù)讀的問(wèn)題。因?yàn)榈诙巫x,并沒(méi)有新建ReadView,而是使用了一開(kāi)始創(chuàng)建的那個(gè)ReadView,所以數(shù)據(jù)可見(jiàn)性和第一次是一樣的。
MVCC中,讀操作分為兩類(lèi):快照讀,當(dāng)前讀
- 快照讀(一致性非鎖定讀)
讀取的時(shí)記錄的可見(jiàn)版本(可能是歷史版本),不加鎖。
當(dāng)某一行被一個(gè)事務(wù)A加了X鎖時(shí),另一個(gè)事務(wù)B仍然可以讀取該行,只不過(guò)讀取的是歷史版本。
-- 簡(jiǎn)單select SELECT * FROM product;
- 當(dāng)前讀
讀取的是記錄的最新版本,當(dāng)前讀返回的記錄,會(huì)加鎖,保證了其他并發(fā)事務(wù)不能修改當(dāng)前記錄
SELECT * FROM product lock in share mode; SELECT * FROM product for update; insert .... update .... delete ....
LBCC
LCC
Lock-Based Concurrency Control
讀加讀鎖,寫(xiě)加寫(xiě)鎖。讀讀不互斥,讀寫(xiě),寫(xiě)寫(xiě)互斥。Serilizable的隔離級(jí)別是通過(guò)LBCC實(shí)現(xiàn)的
以上就是MySQL學(xué)習(xí)之事務(wù)與并發(fā)控制的詳細(xì)內(nèi)容,更多關(guān)于MySQL事務(wù)與并發(fā)控制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mysql load data infile 的用法(40w數(shù)據(jù) 用了3-5秒導(dǎo)進(jìn)mysql)
測(cè)試數(shù)據(jù)的時(shí)候,生成txt文件應(yīng)該快點(diǎn),再用這種方式導(dǎo)入到mysql 速度上快點(diǎn)。40w數(shù)據(jù) 用了3-5秒導(dǎo)進(jìn)mysql,牛逼毀了2013-01-01mysql5.7.19 winx64安裝配置方法圖文教程(win10)
這篇文章主要為大家詳細(xì)介紹了mysql5.7.19 winx64安裝配置教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Mysql查看數(shù)據(jù)庫(kù)時(shí)區(qū)并設(shè)置時(shí)區(qū)的方法
這篇文章主要介紹了Mysql查看數(shù)據(jù)庫(kù)時(shí)區(qū)并設(shè)置時(shí)區(qū)的方法,設(shè)置時(shí)區(qū)的方式可以通過(guò)mysql命令行模式下動(dòng)態(tài)修改以及通過(guò)修改配置文件來(lái)修改時(shí)區(qū),需要的朋友可以參考下2024-02-02教你如何在Mac上安裝mysql數(shù)據(jù)庫(kù)
本文給大家詳細(xì)介紹了如何在Mac上安裝mysql數(shù)據(jù)庫(kù)的方法,非常的細(xì)致,有需要的小伙伴可以參考下2018-05-05mysql內(nèi)連接,連續(xù)兩次使用同一張表,自連接方式
這篇文章主要介紹了mysql內(nèi)連接,連續(xù)兩次使用同一張表,自連接方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12MySQL使用命令備份和還原數(shù)據(jù)庫(kù)
這篇文章主要介紹了MySQL使用命令備份和還原數(shù)據(jù)庫(kù),本文使用Mysql內(nèi)置命令實(shí)現(xiàn)備份和還原,比較簡(jiǎn)單,需要的朋友可以參考下2015-01-01解決MySQL報(bào)錯(cuò)Error 3948 (42000): Loading loc
在執(zhí)行MySQL項(xiàng)目過(guò)程中意外出現(xiàn)的報(bào)錯(cuò),之前也沒(méi)有遇到過(guò),報(bào)錯(cuò)信息如下,Error 3948 (42000): Loading local data is disabled; this must be enabled on both the client an,本文小編就給大家介紹一下解決報(bào)錯(cuò)的方法,需要的朋友可以參考下2023-09-09Mysql中文漢字轉(zhuǎn)拼音的實(shí)現(xiàn)(每個(gè)漢字轉(zhuǎn)換全拼)
這篇文章主要介紹了Mysql中文漢字轉(zhuǎn)拼音的實(shí)現(xiàn),并且每個(gè)漢字會(huì)轉(zhuǎn)換全拼,使用Mysql自定義函數(shù)實(shí)現(xiàn),需要的朋友可以參考下2014-06-06