刪庫后,咱除了跑路還能干什么?
小伙伴們大家好呀,我是小牛肉,今天分享一篇百度大佬的文章,刪庫跑路調(diào)侃歸調(diào)侃,如果實(shí)際發(fā)生了咱能做些什么挽回?fù)p失呢~
目錄:
前言
數(shù)據(jù)備份有哪些種?
實(shí)用的mysqldump備份方式
得先知道什么是GTID
看一條binlog長啥樣
數(shù)據(jù)恢復(fù)
下一篇:兩階段提交和分布式事務(wù)
前言
看完本篇你將了解:常見的數(shù)據(jù)庫備份方式、mysqldump實(shí)戰(zhàn)、一條binlog長啥樣、什么是gtid?什么是binlog位點(diǎn)?mysqlbinlog數(shù)據(jù)恢復(fù)實(shí)戰(zhàn)。
數(shù)據(jù)備份有哪些種?
MySQL中數(shù)據(jù)備份的方式還是蠻多的,常見的有冷備份、邏輯備份、熱備份、快照備份。
什么是冷備份?
所謂的冷備份,說白了就是在數(shù)據(jù)庫停止運(yùn)行的情況下,直接備份磁盤中MySQL用來存儲數(shù)據(jù)的那些數(shù)據(jù)文件。
在前面的文章中,白日夢跟大家分享過MySQL的表空間??催^那篇文章的同學(xué)都知道,MySQL中的數(shù)據(jù)最終都存儲在表空間中的。表空間 == 表空間文件。其實(shí)而所謂的空間,本質(zhì)上對應(yīng)著存在于操作系統(tǒng)磁盤上的肉眼能看到的物理文件。
下面你可以看一下我的MySQL的表空間文件都是怎么配置的,以及它們都在哪里。
MySQL版本:5.7 ,并且我在 my.cnf 配置文件中添加了如下的配置。
#?表示每一個(gè)數(shù)據(jù)庫單獨(dú)使用一個(gè)表空間
innodb_file_per_table=on
然后我創(chuàng)建數(shù)據(jù)庫:stusy。
創(chuàng)建數(shù)據(jù)表:test_backup。
進(jìn)入到如下的目錄中,你可以看到MySQL為我們創(chuàng)建的數(shù)據(jù)庫表創(chuàng)建出了單獨(dú)的目錄,而目錄中的有 .frm、.idb文件就是冷備份需要備份的文件。

什么是邏輯備份?
邏輯備份指的是使用 mysqldump 工具去備份數(shù)據(jù)。使用mysqldump進(jìn)行數(shù)據(jù)庫的邏輯備份也是在做的各位RD需要掌握的技能。日常開發(fā)中難免會有將線上的數(shù)據(jù)備份到測試環(huán)境使用的場景。
為啥說mysqldump是邏輯備份?原因大概是:你使用mysqldump去備份最終得到的參數(shù)其實(shí)是一堆sql,再通過回放sql的形式完成數(shù)據(jù)的恢復(fù)。白日夢之前的文章中跟大家分享過(可自行查看歷史文章哈)。在MySQL中數(shù)據(jù)表、數(shù)據(jù)行其實(shí)是邏輯存上的概念。像數(shù)據(jù)頁這種概念是物理真實(shí)存在的。所以你用mysqldump得到一堆sql,自然稱得上是邏輯備份嘍。
下文中具體說,mysqldump實(shí)戰(zhàn)。
什么是熱備份?
所謂熱備份其實(shí)是指:直接對運(yùn)行中的數(shù)據(jù)庫進(jìn)行備份。相對于冷備份,熱備份還是比較復(fù)雜的。你想啊,對處于運(yùn)行過程中的數(shù)據(jù)庫進(jìn)行備份,肯定就得將一些增量的數(shù)據(jù)也備份進(jìn)去。
通常人們會使用一款叫:xtraback 的工具完成數(shù)據(jù)庫的熱備份。
除此之外,我了解有一款Golang寫的開源工具 ghost,在github上還是挺火的。它是一款支持做無損DDL的工具(后面會專門有一篇文章講這個(gè)工具的原理)。這款工具在實(shí)現(xiàn)支持無損DDL功能時(shí),有一部分邏輯本質(zhì)上也是在支持增量數(shù)據(jù)的備份。
ghost的實(shí)現(xiàn)手段是:添加binlog監(jiān)聽事件,監(jiān)聽到binlog event后去解析binlog得到sql,再回放這個(gè)SQL。就像是從庫使用主庫對binlog進(jìn)行數(shù)據(jù)恢復(fù)一樣。
什么是快照備份?
再了解一下什么是快照備份:
快照備份不是數(shù)據(jù)庫本身提供的能力,本質(zhì)上它是借助于文件系統(tǒng)的快照功能來實(shí)現(xiàn)的對數(shù)據(jù)庫的備份。
我們知道的Linux服務(wù)器本質(zhì)上也是電腦的,它會有自己的磁盤,無論是固態(tài)硬盤,還是機(jī)械磁盤。反正會有這種固態(tài)存儲。還需要進(jìn)一步對磁盤進(jìn)行分區(qū)。然后才有將Linux文件系統(tǒng)中的目錄都會掛載在不同的分區(qū)上。這么做的目的,簡單來說就像你的window有C盤、D盤、E盤。D盤中的出問題后不會影響E盤一樣。
快照備份要求:數(shù)據(jù)庫的所有數(shù)據(jù)文件都要放在一個(gè)數(shù)據(jù)分區(qū)中。
常見的支持快照工具的文件系統(tǒng)和設(shè)備有:FreeBSD、UFS文件系統(tǒng)、Solaris的ZFS文件系統(tǒng)。GNU/Linux的LVM(Logical Volume Manager)
實(shí)用的mysqldump備份方式
本小節(jié)看幾個(gè)實(shí)戰(zhàn)mysqldump備份case。
測試環(huán)境:創(chuàng)建如下表
CREATE?TABLE?`test_backup2`?(
??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
??`name`?varchar(255)?DEFAULT?NULL,
??PRIMARY?KEY?(`id`)
)?ENGINE=InnoDB?DEFAULT?CHARSET=utf8;
數(shù)據(jù)表中寫入3條數(shù)據(jù)

mysqldump語法
mysqldump?[arguments]?>?file_name;
1、備份指定的數(shù)據(jù)庫
通過參數(shù)--databases?指定你要備份的數(shù)據(jù)庫
#?mysqldump??-uroot?-p?--databases?db1?db2?db3?>?自定義名.sql;?
./mysqldump??-uroot?-p?--databases?stusy?>?test_backup.sql;?

因?yàn)槲议_啟了GTID,所以直接執(zhí)行如上的命令行有報(bào)錯(cuò)提示說:如果我只想完成數(shù)據(jù)的dump,需要在命令行中添加上它提示的那些參數(shù)。
#?如果你沒有開啟GTID選項(xiàng),它提示我加的這些參數(shù)你都沒有必要添加的。
#??--triggers?備份觸發(fā)器
#??--routines?備份存儲過程和函數(shù)
#??--events?備份事件調(diào)度器
./mysqldump?--set-gtid-purged=OFF?--databases?stusy?--triggers?--routines?--events?-uroot?-p?>?test_backup.sql;?
查看產(chǎn)出的SQL文件:
注意點(diǎn):使用參數(shù) --databases 參數(shù)。最終產(chǎn)出的SQL中為你創(chuàng)建數(shù)據(jù)庫了。

文件開始和結(jié)束的部分有很多注釋,這些注釋可以用來設(shè)置MYSQL數(shù)據(jù)的各項(xiàng)參數(shù)。一般用來保證還原數(shù)據(jù)時(shí)可以更加有效準(zhǔn)確的工作。
2、備份指定數(shù)據(jù)庫中的指定數(shù)據(jù)表:通過參數(shù) `--tables` 指定你要備份的數(shù)據(jù)表。
#?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、對一個(gè)架構(gòu)進(jìn)行備份
不使用--databases,直接寫數(shù)據(jù)庫名。對整庫架構(gòu)進(jìn)行備份
./mysqldump?--set-gtid-purged=OFF?--triggers?--routines?--events?-uroot?-p?mysql>?mysql_backup.sql;?
查看備份的結(jié)果
注意點(diǎn):相對于使用 --databases 參數(shù)來說。最終產(chǎn)出的SQL中??!沒有??!為你創(chuàng)建數(shù)據(jù)庫。

4、重點(diǎn)理解參數(shù):--single-transaction
如果你想獲得一份“一致性備份”可以使用該參數(shù)。那什么是一致性備份呢?
添加--single-transaction參數(shù)后,mysqldump會自動幫你執(zhí)行?start transaction??開啟事務(wù)的SQL。如果你看過白日夢之前寫的 “我勸!這位年輕人不講MVCC,耗子尾汁!”,想必你一定了解,MVCC的實(shí)現(xiàn)原理,回到現(xiàn)在的這個(gè)問題中,也就是說,只要你執(zhí)行開啟事務(wù)的語句就會得到一個(gè)一致性可重復(fù)讀的視圖(read view)。說白了:此次執(zhí)行mysqldump得到的SQL文件中的數(shù)據(jù),就是你執(zhí)行的該命令的那個(gè)瞬間,打下的快照的數(shù)據(jù)。
注意:如果你不使用--single-transaction參數(shù),會自動添加上--lock-all-tables。此外,還需要知道當(dāng)我們使用參數(shù)--single-transaction獲取到的那個(gè)一致性實(shí)圖并不能隔離DDL(表級別的操作,比如添加列)。所以你要確保在備份時(shí)沒有其他的DDL語句執(zhí)行。
5、重點(diǎn)理解參數(shù):--master-data
#?當(dāng)值為1時(shí),轉(zhuǎn)存文件中會有change master 語句。
--master-data?=?1
#?當(dāng)值為2時(shí),轉(zhuǎn)存文件中當(dāng) change master 語句會被注釋。
--master-data?=?2
下面分別讓 ?--master-data?為不同的值。查看產(chǎn)出。
./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集群的同學(xué)都知道這條change master sql語句的作用是:從庫認(rèn)主庫的命令。
是的,使用參數(shù)--master-data=1得到的備份文件通常主要作用是創(chuàng)建一個(gè)replication(從庫)。
上面介紹了工作中常用的幾種用法和注意點(diǎn)。
其實(shí)mysqldump支持的參數(shù)多達(dá)幾十個(gè)。你可以使用 --help查看它們。
如果上面的參數(shù)不能滿足你的需求。你可去官網(wǎng)查閱:https://dev.mysql.com/doc/refman/5.7/en/mysqldump.html
得先知道什么是GTID
GTID (global transcation identifier)它是MySQL5.6版本中添加進(jìn)來的新特性 ,使用GTID可以唯一的標(biāo)識一個(gè)事物。
我用大白話描述一下GTID常見的作用:
比如一條update有語句進(jìn)入MySQL之后經(jīng)歷如下過程:
1.?寫undolog?
2.?寫redolog(prepare)
3.?寫binlog?
4.?寫redolog(commit)
#?這也是所謂的兩階段提交
不管你有沒有自己搭建過MySQL集群,你一定聽說過MySQL集群!主庫將自己成功執(zhí)行過的事物都寫在binlog,然后集群中的從庫會dump主庫記錄的binlog回放出數(shù)據(jù),完成數(shù)據(jù)同步。當(dāng)我們將GTID相關(guān)的配置打開后,update語句經(jīng)歷如下過程:
1.?寫undolog?#?回滾
2.?寫redolog(prepare)#?保證提交的不會丟失
3.?寫一個(gè)特殊的Binlog?Event,類型為GTID_Event,指定下一個(gè)事務(wù)的GTID?
4.?寫binlog?#?主從同步事物使用
5.?寫redolog(commit)
也就是說mysql會在binlog中多為我們記錄一行g(shù)tid。這個(gè)gtid和當(dāng)前事物唯一對應(yīng)。不會重復(fù)。
這時(shí)當(dāng)從庫向主庫發(fā)送同步數(shù)據(jù)的請求時(shí):bin-log和gtid都會傳送到slave端,從庫在回放日志同步數(shù)據(jù)時(shí),同樣會使用gtid寫bin-log,這樣主庫和從庫之間的數(shù)據(jù),就通過GTID強(qiáng)制性的關(guān)聯(lián)并且保持同步了。
下圖截取自binlog一條事務(wù),你可以看到里面會記錄gtid。

這時(shí)如果從庫想在主庫同步數(shù)據(jù),只需要告訴主庫自己有哪些gtid就好了,主庫會把從庫沒有的gtid對應(yīng)的事務(wù)日志給從庫讓它去同步數(shù)據(jù)。
而在這種方式出現(xiàn)之前,主從之間同步數(shù)據(jù)時(shí),從庫需要告訴主庫自己已經(jīng)同步到binlog.0000x,position=yyy的地方了。這個(gè)binlog.0000x,position=yyy需要人為的去查看一下。不能說查看這兩個(gè)信息比較麻煩,但是肯定不如GTID來的方便。
看一條binlog長啥樣
為了對小白友好一點(diǎn),再看一下這張圖:

首先你得知道,像select這種查詢類型的sql,是不會被記錄進(jìn)binlog中的,binlog中只會記錄對數(shù)據(jù)庫作出修改的寫入或者更新的sq。就像上圖中,你可以看我圖中begin、xxx、commit。
另外binlog中是有位點(diǎn)的,人們一般把稱它叫:position。其實(shí)所謂的位點(diǎn)就是上圖中的at xxx中的xxx。
每一個(gè)事物都有自己的開啟、結(jié)束位點(diǎn),換句話說我們可以通過開始和結(jié)束的位點(diǎn)找到一個(gè)或者是好多和事物。就上圖來說,這個(gè)事物的start-positon=956,stop-position=1230。
這個(gè)位點(diǎn)有啥用呢?
作用1:搭建主從集群時(shí),通過下面的命令告訴從庫,應(yīng)該從主庫的哪個(gè)binlog的哪個(gè)位點(diǎn)開始同步數(shù)據(jù)
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;?#?這就是位點(diǎn)
作用2:數(shù)據(jù)恢復(fù)時(shí),指定從哪個(gè)位點(diǎn)恢復(fù)到哪個(gè)位點(diǎn)?;蛘咛^哪個(gè)位點(diǎn),下面我們一起看下基于binlog的數(shù)據(jù)恢復(fù)。
如果你不曾搭建過集群,沒關(guān)系,歡迎關(guān)注白日夢,我后面會分享基于 binlog+position、基于gtid、基于docker+gtid搭建MySQL集群的方法。
數(shù)據(jù)恢復(fù)
不知道你有沒有誤刪過數(shù)據(jù)庫中的數(shù)據(jù),之前我就誤刪過。不過還好是測試環(huán)境的。
其實(shí)誤刪數(shù)據(jù)后是可以通過binlog將數(shù)據(jù)恢復(fù)出來的。既然是使用binlog恢復(fù)數(shù)據(jù),前提是你的MySQL開啟了binlog(默認(rèn)情況下mysql不會幫你記錄binlog,如果你還不知道什么是binlog也沒關(guān)系,白日夢前面的文章有分享,你可以去看下)。
大部分情況下,DBA同學(xué)會將你使用的MySQL binlog打開。你可以像下面這樣驗(yàn)證一下自己使用的數(shù)據(jù)庫binlog是否打開了。如果沒有打開binlog,數(shù)據(jù)可能真的沒辦法恢復(fù)。

線上的數(shù)據(jù)庫不斷承接流量,binlog會不斷滾動變大,你要趕在binlog被清理之前去恢復(fù)數(shù)據(jù)。
下面一起看看如何使用binlog恢復(fù)數(shù)據(jù),下面看我的實(shí)驗(yàn)步驟:
先查看我的所有的binlog:

然后我把數(shù)據(jù)庫中的數(shù)據(jù)全部刪除。

情況一:沒有開啟GITD
如果你的MySQL沒有開啟GTID。直接使用下面的命令,就能把你指定的binlog中指定范圍的positon的數(shù)據(jù)回放出來。
./mysqlbinlog?start-positon=956,stop-position=1230?../var/mysql-bin.000003?|?./mysql-uroot?-p
除了用位點(diǎn)縮小范圍,還可以指定開始時(shí)間和結(jié)束時(shí)間來縮小范圍。
思考這樣的情況:
假設(shè)你沒有趕在binlog被清理之前去恢復(fù)數(shù)據(jù),當(dāng)你去恢復(fù)數(shù)據(jù)時(shí)上圖中delete sql之前的binlog已經(jīng)被刪除了。那怎么辦?
這時(shí)你可以通過最近的全量備份把delete之前的數(shù)據(jù)恢復(fù)出來,然后delete之后的增量數(shù)據(jù),通過mysqlbinlog工具恢復(fù)出來,注意別忘了通過positon跳過這個(gè)delete,不然一執(zhí)行會放出來delete語句,數(shù)據(jù)又全被刪除了。
如果你沒有全量備份,binlog也不全了。那估計(jì)就懸了!
情況二:開啟GITD
開啟GTID的MySQL,同樣執(zhí)行這行命令恢復(fù)數(shù)據(jù)會遇到下面的錯(cuò)誤。
./mysqlbinlog?start-positon=956,stop-position=1230?../var/mysql-bin.000003?|?./mysql-uroot?-p

如果你看了前面白日夢跟你介紹的什么是GTID,想必你已經(jīng)知道為啥報(bào)錯(cuò)了。因?yàn)槟阌胋inlog回放數(shù)據(jù),其實(shí)就是讓mysql重新執(zhí)行一下binlog中記錄的邏輯,問題就出在binlog中記錄了set next_gtid=xxx,因?yàn)間tid唯一的,是不能重復(fù)的。
所以需要添加參數(shù)--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:在準(zhǔn)備面試或者有想要發(fā)布內(nèi)推信息的小伙伴,可以加我微信,我拉你進(jìn)【互聯(lián)網(wǎng)春|秋招交流群】,大家一起吐槽信息共享呀~
