深入聊聊MySQL中的事務特性與實作原理

青灯夜游
發布: 2023-02-10 19:55:17
轉載
1420 人瀏覽過

這篇文章帶大家聊聊MySQL中的事務特性,介紹一下多版本並發控制MVCC實現原理,希望對大家有幫助!

深入聊聊MySQL中的事務特性與實作原理

一、概念

# 一般指的是邏輯上的一組運算,或者作為單一邏輯單元執行的一系列操作,一個事務中的所有操作會被封裝成一個不可分割的執行單元,這個單元的所有操作要么全部執行成功,要么全部執行失敗,只要其中任意一個操作執行失敗,整個事務就會執行回滾操作。

二、交易的特性以及型別介紹

#2.1 事務特性

深入聊聊MySQL中的事務特性與實作原理

##原子性(atomicity)

交易的原子性指的是所有構成事務的操作要麼全部執行成功,要麼全部執行是失敗。

一致性(consistency)

交易的一致性指的是事務執行之前和執行之後,資料始終處於一致的狀態。

隔離性(isolation)

交易的隔離性指的是並發執行的兩個交易之間互不干擾,也就是說,一個交易執行的過程中是無法看到其他事務運行過程的中間狀態的。

?‍注意:MySQL是透過鎖定MVCC機制來保證交易的隔離。

持久性(duration)

交易的持久性指的是一旦交易被提交後,此交易對資料的變更操作會被持久化到資料庫中,並且不會被回滾。

2.2 兩個交易類型介紹

    本機交易
  • 分散式交易
##本地事務

通常基於關係型資料庫控制的事務可以稱為傳統事務或本地事務。

本地事務的執行流程

深入聊聊MySQL中的事務特性與實作原理

    #客戶端開始交易操作之前,需要開啟一個連線回話;
  • 開始回話之後,客戶端發起開啟事務的指令;
  • 交易開始後,客戶端發送各種SQL語句處理資料;
  • 正常情況下,客戶端會啟動提交交易的指令,如果發生異常情況,客戶端會啟動回滾事務命令;
  • 上述流程完成後,關閉會話。
✔本機事務是有資源管理器在本機進行管理的。

本地交易的缺點在於:

不具備分散式交易的處理能力
  • 一次交易過程只能連接一個支援事務的資料庫,既不能用於多個事務性資料庫。
三、並發交易會帶來的哪些問題?

深入聊聊MySQL中的事務特性與實作原理

#更新遺失(髒寫)

當兩個會在兩個以上的事務同時操作同一行數據,並給予最初選定的值更新該行數據時,視為事務之間是無法感知彼此的存在的,所以會出現最後的更新操作會覆寫之前其他交易完成的更新操作。

舉例:

張三的帳戶為100元,目前有兩個事務:事務1和事務2,事務1是將張三帳戶餘額增加100元,事務2是將張三餘額增加200,起初事務1和事務2同時讀到張三的帳戶餘額都是100元,然後事務1和事務2分別更新張三月,假設事務1先於事務2提交,但是最近兩個事務都提交後張三的餘額為300元(正常情況應該是400元),

也就是說:後提交的事務2覆蓋了事務1的更新操作,這就是所謂的更新遺失,更新遺失(髒寫)本質上是寫操作的衝突,然而解決髒寫的方式是讓每個事務串列方式執行,保證事務按照一定的順序執行寫操作。

#

髒讀

一個交易讀取到另一個交易未提交的資料。例如:事務1正在對張三的餘額增加100元,在這個事務沒提交之前,另一個事務2讀取了正在修改的這條數據,如果沒有事務的控制下,第二條事務就會讀取到沒有被提交的髒數據,並且對髒數據叢下一步的處理,此時就會產生未提交數據的依賴關係,通常這種現像被稱為臟讀,也就是說:髒讀是一個事務讀取了另一個事務沒提交的資料。

深入聊聊MySQL中的事務特性與實作原理

?髒讀本質上是讀寫操作的衝突,解決方法是先寫後讀,也就是寫完後再讀。

不可重複讀取

一個事務讀取了某些數據,在一段時間後,這個事務再次讀取之前讀過的數據,此時發現讀取的資料發生了變化,或者其中某些資料記錄已經被刪除,這種現像被稱為:不可重複讀,即同一個事務,使用同樣的查詢語句,在不同時刻讀取到的結果不一致。

不可重複讀取的本質上也是讀寫操作衝突,解決方法是先讀後寫,也就是讀完之後再寫。

幻讀

一個事務按照相同的查詢條件重新讀取之前讀過的數據,此時發現其他事務插入了滿足當前查詢條件的新數據,數據結果集變多,這種現象稱為幻讀,即一個事務兩次讀取一個範圍的數據記錄,兩次讀到的結果不同。

幻讀的本質上也是讀寫操作衝突,解決方法是先讀後寫,也就是讀完之後再寫。

那到這裡,很多小夥伴就有疑問,同樣本質都是讀寫操作衝突,解決方法是先讀後寫,不可重複讀和幻讀到底有何區別?,我以下見簡單解釋:

  • 1️⃣ 不可重複讀的重點在於更新和刪除操作,而幻讀的重點在於插入操作。

  • 2️⃣ MySQL使用鎖定機制實作交易的隔離等級時,在可重複讀取隔離等級中,SQL語句第一個讀取到資料後,會將對應的資料加鎖,使得其他事務無法修改和刪除這些數據,透過鎖的方式實現可重複讀取。但是這種方法無法對新數據的插入加鎖,如果事務1讀取了數據,或者修改和刪除了數據,事務2還可以進行插入操作,導致事務1莫名其妙地多了一條之前沒有的數據,這就是幻讀。

  • 3️⃣ 所以,幻讀是無法透過鎖定機制來避免,需要使用串行化的事務隔離級別,但是這種事務隔離級別會大大降低資料庫的並發能力。

✔MySQL是透過MVCC(多版本的同時控制)機制來避免無法重複讀取和幻讀。

四、MySQL事務的隔離等級

深入聊聊MySQL中的事務特性與實作原理

#使用下面的指令可以查詢全域層級和會話層級的事務隔離等級:

# 默认数据库的事务隔离级别为:可重复读(REPEATABLE-READ)
SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
SELECT @@tx_isolation;
登入後複製

深入聊聊MySQL中的事務特性與實作原理

五、多版本並發控制MVCC

Multi-Version Concurrency Control多版本並發控制,MVCC是一種並發控制的方法,一般在資料庫管理系統中,實現對資料庫的並發存取。

MVCC比鎖的優勢

可以將MVCC看成行級鎖定的一種妥協,它在許多情況下避免了使用鎖,同時可以提供更小的開銷。根據實現的不同,它可以允許非阻塞式讀,在寫入操作進行時只鎖定必要的記錄。

MVCC的实现原理

MVCC的是通过保存数据澡某个时间点的快照来实现的,也就是说,不管事务执行多长的时间,每个事务看到的数据都是一致的。根据事务的开始时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。InnonDB主要通过为每一行记录添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。但是InnoDB并不存储这些事件发生时的 实际时间 ,相反它只存储这些事件发生时的系统 版本号(version) 。这是一个随着事务的创建而不断增长的数字。每个事务在事务开始时会记录它自己的系统版本号。每个查询必须去检查每行数据的版本号与事务的版本号是否相同。

《高性能MySQL》书籍中介绍到:

MVCC只在REPEATABLE READREAD COMMITTIED两个隔离级别下工作,其他两个隔离级别都和MVCC不兼容,因为READ UNCOMMITTED总是读取最新的数据行,而不符合当前事务版本数据行,而SERIALIZABLE串行化隔离级别则会对所有读取的行都加锁。

接下来举个例子说明在可重复读事务隔离级别下,MVCC机制是如何完成增删改查操作的。

  • 查询操作(SELECT)

在查询操作中,InnoDB存储引擎跟根据以下两个条件查询对应的行记录,只有满足对应条件才会被返回:

1️⃣ 只查找不晚于当前事务版本的数据行,也就是说,InnoDB存储引擎只会查找版本号小于或者等于当前事务版本的数据行,这些数据行要么在该事务开始前就存在,要么就是事务本身插入或者更新的行。
2️⃣ 对于数据删除,数据行删除的版本要么还没有被定义,要么大于当前事务的版本号,只有这样才能确保事务读到的行,在事务开始前并没有被删除。

举个例子,存在 事务A事务B 两个事务,事务A中存在两套相同的SELECT语句,事务B存在一条UPDATE语句,事务A 的第一条查询语句在事务B提交之前执行,第二条查询语句在 事务B 提交之后执行,事务A 如下所示:

-- 事务A操作
START TRANSACTION;
SELECT * FROM account WHERE id = 1;     //在事务B提交之前执行
SELECT * FROM account WHERE id = 1;     //在事务B提交之后执行
COMMIT;
登入後複製

事务B:

-- 事务B操作
START TRANSACTION;
UPDATE account SET balance = balance+100 WHERE id = 1;
COMMIT;
登入後複製

✔结论:如果没有使用MVCC机制,则事务A中的第一条SELECT语句读取的数据是修改前的数据,而第二条SELECT语句读取的是修改后的数据,两次读取的数据不一致,想想,那不就乱了吗?? 如果使用了MVCC机制,无论事务B如何修改数据,事务A的两条查询语句的到的结果始终是一致的。

  • 插入操作(SELECT)

在插入操作中,InnoDB会将新插入的每一条行记录的当前系统版本包保存为行版本号。

比如:向account表插入一条数据,同时MVCC的两个版本号分别为create_versiondelete_versioncreate_version代表创建行的版本号,delete_version代表删除行的版本号,另外还有一个事务ID字段,如下面所示:

INSERT INTO account(id, name, balance) values(1001, 'austin', 100);
登入後複製

对应的版本号信息如下表:

idnamebalancetransaction_idcreate_versiondelete_version
1001austin10011未定义

可以看出,当向数据表新增记录时,需要设置保存行的版本号,而删除行的版本号未定义。

  • 更新操作(SELECT)

在更新操作中,InnoDB存储引擎会插入一行新记录,并保存当前系统的版本号为新记录行的版本号,同时保存当前系统的版本号到原来数据行作为删除标识。比如:将account数据表中id为1001的用户账户月增加100元,对应SQL如下:

UPDATE account SET balance = balance+100 WHERE id = 1001;
登入後複製

执行SQL, 在MVCC机制下的更新操作如下表所示:

idnamebalancetransaction_idcreate_versiondelete_version
1001austin100112
1001austin20022未定义

可以明显看出,执行更新操作时,MVCC机制是先将原来的数据复制一份,将balance字段增加100后,再讲create_version字段的值设置为当前系统的版本号,而delete_version字段的值未定义。

注意的是:原来的行会被复制到Undo Log中。

  • 删除操作(SELECT)

在删除操作中,InnoDB存储引擎会保存删除的每一个行记录当前的系统版本号,作为删除标识。比如:删除account数据表中id为1001的数据,SQL如下所示:

DELETE FROM account WHERE id = 1001;
登入後複製

对应MVCC机制下的删除操作如下表所示:

id name balance transaction_id create_version delete_version
1001 austin 200 3 2 3

可以看出,当删除数据表数据行时,MVCC机制会将当前系统的版本号写入删除数据行版本字段delete_version中,以此来表示当前数据行已经被删除。

六、总结

本文主要讲述了事务的特性、类型和本地事务和分布式事务的区别,通过不同的示例解释并发事务带来的更新丢失、脏读、不可重复读、幻读的问题,同时介绍了多版本并发控制MVCC的实现原理,如果文章对你有帮助,感谢点赞?+评论?+收藏❤,我是:?‍?austin流川枫,我们下期见!

【相关推荐:mysql视频教程

以上是深入聊聊MySQL中的事務特性與實作原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!