数据库系列:InnoDB下实现高并发控制
數據庫系列:MySQL慢查詢分析和性能優化
數據庫系列:MySQL索引優化總結(綜合版)
數據庫系列:高并發下的數據字段變更
數據庫系列:覆蓋索引和規避回表
數據庫系列:數據庫高可用及無損擴容
數據庫系列:使用高區分度索引列提升性能
數據庫系列:前綴索引和索引長度的取舍
數據庫系列:MySQL引擎MyISAM和InnoDB的比較
1 介紹
并發控制是為了防止多用戶并發使用數據庫時造成數據錯誤和程序運行錯誤,保證數據的完整性。當多個事務并發地存取數據庫時,就會產生同時讀取和/或修改同一數據的情況。若對并發操作不加控制就可能會存取和存儲不正確的數據,破壞數據庫的一致性(Consistency)。因此,數據庫中間件必須提供并發控制(Concurrency Control)機制能力,而MySQL的InnoDB引擎,很好的支持了這一塊。
2 InnoDB并發控制
MySQL 是一個流行的關系型數據庫管理系統,它支持多用戶并發訪問。并發控制是確保數據庫一致性和完整性的重要機制。在 MySQL 中,有幾種方法可以實施并發控制:
- 讀寫鎖(Read-Write Locks):MySQL 使用了讀寫鎖來控制對數據的并發訪問。讀寫鎖是共享的,多個客戶端可以同時持有讀鎖,但只有一個客戶端可以持有寫鎖。當一個客戶端獲得寫鎖時,其他客戶端無法獲得讀鎖或寫鎖,直到寫鎖被釋放。
- 事務隔離級別 :MySQL 提供了不同的事務隔離級別,包括讀未提交(Read Uncommitted)、讀已提交(Read Committed)、可重復讀(Repeatable Read)和串行化(Serializable)。較低的隔離級別允許更多的并發訪問,但可能導致數據不一致;較高的隔離級別可以確保數據一致性,但會限制并發訪問。
- 鎖等待和死鎖:MySQL 提供了鎖等待和死鎖檢測機制。當一個事務嘗試獲取一個已經被其他事務持有的鎖時,MySQL 會阻塞等待,直到鎖被釋放。如果事務之間形成死鎖,MySQL 會檢測到并終止其中一個事務以解除死鎖。
- 分段鎖定(Segmented Locking):MySQL 還支持分段鎖定,它允許對數據庫的特定部分進行鎖定,而不是對整個數據庫進行鎖定。這對于處理大型數據集時非常有用,因為它可以減少鎖定范圍,提高并發性能。
- 樂觀并發控制(Optimistic Concurrency Control):MySQL 還支持樂觀并發控制,它假設沖突不太可能發生,因此不會立即鎖定數據。而是在更新時檢查是否存在沖突,如果存在沖突,則進行適當的處理,比如回滾或重試。
- 多版本并發控制(MVCC):MVCC允許在事務隔離級別下執行一致性讀操作,以提高并發性能。
通過合理配置和使用上述機制,可以在 MySQL 中實現有效的并發控制,來保證在數據庫中執行一致性和數據完整性。
下面詳細來說說通過并發控制保證數據一致性的常見手段:
- 鎖(Locking)
- 數據多版本(Multi Versioning)
3 鎖的基本實現
MySQL 使用鎖來保持數據的一致性。在并發控制中,鎖是用來防止多個事務同時對同一數據進行修改或刪除,以保持數據的一致性。MySQL 中的鎖機制包括共享鎖和排他鎖。
鎖的基本實現,一般是這樣的:
- 當用戶對數據進行操作前,鎖住,實施互斥,不允許其它任務的操作;
- 當前操作完成后,釋放鎖之后,其他任務才可以執行;
但是存在一個問題,他的執行本質是串行的,無論讀寫,都無法并行,這樣性能太差了,也不符合互聯網高并發需求。
于是MySQL 中的鎖機制實現了共享鎖和排他鎖:
- 共享鎖(Shared Lock):多個事務可以同時持有共享鎖,用于讀取數據,但不允許修改數據。共享鎖允許并發讀取,提高了并發性能。
- 排他鎖(Exclusive Lock):只有一個事務可以持有排他鎖,用于修改數據,不允許其他事務同時修改。排他鎖會阻塞其他事務的讀寫操作,降低了并發性能。
簡而言之就是: - 共享鎖之間不互斥,即讀讀可以并行,這樣提高了數據讀取的并發能力
- 排他鎖與任何鎖互斥,所以寫讀,寫寫不可以并行,其他線程的讀寫操作都在鎖釋放之后才能執行,這樣對并發度是有較大影響的
所以說,單純的鎖機制,還是不滿足需求,為了保證寫任務沒有完成之時,其他讀的任務也可以并發執行,我們就需要使用另外一個能力來補充。
那就是數據多版本(Multi Versioning)
4 數據多版本的實現原理
MySQL的并發控制是通過多版本并發控制(MVCC)實現的。MVCC允許在事務隔離級別下執行一致性讀操作,以提高并發性能。
在MySQL的MVCC中,每個數據行都有多個版本,每個版本都表示該行在不同時間點的狀態。當一個事務讀取數據時,它只看到該事務開始之前存在的數據版本,而不是當前最新的數據版本。這種方式允許并發讀取多個數據版本,而不會相互阻塞,進一步提高并發的效果。
詳細拆分開來,讀寫同步執行的原理是這樣的:
- 執行寫任務發時,Clone一份數據,打上新的版本號,與原版本號區分
- 寫任務實際操作的是克隆那個版本的數據,直至操作并提交完成后
- 讀任務可以并發執行,持續讀取,讀的是原版本的數據,并不會造成阻塞
如圖所示,可以分成這幾個步驟去解讀:
- 初始數據版本為V1.0
- T1發起了一個寫任務,這時候把數據clone了一份,進行修改,版本變為V2.0,這時候修改進行中,任務還未完成
- T2并發了一個讀任務,依然讀的是V1.0版本的數據
- T3又并發了一個讀任務,依然不會阻塞,讀的還是V1.0的版本
- 這時候數據修改,V1.0的數據變為V2.0的數據
- T4這時候發起的度任務,讀到的就是V2.0的數據了
從這邊可以看出,數據多版本,讀寫之間不需要阻塞,能夠極大提高任務的并發能力。
- 普通意義上的鎖機制,本質是串行執行,效率十分低下
- 讀寫鎖,可以實現讀讀并發,但是寫讀依然是互斥的,也不符合互聯網的機制
- 數據多版本(Multi Versioning),才是實現讀寫并發的要素
5 MySQL 數據多版本的相關實現
5.1 概念介紹
在MySQL的InnoDB存儲引擎中,使用MVCC(多版本并發控制,Multiversion Concurrency Control)實現多版本控制。
MVCC的實現主要基于undo日志、redo日志、rollback segment 回滾存儲區間、和read view。undo日志用于回滾操作,而read view用于生成數據行的歷史版本。通過這種方式,InnoDB實現了非阻塞的一致性讀操作。
-
redo日志
數據庫事務提交后,必須將更新的數據刷到磁盤上,以保證ACID特性。磁盤隨機寫性能較低,且過度頻繁的刷盤,會極大影響數據庫的吞吐量。
優化方式是將修改行為先寫到redo日志里,這樣隨機就變成了有序性,再按照時間周期將數據持久化到磁盤上,極大提高了性能。
另外一方面,即使數據庫崩潰,恢復之后也可以從redo日志里面獲取操作Log,重新提交事務操作,然后刷盤,最終保證數據的一致性。 -
undo日志
數據庫事務未提交時,會將事務修改數據的Mirror Data(修改前的版本 )存放到 Undo Log中,它的主要作用是在事務執行過程中,如果發生錯誤或者需要回滾操作,可以通過Undo Log中的記錄來撤銷已經執行的操作,恢復數據到事務開始之前的狀態。
另外一方面,數據庫奔潰時,也可以使用undo日志,撤銷未提交事務,保證事務的ACID特性。- insert操作:undo日志存儲新數據的PK(ROW_ID),回滾時執行刪除即可。
- delete/update操作:undo日志存儲舊數據row(整行數據),回滾時直接恢復。
-
rollback segment
Rollback Segment(回滾存儲區)是數據庫中的一部分存儲空間,用于臨時保存當數據庫數據發生改變時的先前值。它主要有兩個作用:- 通過Rollback操作來取消數據操作,使之恢復到改變之前的原始值,在transaction的過程才有效。如果執行了commit命令,那么Rollback Segment里面的值就會標識為失效,數據的改變將永久化。
- select讀取的同時另一個事務也在修改這個表的值,那么select出來的數據是修改前的值,因為修改之前的原數據存入到了Rollback Segment中,所以不會被阻塞到。
5.2 示例說明
5.2.1 初始數據
# 表結構
t_userinfo(id PK, name, sex, age);
# 默認數據
1,Brand,0,22
2,Helenlyn,1,19
3,Sol,0,21
先初始化一個默認的表,里面模擬幾條數據。此時沒有任何的事務未提交操作,所以回滾段是空的,如上圖。
5.2.2 事務操作示例
start transaction;
delete from t_userinfo where id = 1;
update t_userinfo set name = 'Helenlyn...' where id = 2;
insert into t_userinfo(name, sex, age) valus ('Lili', 1, 18);
還未執行commit 或者 rollback,所以事務處于未提交的狀態
綜上,我們可以看出Commit之前我們進行如下操作:
- 正式提交刪除前,id=1的數據作為舊版本的數據,進入了回滾存儲區;
- 正式提交修改前,id=2的數據作為舊版本的數據,進入了回滾存儲區;
- 新插入的數據 ('Lili', 1, 18), id= 4,在正式提交之前,也進入了回滾段;
我們上面說了,不是所有的操作最終都會commit,如果失敗,事務rollback,就可以通過回滾存儲區中的undo日志對操作進行回滾。
如果成功commit,則整體提交成功了
可以看到:
- id=1 數據刪除成功;
- id=2 字段更新成功;
- id=4 數據行插入成功;
- 回滾存儲區相關日志清掉
如果失敗并執行rollback,則全部回滾
- 數據刪除的恢復了
- 被修改的舊數據也恢復
- 新增寫入的數據刪除
- 回滾存儲區相關日志清掉
6 總結
- MySQL實現并發控制,保證數據一致性的方法有鎖,數據多版本等
- 普通鎖串行,讀寫鎖讀讀并行,Multi Versioning 讀寫并行;
- redo日志保證已提交事務的ACID特性, undo日志用來回滾未提交的事務,rollback segment 為臨時回滾存儲區;
- InnoDB是基于多版本并發控制的存儲引擎;
- InnoDB用的多版本是快照讀不加鎖,所有select都是快照讀,這些數據不會被修改,并發性能特別高;
總結
以上是生活随笔為你收集整理的数据库系列:InnoDB下实现高并发控制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: zookeeper源码(04)leade
- 下一篇: 设计模式(十一)享元