最好科技廣州網(wǎng)站建設(shè)seo排名工具提升流量
目錄
1.原子性和持久性
1.1.手動提交事務(wù)
1.2.自動提交事務(wù)
?1.3.事務(wù)的原理:
2.隔離性
1.讀未提交(Read Uncommitted)
?2.讀提交(Read Committed)
3.可重復(fù)讀
4.串行化
3.一致性
4.理解讀提交和可重復(fù)讀的實現(xiàn)原理區(qū)別
4.1.模擬 MVCC
4.2.read view
4.3.RR 與 RC的本質(zhì)區(qū)別
事務(wù)的4大屬性:原子性,持久性,隔離性,一致性;
在mysql中只有innodb支持事務(wù),myisam是不支持事務(wù)的;
事務(wù)的作用:
- 由于事務(wù)的隔離性,多個用戶同時訪問數(shù)據(jù)庫時不必擔心相互之間的影響,提高并發(fā)性和數(shù)據(jù)安全性;
- 事務(wù)的提交和回滾操作還可以保證數(shù)據(jù)的安全性和完整性;
1.原子性和持久性
1.1.手動提交事務(wù)
1.事務(wù)的基本操作?
- 使用begin或者start transaction,創(chuàng)建事務(wù)
- 中間可以增刪改操作
- 使用commit提交事故?
2.創(chuàng)建保存點和回滾操作
- savepoint? 標記;
- rollback to??標記:回滾到標記位置;rollback回滾到事務(wù)開頭(begin位置)
只要事務(wù)未提交(未commit)就可以使用回滾操作?
1.2.自動提交事務(wù)
查看自動提交事務(wù)狀態(tài):show variables like 'autocommit';
設(shè)置為關(guān)閉:SET AUTOCOMMIT=0;
設(shè)置為開啟:SET AUTOCOMMIT=1;
由下圖可以得出:
- 自動提交事務(wù)是對輸入單條sql語句(不使用手動提交的情況)做自動提交;
- 如果關(guān)閉自動提交,那么單條sql不會被寫入磁盤,保證原子性;?
?1.3.事務(wù)的原理:
原子性:要么做完,要么不做,沒有中間態(tài) ;可以分為3個狀態(tài),執(zhí)行前,執(zhí)行中,執(zhí)行后;
1.事務(wù)的3個狀態(tài)可以為:
- 執(zhí)行前:使用begin從磁盤中把數(shù)據(jù)拿到內(nèi)核緩沖區(qū),再從內(nèi)核緩沖區(qū)拷貝到用戶層的buffer pool中,簡單的說就是把磁盤數(shù)據(jù)保存到內(nèi)存的用戶層的緩沖區(qū);
- 執(zhí)行中:使用sql語句對拿到內(nèi)存中數(shù)據(jù)進行增刪改;
- 執(zhí)行后:使用commit把修改的內(nèi)存數(shù)據(jù),拿到磁盤去;
如何保證的原子性:如果不使用commit交付數(shù)據(jù),事務(wù)結(jié)束后就自動會回滾到執(zhí)行前,使用commit交付數(shù)據(jù)那么就是執(zhí)行后
持久性是什么:就是已經(jīng)交付的數(shù)據(jù)(commit),就會保存在磁盤中且不受回滾的約束
2.隔離性
- MySQL服務(wù)可能會同時被多個客戶端進程(線程)訪問,訪問的方式以事務(wù)方式進行(并發(fā)執(zhí)行)
- 數(shù)據(jù)庫中,為了保證事務(wù)并發(fā)執(zhí)行過程中盡量不受干擾,就有了一個重要特征:隔離性
- 在不同的場景需要不同的隔離級別:有4種,讀未提交、讀提交、可重復(fù)讀、串行化
查看設(shè)置隔離級別:
select @@global.tx_isolation;查看全局隔離級別
select @@session.tx_isolation;查看會話隔離級別
設(shè)置隔離等級格式:set session/global transaction isolation level read uncommitted/read committed/repeatable read/serializable; //全局和會話選一個,隔離等級4選1;
設(shè)置會話隔離等級
1.讀未提交(Read Uncommitted)
在該隔離級別,所有的事務(wù)都可以看到其他事務(wù)沒有提交的執(zhí)行結(jié)果。(實際生產(chǎn)中不可能使用這種隔離級別的),但是相當于沒有任何隔離性,也會有很多并發(fā)問題;(臟讀,幻讀,不可重復(fù)讀);
臟讀:一個事務(wù)進行增刪改,另一個正在執(zhí)行的事務(wù)可以讀取到未提交(commit)的數(shù)據(jù);?
臟讀的問題舉例:要對查詢到表的多條記錄發(fā)獎勵,另外一個事務(wù)新增了一行記錄且沒有提交,那么獎勵多發(fā)一個;?
?2.讀提交(Read Committed)
該隔離級別是大多數(shù)數(shù)據(jù)庫的默認的隔離級別(不是 MySQL 默認的)。它滿足了隔離的簡單定義:一個事務(wù)只能看到其他的已經(jīng)提交的事務(wù)所做的改變,有不可重復(fù)讀的問題
不可重復(fù)讀:多個事務(wù)并發(fā)執(zhí)行,一個事務(wù)增/刪/改一條數(shù)據(jù)且提交,其他的正在執(zhí)行的事務(wù)就可以看到這條數(shù)據(jù);
不可重復(fù)的問題舉例:我們按分數(shù)發(fā)獎勵,100分發(fā)100塊錢,90分發(fā)50塊錢,80分發(fā)30塊,小明得了100分,在比較100分的時候給他發(fā)100塊,在比較90分的時候入過另一個事務(wù)把小明改為90分并且提交,那么小明又可以得到50塊,那么就出問題了
?
3.可重復(fù)讀
概念:這是MySQL默認的隔離級別,它確保同一個事務(wù),在執(zhí)行中,多次讀取操作數(shù)據(jù)時,會看到同樣的數(shù)據(jù)行。但是一般數(shù)據(jù)庫會有幻讀問題,mysql沒有這個問題被解決了
幻讀:這一系列隔離都是加鎖完成的,但是新增的數(shù)據(jù)沒到磁盤一般鎖不能限制,所以其他事務(wù)插入,查詢時會看到新增數(shù)據(jù),mysql是使用Next-Key鎖(GAP+行鎖)解決的;
?
4.串行化
?概念:: 這是事務(wù)的最高隔離級別,它通過強制事務(wù)排序,使之不可能相互沖突,從而解決了 幻讀的問題。它在每個讀的數(shù)據(jù)行上面加上共享鎖。但是可能會導(dǎo)致超時和鎖競爭(這種隔離級別太極端, 實際生產(chǎn)基本不使用)
3.一致性
例如轉(zhuǎn)賬問題A有1000¥,B有800¥,A給B轉(zhuǎn)賬200¥,A有800¥B擁有1000¥;
一致性的概念:事務(wù)執(zhí)行的結(jié)果,必須使數(shù)據(jù)庫從一個一致性狀態(tài),變到另一個一致性狀態(tài)。A有1000B有800是一個一致性狀態(tài),A有800B有1000是另一個一致性狀態(tài);
如何保證轉(zhuǎn)賬成功呢,防止A轉(zhuǎn)賬了,B沒有收到了,
- 有回滾和提交這種方法(原子性),保護已經(jīng)修改的數(shù)據(jù)不會回滾(持久性),并發(fā)執(zhí)行不受影響(隔離性);
- 用戶對A和B的數(shù)據(jù)修改
由上可得:一致性并沒有具體的實例,是由用戶和mysql的特性來共同來維護的概念
4.理解讀提交和可重復(fù)讀的實現(xiàn)原理區(qū)別
數(shù)據(jù)庫并發(fā)的場景有三種:
- 讀讀:不存在任何問題,也不需要并發(fā)控制;
- 讀寫:有線程安全問題,可能會造成事務(wù)隔離性問題,可能遇到臟讀,幻讀,不可重復(fù)讀
- 寫寫:有線程安全問題,可能會存在更新丟失問題
讀寫
多版本并發(fā)控制( MVCC )是一種用來解決 讀-寫沖突 的無鎖并發(fā)控制
4個記錄隱藏列字段:
- DB_TRX_ID :6 byte,最近修改( 修改/插入 )事務(wù)ID,記錄創(chuàng)建這條記錄/最后一次修改該記錄的事務(wù)ID,識別不同事務(wù)身份;
- DB_ROLL_PTR : 7 byte,回滾指針,指向這條記錄的上一個版本(簡單理解成,指向歷史版本就行,這些數(shù)據(jù)一 般在 undo log 中)
- DB_ROW_ID : 6 byte,隱含的自增ID(隱藏主鍵),如果數(shù)據(jù)表沒有主鍵, InnoDB 會自動以 DB_ROW_ID 產(chǎn)生一 個聚簇索引
- 刪除flag隱藏字段, 既記錄被更新或刪除并不代表真的刪除,而是刪除flag變了,flag=0未刪,flag=1已刪除(內(nèi)存)
加上隱藏列:?
undo日志
- 是一個內(nèi)存緩沖區(qū),用來保存日志數(shù)據(jù);
4.1.模擬 MVCC
修改張三這行記錄的過程
- 事務(wù)10,因為要修改,所以要先給該記錄加行鎖。
- 修改前,現(xiàn)將改行記錄拷貝到undo log中,所以,undo log中就有了一行副本數(shù)據(jù)。(原理就是寫時拷貝)
- 所以現(xiàn)在 MySQL 中有兩行同樣的記錄。現(xiàn)在修改原始記錄中的name,改成 '李四'。并且修改原始記錄的隱藏字段 DB_TRX_ID 為當前 事務(wù)10 的ID, 我們默認從 10 開始,之后遞增。而原始記錄的回滾指針 DB_ROLL_PTR 列, 里面寫入undo log中副本數(shù)據(jù)的地址,從而指向副本記錄,既表示我的上一個版本就是它
- 事務(wù)10提交,釋放鎖。
現(xiàn)在有一個事務(wù)11,要對當前記錄的age修改為38;
?這樣,我們就有了一個基于鏈表記錄的歷史版本鏈。所謂的回滾,無非就是用歷史數(shù)據(jù),覆蓋當前數(shù)據(jù)。
- 上面的一個個版本,我們可以稱之為一個個的快照。
- 當前讀:讀取最新的記錄,就是當前讀。增刪改,都叫做當前讀,select也有可能當前讀,比如:select lock in share mode(共享鎖), select for update
- 快照讀:讀取歷史版本(一般而言),就叫做快照讀。
4.2.read view
read view是一個類(mysql1使用c++實現(xiàn)),下面是它的一部分;
class ReadView {
// 省略...
private:/** 高水位,大于等于這個ID的事務(wù)均不可見*/
trx_id_t m_low_limit_id/** 低水位:小于這個ID的事務(wù)均可見 */
trx_id_t m_up_limit_id;/** 創(chuàng)建該 Read View 的事務(wù)ID*/
trx_id_t m_creator_trx_id;/** 創(chuàng)建視圖時的活躍事務(wù)id列表*/
ids_t m_ids;
//省略...
};
- 無論是讀提交還是可重復(fù)讀都遵守下面的概念;它們兩的區(qū)別不在這里,所以不要想著它們的概念來理解下面的圖,反而更難理解
- 由read view來決定可不可讀,形成快照后分為3部分,第一部分都可讀,第二部分提交的就可讀,第三部分都不可讀;
- 第一部分:creator_limit_id==DB_TRX_ID就是自己的事務(wù)id當然可讀,DB_TRX_ID<up_limit_id說明不在這個m_ids(活躍事務(wù)id列表)范圍之內(nèi),而且因為事務(wù)id自增長的,比最小活躍事務(wù)還小說明它以前已經(jīng)提交的;
- 第二部分:up_limit_id到low_limit_id之間;不就是m_ids里面的活躍事務(wù)嗎;那么已經(jīng)提交的可讀,未提交不可讀(這里不是不可重復(fù)讀問題,讀提交和可重復(fù)讀都支持read view來決定可不可讀概念,下面說明它們的區(qū)別是讀提交每次快照讀就會生成一個新的read view,可重復(fù)讀快照讀生成一個read view就不會再生成了);
- 第三部分:DB_DRX_ID>=low_limit_id,比m_ids的所以活躍事務(wù)都大,那么是不可讀的;
4.3.RR 與 RC的本質(zhì)區(qū)別
可重復(fù)讀隔離等級下
?
- ?用例1與用例2:唯一區(qū)別僅僅是 表1 的事務(wù)B在事務(wù)A修改age前 快照讀 過一次age數(shù)據(jù)
- 而表2的事務(wù)B在事務(wù)A修改age前沒有進行過快照讀。
RR 與 RC的本質(zhì)區(qū)別:
- 正是Read View生成時機的不同,從而造成RC,RR級別下快照讀的結(jié)果的不同
- 在RR級別下的某個事務(wù)的對某條記錄的第一次快照讀會創(chuàng)建一個快照及Read View, 將當前系統(tǒng)活躍的其他事務(wù)記錄起來(m_ids)
- RR級別此后在調(diào)用快照讀的時候,還是使用的是同一個Read View,所以只要當前事務(wù)在其他事務(wù)提交更新之前使用(活躍事務(wù))過快照讀,那么之后的快照讀使用的都是同一個Read View,所以對之后的修改(第三部分)不可見;
- 而在RC級別下的,事務(wù)中,每次快照讀都會新生成一個快照和Read View, 這就是我們在RC級別下的事務(wù)中可以 看到別的事務(wù)提交的更新的原因
- 在RC隔離級別下,是每個快照讀都會生成并獲取最新的Read View;而在RR隔離級別下,則是同一個事務(wù) 中的第一個快照讀才會創(chuàng)建Read View, 之后的快照讀獲取的都是同一個Read View。
- 正是RC每次快照讀,都會形成Read View,所以,RC才會有不可重復(fù)讀問題。