Mysql系列第二十五講 mysql如何確保資料不丟失?有幾點值得我們借鑑
預備知識
-
mysql內部是使用b+樹的結構將資料儲存在磁碟中,b+樹中節點對應mysql中的頁,mysql和磁碟互動的最小單位為頁,頁預設情況下為16kb,表中的資料記錄儲存在b+樹的葉子節點中,當我們需要修改、刪除、插入資料時,都需要按照頁來對磁碟進行操作。
-
磁碟順序寫比隨機寫效率要高很多,通常我們使用的是機械硬碟,機械硬碟寫資料的時候涉及磁碟尋道、磁碟旋轉定址、資料寫入的時間,耗時比較長,如果是順序寫,省去了尋道和磁碟旋轉的時間,效率會高几個數量級。
-
記憶體中資料讀寫操作比磁碟中資料讀寫操作速度高好多個數量級。
mysql確保資料不丟失原理分析
我們來思考一下,下面這條語句的執行過程是什麼樣的:
按照正常的思路,通常過程如下:
-
找到user_id=666這條記錄所在的頁p1,將p1從磁碟載入到記憶體中
-
在記憶體中對p1中user_id=666這條記錄資訊進行修改
-
mysql收到commit指令
-
將p1頁寫入磁碟
-
給客戶端返回更新成功
上面過程可以確保資料被持久化到了磁碟中。
我們將需求改一下,如下:
來看一下處理過程:
-
找到user_id=666這條記錄所在的頁p1,將p1從磁碟載入到記憶體中
-
在記憶體中對p1中user_id=666這條記錄資訊進行修改
-
找到user_id=888這條記錄所在的頁p2,將p2從磁碟載入到記憶體中
-
在記憶體中對p2中user_id=888這條記錄資訊進行修改
-
mysql收到commit指令
-
將p1頁寫入磁碟
-
將p2頁寫入磁碟
-
給客戶端返回更新成功
上面過程我們看有什麼問題
-
假如6成功之後,mysql當機了,此時p1修改已寫入磁碟,但是p2的修改還未寫入磁碟,最終導致user_id=666的記錄被修改成功了,user_id=888的資料被修改失敗了,資料是有問題的
-
上面p1和p2可能位於磁碟的不同位置,涉及到磁碟隨機寫的問題,導致整個過程耗時也比較長
上面問題可以歸納為2點:無法確保資料可靠性、隨機寫導致耗時比較長。
關於上面問題,我們看一下mysql是如何最佳化的,mysql內部引入了一個redo log,這是一個檔案,對於上面2條更新操作,mysql實現如下:
mysql內部有個redo log buffer,是記憶體中一塊區域,我們將其理解為陣列結構,向redo log檔案中寫資料時,會先將內容寫入redo log buffer中,後續會將這個buffer中的內容寫入磁碟中的redo log檔案,這個個redo log buffer是整個mysql中所有連線共享的記憶體區域,可以被重複使用。
-
mysql收到start transaction後,生成一個全域性的事務編號trx_id,比如trx_id=10
-
user_id=666這個記錄我們就叫r1,user_id=888這個記錄叫r2
-
找到r1記錄所在的資料頁p1,將其從磁碟中載入到記憶體中
-
在記憶體中找到r1在p1中的位置,然後對p1進行修改(這個過程可以描述為:將p1中的pos_start1到pos_start2位置的值改為v1),這個過程我們記為rb1(內部包含事務編號trx_id),將rb1放入redo log buffer陣列中,此時p1的資訊在記憶體中被修改了,和磁碟中p1的資料不一樣了
-
找到r2記錄所在的資料頁p2,將其從磁碟中載入到記憶體中
-
在記憶體中找到r2在p2中的位置,然後對p2進行修改(這個過程可以描述為:將p2中的pos_start1到pos_start2位置的值改為v2),這個過程我們記為rb2(內部包含事務編號trx_id),將rb2放入redo log buffer陣列中,此時p2的資訊在記憶體中被修改了,和磁碟中p2的資料不一樣了
-
此時redo log buffer陣列中有2條記錄[rb1,rb2]
-
mysql收到commit指令
-
將redo log buffer陣列中內容寫入到redo log檔案中,寫入的內容:
- 返回給客戶端更新成功。
上面過程執行完畢之後,資料是這樣的:
-
記憶體中p1、p2頁被修改了,還未同步到磁碟中,此時記憶體中資料頁和磁碟中資料頁是不一致的,此時記憶體中資料頁我們稱為髒頁
-
對p1、p2頁修改被持久到磁碟中的redolog檔案中了,不會丟失
認真看一下上面過程中第9步驟,一個成功的事務記錄在redo log中是有start和end的,redo log檔案中如果一個trx_id對應start和end成對出現,說明這個事務執行成功了,如果只有start沒有end說明是有問題的。
那麼對p1、p2頁的修改什麼時候會同步到磁碟中呢?
redo log是mysql中所有連線共享的檔案,對mysql執行insert、delete和上面update的過程類似,都是先在記憶體中修改頁資料,然後將修改過程持久化到redo log所在的磁碟檔案中,然後返回成功。redo log檔案是有大小的,需要重複利用的(redo log有多個,多個之間採用環形結構結合幾個變數來做到重複利用,這塊知識不做說明,有興趣的可以去網上找一下),當redo log滿了,或者系統比較閒的時候,會對redo log檔案中的內容進行處理,處理過程如下:
-
讀取redo log資訊,讀取一個完整的trx_id對應的資訊,然後進行處理
-
比如讀取到了trx_id=10的完整內容,包含了start end,表示這個事務操作是成功的,然後繼續向下
-
判斷p1在記憶體中是否存在,如果存在,則直接將p1資訊寫到p1所在的磁碟中;如果p1在記憶體中不存在,則將p1從磁碟載入到記憶體,透過redo log中的資訊在記憶體中對p1進行修改,然後將其寫到磁碟中
上面的update之後,p1在記憶體中是存在的,並且p1是已經被修改過的,可以直接重新整理到磁碟中。
如果上面的update之後,mysql當機,然後重啟了,p1在記憶體中是不存在的,此時系統會讀取redo log檔案中的內容進行恢復處理。
-
將redo log檔案中trx_id=10的佔有的空間標記為已處理,這塊空間會被釋放出來可以重複利用了
-
如果第2步讀取到的trx_id對應的內容沒有end,表示這個事務執行到一半失敗了(可能是第9步驟寫到一半當機了),此時這個記錄是無效的,可以直接跳過不用處理
上面的過程做到了:資料最後一定會被持久化到磁碟中的頁中,不會丟失,做到了可靠性。
並且內部採用了先把頁的修改操作先在記憶體中進行操作,然後再寫入了redo log檔案,此處redo log是按順序寫的,使用到了io的順序寫,效率會非常高,相對於使用者來說響應會更快。
對於將資料頁的變更持久化到磁碟中,此處又採用了非同步的方式去讀取redo log的內容,然後將頁的變更刷到磁碟中,這塊的設計也非常好,非同步刷盤操作!
但是有一種情況,當一個事務commit的時候,剛好發現redo log不夠了,此時會先停下來處理redo log中的內容,然後在進行後續的操作,遇到這種情況時,整個事物響應會稍微慢一些。
mysql中還有一個binlog,在事務操作過程中也會寫binlog,先說一下binlog的作用,binlog中詳細記錄了對資料庫做了什麼操作,算是對資料庫操作的一個流水,這個流水也是相當重要的,主從同步就是使用binlog來實現的,從庫讀取主庫中binlog的資訊,然後在從庫中執行,最後,從庫就和主庫資訊保持同步一致了。還有一些其他系統也可以使用binlog的功能,比如可以透過binlog來實現bi系統中etl的功能,將業務資料抽取到資料倉儲,阿里提供了一個java版本的專案:canal,這個專案可以模擬從庫從主庫讀取binlog的功能,也就是說可以透過java程式來監控資料庫詳細變化的流水,這個大家可以腦洞大開一下,可以做很多事情的,有興趣的朋友可以去研究一下;所以binlog對mysql來說也是相當重要的,我們來看一下系統如何確保redo log 和binlog在一致性的,都寫入成功的。
還是以update為例:
一個事務中可能有很多操作,這些操作會寫很多binlog日誌,為了加快寫的速度,mysql先把整個過程中產生的binlog日誌先寫到記憶體中的binlog cache快取中,後面再將binlog cache中內容一次性持久化到binlog檔案中。
過程如下:
-
mysql收到start transaction後,生成一個全域性的事務編號trx_id,比如trx_id=10
-
user_id=666這個記錄我們就叫r1,user_id=888這個記錄叫r2
-
找到r1記錄所在的資料頁p1,將其從磁碟中載入到記憶體中
-
在記憶體中對p1進行修改
-
將p1修改操作記錄到redo log buffer中
-
將p1修改記錄流水記錄到binlog cache中
-
找到r2記錄所在的資料頁p2,將其從磁碟中載入到記憶體中
-
在記憶體中對p2進行修改
-
將p2修改操作記錄到redo log buffer中
-
將p2修改記錄流水記錄到binlog cache中
mysql收到commit指令
將redo log buffer攜帶trx_id=10寫入到redo log檔案,持久化到磁碟,這步操作叫做redo log prepare,內容如下
-
將binlog cache攜帶trx_id=10寫入到binlog檔案,持久化到磁碟
-
向redo log中寫入一條資料:end trx=10;表示redo log中這個事務完成了,這步操作叫做redo log commit
-
返回給客戶端更新成功
我們來分析一下上面過程可能出現的一些情況:
步驟10操作完成後,mysql當機了
當機之前,所有修改都位於記憶體中,mysql重啟之後,記憶體修改還未同步到磁碟,對磁碟資料沒有影響,所以無影響。
步驟12執行完畢之後,mysql當機了
此時redo log prepare過程是寫入redo log檔案了,但是binlog寫入失敗了,此時mysql重啟之後會讀取redo log進行恢復處理,查詢到trx_id=10的記錄是prepare狀態,會去binlog中查詢trx_id=10的操作在binlog中是否存在,如果不存在,說明binlog寫入失敗了,此時可以將此操作回滾
步驟13執行完畢之後,mysql當機
此時redo log prepare過程是寫入redo log檔案了,但是binlog寫入失敗了,此時mysql重啟之後會讀取redo log進行恢復處理,查詢到trx_id=10的記錄是prepare狀態,會去binlog中查詢trx_id=10的操作在binlog是存在的,然後接著執行上面的步驟14和15.
做一個總結
上面的過程設計比較好的地方,有2點
日誌先行,io順序寫,非同步操作,做到了高效操作
對資料頁,先在記憶體中修改,然後使用io順序寫的方式持久化到redo log檔案;然後非同步去處理redo log,將資料頁的修改持久化到磁碟中,效率非常高,整個過程,其實就是 MySQL 裡經常說到的 WAL 技術,WAL 的全稱是 Write-Ahead Logging,它的關鍵點就是先寫日誌,再寫磁碟。
兩階段提交確保redo log和binlog一致性
為了確保redo log和binlog一致性,此處使用了二階段提交技術,redo log 和binlog的寫分了3步走:
攜帶trx_id,redo log prepare到磁碟
攜帶trx_id,binlog寫入磁碟
攜帶trx_id,redo log commit到磁碟
上面3步驟,可以確保同一個trx_id關聯的redo log 和binlog的可靠性。
關於上面2點優秀的設計,我們平時開發的過程中也可以借鑑,下面舉2個常見的案例來學習一下。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2727022/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- RabbitMq如何確保訊息不丟失MQ
- Elasticsearch如何保證資料不丟失?Elasticsearch
- Oracle Goldengate是如何保證資料有序和確保資料不丟失的?OracleGo
- Mysql系列第二講 詳解mysql資料型別(重點)MySql資料型別
- Mysql系列第二十三講 如何正確的使用索引?MySql索引
- Redis能保證資料不丟失嗎?Redis
- Elasticsearch 如何保證寫入過程中不丟失資料的Elasticsearch
- 3DMAX模型匯出到Unity之中如何確保材質不丟失3D模型Unity
- RabbitMQ-如何保證訊息不丟失MQ
- 關於MQ的幾件小事(四)如何保證訊息不丟失MQ
- Mysql系列第十五講 事務詳解MySql
- Mysql系列第二十四講 聊聊mysql如何實現分散式鎖?MySql分散式
- 我們需要向哈佛借鑑的學習精神
- 訊息推送平臺有沒有保證資料不丟?
- 服務重啟了,如何保證執行緒池中的資料不丟失?執行緒
- MySQL實戰:解密樂觀併發控制,確保資料操作不衝突MySql解密
- Mysql系列第二十講 什麼是索引?MySql索引
- kafka 如何保證不重複消費又不丟失資料?Kafka
- Mysql系列第二十一講 mysql索引原理詳解MySql索引
- Mysql系列第二十二講 mysql索引管理詳解MySql索引
- mysql5.6預設有幾個資料庫?mysql5.6初始有幾個資料庫?MySql資料庫
- Redis 中如何保證資料的不丟失,Redis 中的持久化是如何進行的Redis持久化
- 關於MySQL啟動時,丟失資料檔案不報錯的現象MySql
- mysql root密碼丟失MySql密碼
- 安裝QT我都借鑑了哪些有用的資料QT
- 非同步流複製模式如何保證不丟資料?非同步模式
- 【MySQL】面試官問我:MySQL如何實現無資料插入,有資料更新?我是這樣回答的!MySql面試
- 詳細講解!RabbitMQ防止資料丟失MQ
- 我的mysql有點問題MySql
- 我有伺服器...你們買點不...伺服器
- 優步是如何用Kafka構建可靠的重試處理保證資料不丟失Kafka
- Spark Streaming和Kafka整合是如何保證資料零丟失SparkKafka
- 寶塔資料庫恢復 mysql資料庫丟失恢復 mysql資料庫刪除庫恢復 寶塔mysql資料庫恢復資料庫MySql
- 硬碟資料丟失如何恢復?硬碟
- 確保帳戶安全 談MySQL資料庫安全解決方案MySql資料庫
- 借鑑)資料庫開發祕訣資料庫
- 什麼是mysql資料庫?MySQL的特點有哪些?MySql資料庫
- Spark Streaming使用Kafka保證資料零丟失SparkKafka