日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Mysql中的事务详解

發布時間:2024/3/12 数据库 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Mysql中的事务详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

什么是事務

顧名思義,事務就是對一組事情的操作,要么把這件事辦成了,要么這事兒就失敗了;通俗來講,事務就是一組sql語句的集合,要么這組sql全都執行成功,要么就全都執行失敗;事務不是Mysql支持的,是InnoDB搜索引擎提供的。

描述事務最經典的例子就是賬戶轉錢,A要給B轉1000元錢操作如下:
1、檢查A賬戶中余額是否高于1000元錢。
2、從A賬戶中減去1000元錢。
3、給B賬戶中增加1000元錢。
以上三個操作必須打包在一個事務當中,該事務要么成功(A賬戶減少1000,B賬戶增加1000),要么失敗(A,B賬戶余額不變),不應該出現第三種現象。

所以這也是引入事務的原因:事務會把數據庫從一種一致狀態轉換為另一種一致狀態,在數據庫提交工作時,可以確保要么所有修改都已經保存了,要么所有修改都不會保存。

事務的特性

InnoDB存儲引擎中的事務完全符合ACID的特性,ACID是以下四個單詞的縮寫:
原子性(atomicity)
一致性(consistency)
隔離性(isolation)
持久性(durability)

原子性

原子是自然界非常小的單位,我們可以看成它是不可再分的,同時它也是事務的一個特征,任何一個事務都可以想象成一個原子,表示其不可再分。只有事務中所有的數據庫操作都執行成功,才算整個事務成功,事務中任何一個sql語句執行失敗,已經執行成功的sql語句也必須撤銷,數據庫狀態應該退回到執行事務前的狀態。

注意:如果事務中的操作都是只讀的,保持原子性比較簡單,一發生錯誤,要么重試,要么返回錯誤代碼即可,如果當前事務中存在插入或者更新操作,一旦失敗,就會引起數據狀態的變化,因此要保護系統并發用戶訪問受影響的部分數據。

一致性

指數據庫中數據在事務操作前和操作后都必須滿足業務規則約束,也就是A、B賬戶的總金額在轉賬前后必須一致,二者的總金額加起來不能多也不能少,如果有不一致,則必須是短暫的,且只有在事務提交前才會出現的。

再舉一個例子,在表中有一個字段為姓名,是唯一的約束,即在表中姓名不能重復,如果有一個事務對姓名字段進行了修改,在事務提交后,表中的姓名變得非唯一了,這就破壞了事務一致性的要求,因為事務將數據庫從一種狀態變成了一種不一致的狀態。

隔離性

隔離性還有其他稱呼,比如并發控制、可串行化、鎖等。通常來說,一個事務所做的修改在最終提交以前,對其他事務是不可見的。在轉賬的例子中,A向B轉賬時,C同時向A轉賬,如果同時進行,則A和B之間的一致行則不能滿足,所以,當A和B執行事務的過程中,其他事務是不能訪問或修改當前相關的數值。

持久性

一旦事務提交,其所做的修改就會永久保存到數據庫中,此時即使系統崩潰,修改的數據也不會丟失。

注意:只能從事務本身的角度來保證結果是持久性,當事務提交后,所有的變化都是永久的,即使數據庫崩潰需要恢復時,也可以保證恢復后的數據都不會丟失,但是如果不是數據庫本身發生了問題,而是一些外部的原因,比如物理因素,自然災害導致數據庫服務器爆炸,那所有的數據可能都會丟失,因此持久性保證系統的高可靠性(High Reliability),而不是高可用性(High Availability)。

可靠性:指的是服務連續無故障運行的時間,無故障運行時間越長,可靠性就越高
可用性:在一段時間內,正常工作時間/這段時間=可用性。
一個服務器可以正常工作十年沒有問題,則它的可靠性為十年,但是如果服務器損壞,需要一年來維護,則可用性就為90%。

事務的分類

從事務理論的角度來說,可以將事務分為以下幾種類型:

扁平事務(Flat Transactions)
帶有保存點的扁平事務(Flat Transactions With Savepoints)
鏈事務(Chained Transactions)
嵌套事務(Nested Transactions)
分布式事務(Distributed Transactions)

扁平事務

扁平事務是事務類型中最簡單最常用的一種事務,在扁平事務中,所有的操作都處于同一層次,事務開始到提交(回滾)之間操作是原子性的,要么都執行,要么都回滾。扁平事務是應用程序成為原子操作的基本組成模塊。下圖展示了扁平事務的三種結果:

扁平事務就是咱們平常說的事務,如果執行的操作有誤,就會全部回滾。現在假設當前扁平事務有1000條sql,但執行到最后一條sql時失敗了,這時就會全部回滾,即使前999條sql執行成功也會進行回滾,這樣代價太大了,于是就有了帶保存點的扁平事務。

帶有保存點的扁平事務

帶有保存點的扁平事務和扁平事務的區別就是有多個保存點,保存點用來通知系統應該記住事務當前的狀態,以便之后發生錯誤時,事務能回到保存點當時的狀態,這就解決了扁平事務全部回滾代價大的缺點。(為什么說這兩個事務的區別是帶有保存點的扁平事務有多個保存點,其實扁平事務也隱式的設置了一個保存點,這個保存點只存在于事務開始時的位置,如果出現問題,直接回到起點)下面是帶有保存點的扁平事務使用:

上圖灰色部分表示已經被回滾過的沒有執行的事務。當用BEGIN WORK 開啟一個事務的時候,隱式的包含了一個保存點,當事務通過ROLLBACK WORK:2 命令回滾時,事務就會回到 SAVE WORK :2,然后繼續執行,執行到ROLLBACK WORK:7再次回滾,回滾到SAVE WORK 7 處,再繼續執行,直到事務提交完畢,除了灰色部分,其他操作都已經被提交。

注意:保存點是遞增的,執行完了保存點2之后,下一個保存點是3,然后是4,即使3,4被回滾了,下一個保存點依然從4之后開始,也就是5,并不會重新從3開始。

鏈事務

上面說到了帶有保存點的扁平事務,在執行該類型事務時,如果系統發生了崩潰,則所有的保存點都會消失,因為保存點是易失的(volatile),而非持久的(persistent),也就是當進行恢復時,事務還是要從開始處重新執行,而不會從最近的一個保存點繼續執行。
于是就有了鏈式事務:在提交一個事務時,緊接著將上下文處理傳遞給下一個要開始的事務,也就是當前提交事務的操作和開始下一個事務的操作將合并成為一個原子操作。這意味著下一個事務將看到上一個事務的結果,就好像在一個事務中進行的一樣。下圖展示了鏈式事務的工作方式:

鏈式事務與帶有保存點的扁平事務不同的是,帶有保存點的扁平事務可以回滾到任意正確的保存點,而鏈式事務只能回滾到最近的一個保存點,并且回滾只限于當前事務,就像是帶有保存點的扁平事務的當前保存點之前,上一個保存點之后為一個事務。然后這些被分割的事務通過觸發器進行連接成為鏈事務。

嵌套事務

嵌套事務是一個層次結構的框架,由一個頂層事務(top-level transaction)控制著各個層次的事務,頂層事務之下嵌套的事務被成為子事務(subtransaction),其控制每一個局部的變化,嵌套事務結構層次如下:

在嵌套事務中,實際的工作都是葉子節點來完成的,即只有葉子節點的事務才能訪問數據庫、發送消息、獲取其他類型的數據。高層事務僅負責邏輯控制,決定何時調用相關的的子事務。
對嵌套事務的定義:
1、嵌套事務是由若干事務組成的一棵樹,子事務既可以是嵌套事務,也可以是扁平事務。
2、處在葉子節點的事務是扁平事務,但是每個子事務從根到葉子節點的距離可以是不相同的。
3、位于根節點的事務成為頂層事務,其他事務成為子事務,事務的前驅(predecessor)為父事務(parent),事務的下一層稱為兒子事務(child)。
4、子事務既可以提交也可以回滾,但是它的提交操作并不馬上生效,除非其父事務已經提交,所以子事務都是在頂層事務提交之后才會真正的提交。
5、樹中的任意一個事務的回滾會引起它的所有子事務一同回滾,故子事務僅保留ACI特性,不具有D的特性。

注意:對于InnoDB存儲引擎來說,是支持扁平事務、帶有保存點的扁平事務、鏈式事務、分布式事務的,并不原生支持嵌套事務。但可以通過帶有保存點的事務來模擬串行的嵌套事務。

分布式事務

分布式事務通常是一個在分布式環境下運行的扁平事務,因此需要根據數據所在位置訪問網絡中不同的節點。
還是拿上面轉賬的例子來說,用戶在ATM機處進行轉賬操作,將招商銀行的錢轉賬到工商銀行,ATM機視為節點A,招商銀行后臺的數據庫視為節點B,工商銀行的后臺數據庫視為節點C。這里就需要使用分布式事務,因為節點A不能通過調用一臺數據庫就能完成任務,需要訪問網絡中兩個節點的數據庫,每個節點的數據庫執行的事務是扁平事務。

事務的實現

事務的隔離性通過鎖來實現,原子性、一致性、持久性通過數據庫的redo log 和 undo log 來完成。redo log 稱為重做日志,用來保證事務的原子性和持久性,undo log 用來保證事務的一致性。

實現隔離性

隔離性是通過鎖來進行實現的,參考博客:https://blog.csdn.net/aaaPostcard/article/details/118961910

實現原子性

要想保證事務的原子性,就需要在執行發生異常時,對已經執行的操作進行回滾。在InnoDB中,恢復機制是通過回滾日志(undo log)來實現的,所有事務進行的修改都會先記錄到這個回滾日志中。
那么是如何通過undo log來實現回滾的呢?當我們對數據庫進行修改時,InnoDB存儲引擎不但會產生redo log 還會產生 undo log,如果用戶執行的事務或者語句由于某種原因失敗了,可以利用ROLLBACK語句進行回滾,這個回滾操作就是利用undo log中的信息,所以 undo log 中存放的是表之前的記錄。通過undo log 就可以保證該事務中某個操作失敗的話就全部回滾,使這整個事務失敗,來保證原子性。

回滾日志除了能夠在發生錯誤或者用戶執行 ROLLBACK 時提供回滾相關的信息,它還能夠在整個系統發生崩潰、數據庫進程直接被殺死后,當用戶再次啟動數據庫進程時,還能夠立刻通過查詢回滾日志將之前未完成的事務進行回滾,這也就需要回滾日志必須先于數據持久化到磁盤上,是我們需要先寫日志后寫數據庫的主要原因。

undo log 簡介

undo log是邏輯日志,并不能將數據庫物理地恢復到執行語句或事務之前的樣子,這是因為在多用戶并發系統中,可能會有上千個并發事務,如果一個事務在修改當前一個頁中某幾條記錄,同時還有別的事務在對同一個頁中另幾條記錄進行修改,就不能將這個頁回滾到事務開始的樣子,這樣會影響其他的事務。
例如:用戶執行了一個INSERT 10w條記錄的事務,這個事務會導致分配了一個新的段,即表空間會增大,當再執行ROLLBACK時,會將插入的事務進行回滾,但是表空間不可能再縮小,物理上不會改變。因此,當InnoDB存儲引擎發生回滾時,它實際做的是與先前相反的工作,對于每個INSERT,InnoDB存儲引擎會完成一個DELETE(只限于事務提交之前);對于DELETE,存儲引擎會執行一個INSERT;對于每個UPDATE,InnoDB存儲引擎會執行一個相反的UPDATE,將修改前的行放回去。最后一點,undo log 會產生redo log,這是因為 undo log 也需要持久性的保護。除此之外 ,undo log 還可以實現非鎖定讀,詳細見上面實現隔離性中博客地址。

undo log 存儲管理

InnoDB對 undo 的管理采用段的方式,段存放于共享表空間中。InnoDB存儲引擎有rollback segment(回滾段),每個回滾段中記錄了1024個undo log segment(段),每一個undo log segment 代表一個事務。
在InnoDB1.1版本之前(不包括1.1版本),只有一個rollback segment,因此支持 1024個事務,從1.1版本開始InnoDB支持最大128個rollback segment,也就是將同時在線的事務限制提高到了128 * 1024。
事務在undo log segment 分配頁并寫入undo log 的這個過程同樣需要寫入重做日志,當事務提交時,InnoDB存儲引擎會做兩件事情:
1、將undo log 放入列表中,以提供之后的purge 操作
2、判斷undo log 所在的頁是否可以重用,若可以分配給下個事務使用
事務提交后并不能馬上刪除 undo log 及undo log 所在的頁,這是因為可能還有其他事務需要通過undo log 來得到行記錄之前的版本(一致性非鎖定讀),所以事務提交時將undo log 放入一個鏈表中,是否可以最終刪除 undo log 及undo log 所在頁由purge 線程來判斷。

undo log 格式

在InnoDB存儲引擎中,undo log 分為:
1、insert undo log
2、update undo log

insert undo log 是指在insert 操作中產生的undo log,因為是insert操作的記錄,只對事務本身可見,對其他事務不可見(事務隔離性的要求),故該undo log 可以在事務提交后直接刪除,因為其他事務不可能讀到還未插入的數據,這條數據根據就不存在。

上圖中*表示對存儲字段進行了壓縮,next記錄的是下一個undo log 的位置,通過該next的字節可以知道一個undo log所占的空間字節數;尾部的start記錄的是undo log 的開始的位置,占兩個字節;type_cmpl占用一個字節,記錄的是undo 的類型,對于 insert undo log,該值永遠是11;undo no 記錄事務的ID;table_id記錄 undo log所對應的表對象;剩下的部分記錄了所有主鍵的列和值,在rollback操作時,根據這些值可以定位到具體的記錄,然后進行直接刪除。

update undo log 記錄的是delete 和update 操作產生的undo log 可能需要提供MVCC 機制,因此不能在事務提交時就進行刪除,提交時放入 undo log 鏈表,等待purge 線程進行最后的刪除。update undo log 結構如下:

next、start、undo_no、table_id與之前介紹的insert undo log 部分相同,update_vector 表示update操作導致發生改變的列。每個修改的列信息都要記錄到undo log中。

實現持久性

redo log 組成

實現持久性是通過redo log(重做日志)來實現的,其由兩個部分組成:一是內存中的重做日志緩沖(redo log buffer),其是易失的;二是重做日志文件(redo log file),其是持久的。當事務提交(COMMIT)時,必須先將該事務的所有日志寫入到重做日志文件進行持久化,待事務的COMMIT操作完成才算完成。

redo log 工作原理

在將redo log 寫入磁盤之前,先將重做日志緩沖寫入重做日志文件,然后再進行一次fsync(同步文件到存儲設備)操作,才將日志寫入到磁盤當中做持久化,由于fsync的效率取決于磁盤的性能,因此磁盤的性能決定了事務提交的性能,也就是數據庫的性能。如果在數據庫數據發生改變時,直接修改到磁盤中,那整個過程的IO成本是非常高的,所以當一條數據需要更新的時候,InnoDB先把記錄寫入到文件系統緩存,然后在適當的時候將記錄更新到磁盤里面。

redo log 是固定大小的,比如可以配置一組4個文件,每個文件大小是1GB,總共就是4GB,從頭開始寫,寫到末尾就又回到開頭循環寫,如下圖所示:

write pos是當前記錄的位置,一邊寫一邊后移,寫到第3號文件末尾后就回到0號文件開頭。checkpoint是當前要擦除的位置,也是往后推移并且循環的,擦除記錄前要把記錄更新到數據文件。如果write pos追上checkpoint,表示日志滿了,這時候不能再執行新的更新,得停下來先擦掉一些記錄,把checkpoint推進一下。

調整redo log 同步機制

但是在redo log 寫入到磁盤之前,數據庫突然發生宕機,由于部分日志未刷新到磁盤中,因此會丟失最后一段時間的事務。所以參數 innodb_flush_log_at_trx_commit用來控制重做日志刷新到磁盤的策略。該參數默認為1,表示事務提交時必須調用一次fsync操作,還可以設置參數0和2。0表示事務提交時不進行寫入重做日志操作,該操作僅在master thread 中完成,而master thread 中每 1 秒進行一次fsync操作。2表示事務提交時將重做日志寫入重做日志文件,但僅僅寫入文件系統緩存中,不進行fsync操作,在這個設置下,當mysql 數據庫發生宕機時,并不會導致事務的丟失,當操作系統發生宕機時,重啟數據庫會丟失未從文件系統刷新到重做日志文件那部分事務。
打開mysql ,輸入命令,可看默認是1:

更改命令:

不同的設置對插入50w行數據的速度如下:

redo log 與 bin log 區別

除了redo log之外,mysql中還有一種二進制日志為歸檔日志bin log,bin log 是一個二進制格式的文件,用于記錄用戶對數據庫更新的SQL語句信息,查詢的內容不會進行記錄。它與redo log 的區別如下:
1、redo log 是InnoDB引擎特有的,bin log 是Mysql 的Server層實現的,所有引擎都可以使用。
2、redo log 是物理日志記錄的是對每個頁的修改,bin log 和undo log 一樣,都是邏輯日志,記錄的是SQL語句。
3、redo log 是循環寫的,空間固定會用完;bin log 是可以追加寫入得,當文件達到一定大小后悔切換到下一個,并不會覆蓋以前的日志。
4、兩種日志記錄寫入磁盤的時間點不同,bin log 只在事務提交完成后進行一次寫入,而redo log 在事務進行中就不斷地寫入,表現為日志并不是隨事務提交的順序進行寫入的。
上圖中,bin log 只是在事務提交時記錄的,并且對于每一個事務,只包含對應事務的一個日志;對于redo log,因為記錄的是物理日志,所以每個事務對應多個日志條目,并且事務的redo log 寫入是并發的,并不是在事務提交時寫入,所以在文件中記錄的順序并不是事務開始時的順序,*T2 *T3 *T1 表示的是事務提交時的日志。

redo log 格式

InnoDB存儲引擎的存儲管理是基于頁的,故重做日志格式也是基于頁的,雖然有著不同的重做日志格式,但是它們有著通用的頭部格式,如下圖所示:

redo_log_type:重做日志的類型。
space:表空間的ID。
page_no:頁的偏移量。
redo log body 部分,根據重做日志類型的不同嗎,會有不同的存儲內容,對于頁上記錄的插入和刪除操作,對應如下的格式:

實現一致性

數據庫中的一致性是通過約束來實現的。如果一個事務原子地在一個一致地數據庫中獨立運行,那么在它執行之后,數據庫的狀態一定是一致的。對于這個概念,它的第一層意思就是對于數據完整性的約束,包括主鍵約束、引用約束以及一些約束檢查等等,在事務的執行的前后以及過程中不會違背對數據完整性的約束,所有對數據庫寫入的操作都應該是合法的,并不能產生不合法的數據狀態。還是最上面的例子,更新數據庫前主鍵都是唯一的,那么更新完改數據庫之后主鍵還必須是唯一的。而第二層意思其實是指邏輯上的對于開發者的要求,我們要在代碼中寫出正確的事務邏輯,比如銀行轉賬,事務中的邏輯不可能只扣錢或者只加錢,這是應用層面上對于數據庫一致性的要求。

事務的隔離級別

在說事務的隔離級別之前先來簡單描述一下臟讀、不可重復讀、幻讀、可重復讀。
臟讀:臟讀是一個事務讀取到了其他事務還沒有提交的數據。
不可重復讀:指在一個事務中,讀到了其他事務針對就數據的修改記錄(常見的操作就是update 或者 delete 語句)。
幻讀:指在一個事務中,讀取到了其他事務新增的數據,仿佛出現了幻影的現象(常見的操作是insert語句)。
可重復讀:是mysql默認的事務隔離級別,它消除了臟讀、不可重復讀、幻讀的現象,保證了事務的一致性。

SQL標準的事務隔離級別包括:讀未提交(read uncommitted)、讀提交(read committed)、可重復讀(repeatable read)和串行化(serializable )。
讀未提交是指,一個事務還沒提交時,它做的變更就能被別的事務看到。
讀提交是指,一個事務提交之后,它做的變更才會被其他事務看到。
可重復讀是指,一個事務執行過程中看到的數據,總是跟這個事務在啟動時看到的數據是一致的。當然在可重復讀隔離級別下,未提交變更對其他事務也是不可見的。
串行化,顧名思義是對于同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執行完成,才能繼續執行。

下面是按照時間的順序執行兩個事務的行為:

mysql> create table T(c int) engine=InnoDB; insert into T(c) values(1);


不同的隔離級別下,事務A會有哪些不同的返回結果,也就是圖里面V1、V2、V3的返回值分別是什么:
若隔離級別是“讀未提交”, 則V1的值就是2。這時候事務B雖然還沒有提交,但是結果已經被A看到了。因此,V2、V3也都是2。
若隔離級別是“讀提交”,則V1是1,V2的值是2。事務B的更新在提交后才能被A看到。所以, V3的值也是2。
若隔離級別是“可重復讀”,則V1、V2是1,V3是2。之所以V2還是1,遵循的就是這個要求:事務在執行期間看到的數據前后必須是一致的。
若隔離級別是“串行化”,則在事務B執行“將1改成2”的時候,會被鎖住。直到事務A提交后,事務B才可以繼續執行。所以從A的角度看, V1、V2值是1,V3的值是2。

隔離級別與臟讀、幻讀等的對應關系:

總結

以上是生活随笔為你收集整理的Mysql中的事务详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。