前言
Hi,歡迎訂閱白日夢的MySQL專題!
這篇文章我們一起閑聊,如果你不小心把MySQL中的數據刪了,除了跑路還能干啥?
看完本篇你將了解:常見的數據庫備份方式、mysqldump實戰、一條binlog長啥樣、什么是gtid?什么是binlog位點?mysqlbinlog數據恢復實戰。
數據備份有哪些種?
MySQL中數據備份的方式還是蠻多的,常見的有冷備份、邏輯備份、熱備份、快照備份。
什么是冷備份?
所謂的冷備份,說白了就是在數據庫停止運行的情況下,直接備份磁盤中MySQL用來存儲數據的那些數據文件。
在前面的文章中,白日夢跟大家分享過MySQL的表空間。看過那篇文章的同學都是知道,MySQL中的數據最終都存儲在表空間中的。表空間 == 表空間文件
。其實而所謂的空間,本質上對應着存在於操作系統磁盤上的肉眼能看到的物理文件。
下面你可以看一下我的MySQL的表空間文件都是怎么配置的,以及它們都在哪里。
MySQL版本:5.7 ,並且我在 my.cnf 配置文件中添加了如下的配置。
# 表示每一個數據庫單獨使用一個表空間
innodb_file_per_table=on
然后我創建數據庫:stusy。
創建數據表:test_backup。
進入到如下的目錄中,你可以看到MySQL為我們創建的數據庫表創建出了單獨的目錄,而目錄中的有 .frm、.idb文件就是冷備份需要備份的文件。
什么是邏輯備份?
邏輯備份指的是使用 mysqldump 工具去備份數據。使用mysqldump進行數據庫的邏輯備份也是在做的各位RD需要掌握的技能。日常開發中難免會有將線上的數據備份到測試環境使用的場景。
為啥說mysqldump是邏輯備份?原因大概是:你使用mysqldump去備份最終得到的參數其實是一堆sql,再通過回放sql的形式完成數據的恢復。白日夢之前的文章中跟大家分享過(可自行查看歷史文章哈)。在MySQL中數據表、數據行其實是邏輯存上的概念。像數據頁這中概念是物理真實存在的。所以你用mysqldump得到一堆sql,自然稱得上是邏輯備份嘍。
下文中具體說,mysqldump實戰。
什么是熱備份?
所謂熱備份其實是指:直接對運行中的數據庫進行備份。相對於冷備份,熱備份還是比較復雜的。你想啊,對處於運行過程中的數據庫進行備份,肯定就得將一些增量的數據也備份進去。
通常人們會使用一款叫:xtraback 的工具完成數據庫的熱備份。
除此之外,我了解有一款Golang寫的開源工具 ghost,在github上還是挺火的。它是一款支持做無損DDL的工具(后面會專門有一篇文章講這個工具的原理)。這款工具在實現支持無損DDL功能時,有一部分邏輯本質上也是在支持增量數據的備份。
ghost的實現手段是:添加binlog監聽事件,監聽到binlog event后去解析binlog得到sql,再回放這個SQL。就像是從庫使用主庫對binlog進行數據恢復一樣。
什么是快照備份?
再了解一下什么是快照備份:
快照備份不是數據庫本身提供的能力,本質上它是借助於文件系統的快照功能來實現的對數據庫的備份。
我們知道的Linux服務器本質上也是電腦的,它會有自己的磁盤,無論是固態硬盤,還是機械磁盤。反正會有這種固態存儲。還需要進一步對磁盤進行分區。然后才有將Linux文件系統中的目錄都會掛載在不同的分區上。這么做的目的,簡單來說就像你的window有C盤、D盤、E盤。D盤中的出問題后不會影響E盤一樣。
快照備份要求:數據庫的所有數據文件都要放在一個數據分區中。
常見的支持快照工具的文件系統和設備有:FreeBSD、UFS文件系統、Solaris的ZFS文件系統。GNU/Linux的LVM(Logical Volume Manager)
實用的mysqldump備份方式
本小節看幾個實戰mysqldump備份case。
測試環境:創建如下表
CREATE TABLE `test_backup2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
數據表中寫入3條數據
mysqldump語法
mysqldump [arguments] > file_name;
1、備份指定的數據庫
通過參數--databases
指定你要備份的數據庫
# mysqldump -uroot -p --databases db1 db2 db3 > 自定義名.sql;
./mysqldump -uroot -p --databases stusy > test_backup.sql;
因為我開啟了GTID,所以直接執行如上的命令行有報錯提示說:如果我只想完成數據的dump,需要在命令行中添加上它提示的那些參數。
# 如果你沒有開啟GTID選項,它提示我加的這些參數你都沒有必要添加的。
# --triggers 備份觸發器
# --routines 備份存儲過程和函數
# --events 備份事件調度器
./mysqldump --set-gtid-purged=OFF --databases stusy --triggers --routines --events -uroot -p > test_backup.sql;
查看產出的SQL文件:
注意點:使用參數 --databases 參數。最終產出的SQL中為你創建數據庫了。
文件開始和結束的部分有很多注釋,這些注釋可以用來設置MYSQL數據的各項參數。一般用來保證還原數據時可以更加有效准確的工作。
2、備份指定數據庫中的指定數據表: 通過參數 `--tables` 指定你要備份的數據表。
# mysqldump -uroot -p --databases db1 db2 db3 --tables t1 t2 > 自定義名.sql;
./mysqldump --set-gtid-purged=OFF --databases stusy --tables test_backup --triggers --routines --events -uroot -p > test_backup.sql;
3、對一個架構進行備份
不使用--databases
,直接寫數據庫名。對整庫架構進行備份
./mysqldump --set-gtid-purged=OFF --triggers --routines --events -uroot -p mysql> mysql_backup.sql;
查看備份的結果
注意點:相對於使用 --databases 參數來說。最終產出的SQL中!!沒有!!為你創建數據庫。
4、重點理解參數:--single-transaction
如果你想獲得一份“一致性備份”可以使用該參數。那什么是一致性備份呢?
todo 下面的:我勸!這位年輕人不講MVCC,耗子尾汁! 貼上鏈接。
添加--single-transaction
參數后,mysqldump會自動幫你執行 start transaction
開啟事務的SQL。如果你看過白日夢之前寫的 “我勸!這位年輕人不講MVCC,耗子尾汁!”,想必你一定了解,MVCC的實現原理,回到現在的這個問題中,也就是說,只要你執行開啟事務的語句就會得到一個一致性可重復讀的視圖(read view)。說白了:此次執行mysqldump得到的SQL文件中的數據,就是你執行的該命令的那個瞬間,打下的快照的數據。
注意:如果你不使用--single-transaction
參數,會自動添加上--lock-all-tables
。此外,還需要知道當我們使用參數--single-transaction
獲取到的那個一致性實圖並不能隔離DDL(表級別的操作,比如添加列)。所以你要確保在備份時沒有其他的DDL語句執行。
5、重點理解參數:--master-data
# 當值為1時,轉存文件中會有change master 語句。
--master-data = 1
# 當值為2時,轉存文件中當 change master 語句會被注釋。
--master-data = 2
下面分別讓 --master-data
為不同的值。查看產出。
./mysqldump --set-gtid-purged=OFF --databases stusy --tables test_backup --triggers --routines --events --master-data=2 -uroot -p > test_backup.sql;
./mysqldump --set-gtid-purged=OFF --databases stusy --tables test_backup --triggers --routines --events --master-data=1 -uroot -p > test_backup.sql;
一般搭建過mysql集群的同學都知道這條change master sql語句的作用是: 從庫認主庫的命令。
是的,使用參數--master-data=1
得到的備份文件通常主要做用是創建一個replication(從庫)。
上面介紹了工作中常用的幾種用法和注意點。
其實mysqldump支持的參數多達幾十個。你可以使用 --help查看它們。
如果上面的參數不能滿足你的需求。你可去官網查閱:https://dev.mysql.com/doc/refman/5.7/en/mysqldump.html
得先知道什么是GTID
GTID (global transcation identifier)它是MySQL5.6版本中添加進來的新特性 ,使用GTID可以唯一的標識一個事物。
我用大白話描述一下GTID常見的作用:
比如一條update有語句進入MySQL之后經歷如下過程:
1. 寫undolog
2. 寫redolog(prepare)
3. 寫binlog
4. 寫redolog(commit)
# 這也是所謂的兩階段提交
不管你有沒有自己搭建過MySQL集群,你一定聽說過MySQL集群!主庫將自己成功執行過的事物都寫在binlog,然后集群中的從庫會dump主庫記錄的binlog回放出數據,完成數據同步。當我們將GTID相關的配置打開后,update語句經歷如下過程:
1. 寫undolog # 回滾
2. 寫redolog(prepare)# 保證提交的不會丟失
3. 寫一個特殊的Binlog Event,類型為GTID_Event,指定下一個事務的GTID
4. 寫binlog # 主從同步事物使用
5. 寫redolog(commit)
也就是說mysql會在binlog中多為我們記錄一行gtid。這個gtid和當前事物唯一對應。不會重復。
這時當從庫向主庫發送同步數據當請求時:bin-log和gtid都會傳送到slave端,從庫在回放日志同步數據時,同樣會使用gtid寫bin-log,這樣主庫和從庫之間的數據,就通過GTID強制性的關聯並且保持同步了。
下圖截取自binlog一條事務,你可以看到里面會記錄gtid。
這時如果從庫想在主庫同步數據,只需要告訴主庫自己有哪些gtid就好了,主庫會把從庫沒有的gtid對應的事務日志給從庫讓它去同步數據。
而在這種方式出現之前,主從之間同步數據時,從庫需要告訴主庫自己已經同步到binlog.0000x,position=yyy的地方了。這個binlog.0000x,position=yyy需要人為的去查看一下。不能說查看這兩個信息比較麻煩,但是肯定不如GTID來的方便。
看一條binlog長啥樣
為了對小白友好一點,再看一下這張圖:
首先你得知道,像select這種查詢類型的sql,是不會被記錄進binlog中的,binlog中只會記錄對數據庫作出修改的寫入或者更新的sq。就像上圖中,你可以看我圖中begin、xxx、commit。
另外binlog中是有位點的,人們一般把稱它叫:position。其實所謂的位點就是上圖中的at xxx
中的xxx。
每一個事物都有自己的開啟、結束位點,換句話說我們可以通過開始和結束的位點找到一個或者是好多和事物。就上圖來說,這個事物的start-positon=956,stop-position=1230。
這個位點有啥用呢?
作用1:搭建主從集群時,通過下面的命令告訴從庫,應該從主庫的哪個binlog的哪個位點開始同步數據
CHANGE MASTER TO
MASTER_HOST='10.157.23.158',
MASTER_USER='mysqlsync',
MASTER_PASSWORD='mysqlsync123',
MASTER_PORT=8882,
MASTER_LOG_FILE='mysql-bin.000008',
MASTER_LOG_POS=1013; # 這就是位點
作用2:數據恢復時,指定從哪個位點恢復到哪個位點。或者跳過哪個位點,下面我們一起看下基於binlog的數據恢復。
如果你不曾搭建過集群,沒關系,歡迎關注白日夢,我后面會分享基於 binlog+position、基於gtid、基於docker+gtid搭建MySQL集群的方法。
數據恢復
不知道你有沒有誤刪過數據庫中的數據,之前我就誤刪過。不過還好是測試環境的。
其實誤刪數據后是可以通過binlog將數據恢復出來的。既然是使用binlog恢復數據,前提是你的MySQL開啟了binlog(默認情況下mysql不會幫你記錄binlog,如果你還不知道什么是binlog也沒關系,白日夢前面的文章有分享,你可以去看下)。
大部分情況下,DBA同學會將你使用的MySQL binlog打開。你可以像下面這樣驗證一下自己使用的數據庫binlog是否打開了。如果沒有打開binlog,數據可能真的沒辦法恢復。
線上的數據庫不斷承接流量,binlog會不斷滾動變大,你要趕在binlog被清理之前去恢復數據。
下面一起看看如何使用binlog恢復數據,下面看我的實驗步驟:
先查看我的所有的binlog:
然后我把數據庫中的數據全部刪除。
情況一:沒有開啟GITD
如果你的MySQL沒有開啟GTID。直接使用下面的命令,就能把你指定的binlog中指定范圍的positon的數據回放出來。
./mysqlbinlog start-positon=956,stop-position=1230 ../var/mysql-bin.000003 | ./mysql-uroot -p
除了用位點縮小范圍,還可以指定開始時間和結束時間來縮小范圍。
思考這樣的情況:
假設你沒有趕在binlog被清理之前去恢復數據,當你去恢復數據時上圖中delete sql之前的binlog已經被刪除了。那怎么辦?
這時你可以通過最近的全量備份把delete之前的數據恢復出來,然后delete之后的增量數據,通過mysqlbinlog工具恢復出來,注意別忘了通過positon跳過這個delete,不然一執行會放出來delete語句,數據又全被刪除了。
如果你沒有全量備份,binlog也不全了。那估計就懸了!
情況二:開啟GITD
開啟GTID的MySQL,同樣執行這行命令恢復數據會遇到下面的錯誤。
./mysqlbinlog start-positon=956,stop-position=1230 ../var/mysql-bin.000003 | ./mysql-uroot -p
如果你看了前面白日夢跟你介紹的什么是GTID,想必你已經知道為啥報錯了。因為你用binlog回放數據,其實就是讓mysql重新執行一下binlog中記錄的邏輯,問題就出在binlog中記錄了set next_gtid=xxx,因為gtid唯一的,是不能重復的。
所以需要添加參數--skip-gtids=true
[root@dev-changwu-01 bin]# ./mysqlbinlog --skip-gtids=true --start-position=684 --stop-position=1485 ../var/mysql-bin.000003 | ./mysql -uroot -p
Enter password:
推薦閱讀-白日夢的MySQL專題
- MySQL的修仙之路,圖文談談如何學MySQL、如何進階!(已發布)
- 面前突擊!33道數據庫高頻面試題,你值得擁有!(已發布)
- 大家常說的基數是什么?(已發布)
- 講講什么是慢查!如何監控?如何排查?(已發布)
- 對NotNull字段插入Null值有啥現象?(已發布)
- 能談談 date、datetime、time、timestamp、year的區別嗎?(已發布)
- 了解數據庫的查詢緩存和BufferPool嗎?談談看!(已發布)
- 你知道數據庫緩沖池中的LRU-List嗎?(已發布)
- 談談數據庫緩沖池中的Free-List?(已發布)
- 談談數據庫緩沖池中的Flush-List?(已發布)
- 了解臟頁刷回磁盤的時機嗎?(已發布)
- 用十一張圖講清楚,當你CRUD時BufferPool中發生了什么!以及BufferPool的優化!(已發布)
- 聽說過表空間沒?什么是表空間?什么是數據表?(已發布)
- 談談MySQL的:數據區、數據段、數據頁、數據頁究竟長什么樣?了解數據頁分裂嗎?談談看!(已發布)
- 談談MySQL的行記錄是什么?長啥樣?(已發布)
- 了解MySQL的行溢出機制嗎?(已發布)
- 說說fsync這個系統調用吧! (已發布)
- 簡述undo log、truncate、以及undo log如何幫你回滾事物! (已發布)
- 我勸!這位年輕人不講MVCC,耗子尾汁! (已發布)
- MySQL的崩潰恢復到底是怎么回事? (已發布)
- MySQL的binlog有啥用?誰寫的?在哪里?怎么配置 (已發布)
- MySQL的bin log的寫入機制 (已發布)
- 刪庫后!除了跑路還能干什么?(已發布)
推薦閱讀-二本應屆生的大學生活,已上岸百度
歡迎關注白日夢,走進白日夢的圈子~
下一篇:兩階段提交和分布式事務
一、事務的提交
二、簡單看下兩階段提交的流程
三、兩階段寫日志用意?
四、加餐:sync_binlog = 1 問題
五、兩階段提交設計的初衷 - 分布式事務
六、MySQL兩階段寫日志
七、再留一個彩蛋
參考: