您好,登錄后才能下訂單哦!
本篇內容主要講解“一致性讀實現原理是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“一致性讀實現原理是什么”吧!
MySQL中的事務
事務在RDBMS系統中概念基本都是一樣的,是由一組DML語句構的工作單元,要么全部成功,要么全部失敗。
開發過程中,比較關心長事務,即包含DML語句多的工作單元,事務太長會導致一些錯誤,例如可能由于事務數據包大小超過參數max_allowed_packet設置會導致程序報錯,也可能有事務中某個SQL對應接口報錯,導致整個服務調用失敗,在程序設計時,應該考慮避免長事務帶來的業務影響。
事務的ACID
image-20201114221841801
原子性是事務隔離的基礎,隔離性和持久性是手段,最終目的是為了保持數據的一致性。
事務的并發問題
臟讀:事務A讀取了事務B未提交的數據。
不可重復度:事務A多次讀取同一份數據,事務B在此過程中對數據修改并提交,導致事務A多次讀取同一份數據的結果不一致。
幻讀:事務A修改數據的同時,事務B插入了一條數據,當事務A提交后發現還有數據沒被修改,產生了幻覺。
不可重復讀側重于update操作,幻讀側重于insert或delete。解決不可重復讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表。
事務隔離級別
事務隔離是數據庫處理的基礎之一,隔離級別在多個事務同時進行更改和執行查詢時,對性能與結果的可靠性、一致性和可再現性之間的平衡進行調整,InnoDB利用不同的鎖策略支持不同隔離級別。MySQL中有四種隔離級別,分別是讀未提交(READ UNCOMMITTED),讀已提交(READ COMMITTED),可重復讀(REPEATABLE READ)以及串行化(SERIALIZABLE)。
隔離級別 | 臟讀 | 不可重復讀 | 幻讀 |
---|---|---|---|
READ UNCOMMITTED | Yes | Yes | Yes |
READ COMMITTED | No | Yes | Yes |
REPEATABLE READ | No | No | Yes |
SERIALIZABLE | No | No | No |
InnoDB并發控制
MVCC特性
InnonDB是一個支持行鎖的存儲引擎,為了提供更好支持的并發,使用了非鎖定讀,不需要等待訪問數據上的鎖釋放,而是讀取行的一個快照,該方法是通過InnonDB MVCC特性實現的。
MVCC是Multi-Version Concurrency Control的簡稱,即多版本并發控制,作用是讓事務在并行發生時,在一定隔離級別前提下,可以保證在某個事務中能實現一致性讀,也就是該事務啟動時根據某個條件讀取到的數據,直到事務結束時,再次執行相同條件,還是讀到同一份數據,不會發生變化。
MVCC的好處
讀不加鎖,讀寫不沖突。在讀多寫少的OLTP應用中,讀寫不沖突是非常重要的,可以增加系統的并發性能。
在MVCC中,有兩種讀操作:快照度和當前讀。
MVCC快照
MVCC內部使用的一致性讀快照稱為Read View,在不同的隔離級別下,事務啟動時或者SQL語句開始時,看到的數據快照版本可能也不同,在RR、RC隔離級別下會用到 Read view。
InnoDB 里面每個事務有一個唯一的事務ID,稱為Transaction ID,它是在事務開始的時候向InnoDB的事務系統申請的,是按申請順序嚴格遞增的。而每行數據都有多個版本。每次事務更新數據的時候,都會生成一個新的數據版本Read View,并且把Transaction ID賦值給這個數據版本的事務 ID,標記為 row_trx_id。同時舊的數據版本要保留,并且在新的數據版本中,能夠有信息可以直接拿到它,數據表中的一行記錄,其實可能有多個數據版本 ,每個版本有自己的 row_trx_id。
InnoDB行格式
目前InnoDB默認的行格式Dynamic,是Compat格式的增強版,記錄頭結構信息占用5個字節,事務ID和回滾指針分別占用6和7個字節,行格式如下:
記錄頭結構
項目 | 大小(bit) | 描述 |
---|---|---|
() | 1 | Unknown |
() | 1 | Unknown |
deleted_flag | 1 | 數據行刪除標記 |
min_rec_flag | 1 | =1如果該記錄被預先被定義為最小的記錄 |
n_owned | 4 | 擁有的記錄數 |
heap_no | 13 | 索引堆中該條記錄的排序位置 |
record_type | 3 | 記錄類型;000:普通,001:B+樹葉子節點,010:偽列Infinum,011:Supernum,1xx:保留 |
next_record | 16 | page中下一條記錄的相對位置 |
Transaction ID | 48 | 記錄中的事務ID,固定6個字節 |
Rollback Pointer | 56 | 回滾指針,固定7個字節 |
數據行存儲
#創建表 mysql> create table store_users (id int not null auto_increment primary key comment '主鍵id',name varchar(20) not null default '' comment '姓名'); # 查看表狀態信息 mysql> show table status like 'store_users'\G Row_format: Dynamic #默認行格式為Dynamic Rows: 0 #行數 Avg_row_length: 0 #平均行長度 Data_length: 16384 #初始化段大小16K #開啟事務,插入數據 mysql> begin; mysql> insert into store_users values(null, 'aaaaa'),(null, 'bbbbb'); #查看InnoDB分配的事務ID mysql> select trx_id from information_schema.innodb_trx\G trx_id: 8407246 #事務ID
分析表的行頭信息以及隱藏的事務ID和回滾指針。
# 用Linux下的工具hexdump進行分析 $ hexdump -C -v /usr/local/var/mysql/test/store_users.ibd > store_users.txt $ vi store_users.txt 00010060 02 00 1b 69 6e 66 69 6d 75 6d 00 03 00 0b 00 00 |...infimum......| 00010070 73 75 70 72 65 6d 75 6d 05 00 00 10 00 1c 80 00 |supremum........| 00010080 00 01 00 00 00 80 48 ce 83 00 00 01 d8 01 10 61 |......H........a| #Record Header信息 00010090 61 61 61 61 05 00 00 18 ff d6 80 00 00 02 00 00 |aaaa............| 000100a0 00 80 48 ce 83 00 00 01 d8 01 1d 62 62 62 62 62 |..H........bbbbb| 000100b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
10表示變長字段長度,只有一個varchar(20)沒有超過256字節,且沒有NULL值。
00代表NULL標志位,第一行沒有為NULL數據。
字符a的十六進制是61,即61 61 61 61 61代表的是字段值aaaaa
00 00 00 80 48 ce 6個字節就是Transaction ID,轉換成十進制8407246,正是上面information_schema.innodb_trx.trx_id列的值,trx_id: 8407246 。
83 00 00 01 d8 01 10 7個字節是Rollback Pointer。
1c 80 00 00 01 是5個字節,代表Record Header信息。
隔離級別與快照
REPEATABLE READ
默認的隔離級別,一致讀快照(Read View)是在第一次SELECT發起時建立,之后不會再發生變化。如果在同一個事務中發出多個非 鎖定SELECT語句,那么這些SELECT語句在事務提交前返回的結果是一致的。
在RR下快照Read View不是事務發起時創建,而是在第一個SELECT發起后創建。
READ COMMITTED
在READ COMMITTED讀已提交下,一致讀快照(Read View)是在每次SELECT后都會生成最新的Read View,即每次SELECT都能讀取到已COMMIT的數據,就會存在不可重復讀、幻讀 現象。
Undo回滾段
當開啟事務執行更新語句(insert/update/deeldte),會經過Server層的處理生成執行計劃,然后調用存儲引擎層接口去讀寫數據,用戶沒有觸發COMMIT或ROLLBACK之前,這些Uncommitted Data的數據稱為前鏡像(Post Image),數據存儲在Undo Log,以便用戶回滾或者MySQL Server Crash的恢復,同時Undo Log是循環覆蓋使用。
#開啟事務,更新賬戶余額,不提交事務。 mysql> start transaction; mysql> update account set balance = 100000 where account_no = 10001; Rows matched: 1 Changed: 1 Warnings: 0
上面在RR隔離級別下,開啟一個事務,做update更新操作,不提交事務,通過show engine innodb status\G查看undo情況。
Trx id counter 8407258 Purge done for trx's n:o < 8407257 undo n:o < 0 state: running but idle History list length 33 ...... ---TRANSACTION 8407257, ACTIVE 154 sec 2 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 1
Trx id counter 8407258當前的事務ID,undo log entries 1使用了的undo entries,ACTIVE 154 sec事務持續時間,事務commit后,會調用Purge Thread把undo中的老數據清理掉。
回滾記錄
insert:反向操作是delete,undo里記錄的是delete相關信息,存儲主鍵id即可。
udpate:反向操作是update,undo里記錄的是update前的相關數據。
delete:反向操作是insert,undo里記錄的是insert values(…..)相關的記錄。
從這里可以知道,更新操作占用Undo空間的大小排序如下:
delete > update > insert
所以不建議物理delete刪除數據,會產生大量的Undo Log,Undo快被寫滿就會發生切換,在次期間會有大量的IO操作,導致業務的DML都會變得很慢。
一致性讀
MySQL官方文檔對一致讀的描述:
讀操作基于某個時間點得到一份那時的數據快照,而不管同時其他事務對數據的修改。查詢過程中,若其他事務修改了數據,那么就需要從 undo log中獲取舊版本的數據。這么做可以有效避免因為需要加鎖(來阻止其他事務同時對這些數據的修改)而導致事務并行度下降的問題。
在可重復讀(REPEATABLE READ,簡稱RR)隔離級別下,數據快照版本是在第一個讀請求發起時創建的。在讀已提交(READ COMMITTED,簡稱RC)隔離級別下,則是在每次讀請求時都會重新創建一份快照。
一致性讀是InnoDB在RR和RC下處理SELECT請求的默認模式。由于一致性讀不會在它請求的表上加鎖,其他事務可以同時修改數據不受影響。
一行數據有多個版本,每個數據版本有自己的trx_id,每個事務或者查詢通過trx_id生成自己的一致性視圖。普通select語句是一致性讀,一致性讀會根據row trx_id和一致性視圖確定數據版本的可見性,圖中UR1,UR2就是undo,存儲在Undo Log中,每次查詢時根據當前data page和 Undo page構造出一致性數據頁(Consistent Read Page),通過讀取CR Page將數據返回給用戶。
到此,相信大家對“一致性讀實現原理是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。