<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          刪庫(kù)不跑路,我含淚寫(xiě)下MySQL數(shù)據(jù)恢復(fù)大法

          共 11121字,需瀏覽 23分鐘

           ·

          2022-03-02 11:42

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          作者:程淇銘

          來(lái)源:https://segmentfault.com/a/1190000020116271


          日常工作中,總會(huì)有因手抖、寫(xiě)錯(cuò)條件、寫(xiě)錯(cuò)表名、錯(cuò)連生產(chǎn)庫(kù)造成的誤刪庫(kù)表和數(shù)據(jù)的事情發(fā)生,那么,如果連數(shù)據(jù)都恢復(fù)不了,還要什么 DBA。

          前言

          數(shù)據(jù)恢復(fù)的前提的做好備份,且開(kāi)啟 binlog, 格式為 row。

          如果沒(méi)有備份文件,那么刪掉庫(kù)表后就真的刪掉了,lsof 中還有記錄的話,有可能恢復(fù)一部分文件,但若剛好數(shù)據(jù)庫(kù)沒(méi)有打開(kāi)這個(gè)表文件,那就只能跑路了。

          如果沒(méi)有開(kāi)啟 binlog,那么恢復(fù)數(shù)據(jù)后,從備份時(shí)間點(diǎn)開(kāi)始的數(shù)據(jù)都沒(méi)得了。

          如果 binlog 格式不為 row,那么在誤操作數(shù)據(jù)后就沒(méi)有辦法做閃回操作,只能老老實(shí)實(shí)地走備份恢復(fù)流程。

          直接恢復(fù)

          直接恢復(fù)是使用備份文件做全量恢復(fù),這是最常見(jiàn)的場(chǎng)景。

          mysqldump備份全量恢復(fù)

          使用 mysqldump 文件恢復(fù)數(shù)據(jù)非常簡(jiǎn)單,直接解壓了執(zhí)行
          gzip -d backup.sql.gz | mysql -u<user> -h<host> -P<port> -p

          xtrabackup備份全量恢復(fù)

          恢復(fù)過(guò)程
          # 步驟一:解壓(如果沒(méi)有壓縮可以忽略這一步)innobackupex?--decompress?<備份文件所在目錄># 步驟二:應(yīng)用日志innobackupex --apply-log <備份文件所在目錄> # 步驟三:復(fù)制備份文件到數(shù)據(jù)目錄innobackupex --datadir= --copy-back <備份文件所在目錄>

          基于時(shí)間點(diǎn)恢復(fù)

          基于時(shí)間點(diǎn)的恢復(fù)依賴(lài)的是binlog日志,需要從 binlog 中找過(guò)從備份點(diǎn)到恢復(fù)點(diǎn)的所有日志,然后應(yīng)用,我們測(cè)試一下

          新建測(cè)試表
          chengqm-3306>>show create table mytest.mytest \G;*************************** 1. row ***************************       Table: mytestCreate Table: CREATE TABLE `mytest` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `ctime` datetime DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8

          每秒插入一條數(shù)據(jù)
          [mysql@mysql-test ~]$ while true; do mysql -S /tmp/mysql.sock -e 'insert into mytest.mytest(ctime)values(now())';date;sleep 1;done

          備份
          [mysql@mysql-test ~]$ mysqldump --opt --single-transaction --master-data=2 --default-character-set=utf8 -S /tmp/mysql.sock -A > backup.sql

          找出備份時(shí)的日志位置
          [mysql@mysql-test ~]$ head -n 25 backup.sql | grep 'CHANGE MASTER TO MASTER_LOG_FILE'-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000032', MASTER_LOG_POS=39654;假設(shè)要恢復(fù)到 2019-08-09 11:01:54 這個(gè)時(shí)間點(diǎn),我們從 binlog 中查找從 39654019-08-09 11:01:54 的日志[mysql@mysql-test ~]$ mysqlbinlog --start-position=39654 --stop-datetime='2019-08-09 11:01:54' /data/mysql_log/mysql_test/mysql-bin.000032 > backup_inc.sql[mysql@mysql-test-83 ~]$ tail -n 20 backup_inc.sql......### INSERT INTO `mytest`.`mytest`### SET###   @1=161 /* INT meta=0 nullable=0 is_null=0 */###   @2='2019-08-09 11:01:53' /* DATETIME(0) meta=0 nullable=1 is_null=0 */......

          當(dāng)前數(shù)據(jù)條數(shù)
          -- 2019-08-09 11:01:54之前的數(shù)據(jù)條數(shù)chengqm-3306>>select count(*) from mytest.mytest where ctime < '2019-08-09 11:01:54';+----------+| count(*) |+----------+|      161 |+----------+1 row in set (0.00 sec)
          -- 所有數(shù)據(jù)條數(shù)chengqm-3306>>select count(*) from mytest.mytest;+----------+| count(*) |+----------+| 180 |+----------+1 row in set (0.00 sec)

          然后執(zhí)行恢復(fù)
          # 全量恢復(fù)[mysql@mysql-test ~]$ mysql -S /tmp/mysql.sock < backup.sql # 應(yīng)用增量日志[mysql@mysql-test ~]$ mysql -S /tmp/mysql.sock < backup_inc.sql

          檢查數(shù)據(jù)
          chengqm-3306>>select count(*) from mytest.mytest;+----------+| count(*) |+----------+|      161 |+----------+1 row in set (0.00 sec)
          chengqm-3306>>select * from mytest.mytest order by id desc limit 5;+-----+---------------------+| id | ctime |+-----+---------------------+| 161 | 2019-08-09 11:01:53 || 160 | 2019-08-09 11:01:52 || 159 | 2019-08-09 11:01:51 || 158 | 2019-08-09 11:01:50 || 157 | 2019-08-09 11:01:49 |+-----+---------------------+5 rows in set (0.00 sec)

          已經(jīng)恢復(fù)到 2019-08-09 11:01:54 這個(gè)時(shí)間點(diǎn)

          恢復(fù)一個(gè)表

          從mysqldump備份恢復(fù)一個(gè)表

          假設(shè)要恢復(fù)的表是 mytest.mytest
          # 提取某個(gè)庫(kù)的所有數(shù)據(jù)sed -n '/^-- Current Database: `mytest`/,/^-- Current Database:/p' backup.sql > backup_mytest.sql# 從庫(kù)備份文件中提取建表語(yǔ)句sed -e'/./{H;$!d;}' -e 'x;/CREATE TABLE `mytest`/!d;q' backup_mytest.sql > mytest_table_create.sql# 從庫(kù)備份文件中提取插入數(shù)據(jù)語(yǔ)句grep?-i?'INSERT?INTO?`mytest`'?backup_mytest.sql?>?mytest_table_insert.sql
          # 恢復(fù)表結(jié)構(gòu)到 mytest 庫(kù)mysql -u -p mytest < mytest_table_create.sql
          # 恢復(fù)表數(shù)據(jù)到 mytest.mytest 表mysql -u -p mytest < mytest_table_insert.sql

          從xtrabackup備份恢復(fù)一個(gè)表

          假設(shè) ./backup_xtra_full 目錄為解壓后應(yīng)用過(guò)日志的備份文件

          MyISAM 表

          假設(shè)從備份文件中恢復(fù)表 mytest.t_myisam,從備份文件中找到 t_myisam.frm t_myisam.MYD t_myisam.MYI 這 3 個(gè)文件,復(fù)制到對(duì)應(yīng)的數(shù)據(jù)目錄中,并授權(quán)。

          進(jìn)入 MySQL,檢查表情況
          chengqm-3306>>show tables;+------------------+| Tables_in_mytest |+------------------+| mytest           || t_myisam         |+------------------+2 rows in set (0.00 sec)
          chengqm-3306>>check table t_myisam;+-----------------+-------+----------+----------+| Table | Op | Msg_type | Msg_text |+-----------------+-------+----------+----------+| mytest.t_myisam | check | status | OK |+-----------------+-------+----------+----------+1 row in set (0.00 sec)

          Innodb 表

          假設(shè)從備份文件中恢復(fù)表 mytest.t_innodb,恢復(fù)前提是設(shè)置了 innodb_file_per_table = on

          • 起一個(gè)新實(shí)例
          • 在實(shí)例上建一個(gè)和原來(lái)一模一樣的表
          • 執(zhí)行 alter table t_innodb discard tablespace;,刪除表空間,這個(gè)操作會(huì)把 t_innodb.ibd 刪除
          • 從備份文件中找到 t_innodb.ibd 這個(gè)文件,復(fù)制到對(duì)應(yīng)的數(shù)據(jù)目錄,并授權(quán)
          • 執(zhí)行 alter table t_innodb IMPORT tablespace; 加載表空間
          • 執(zhí)行 flush table t_innodb;check table t_innodb; 檢查表
          • 使用 mysqldump 導(dǎo)出數(shù)據(jù),然后再導(dǎo)入到要恢復(fù)的數(shù)據(jù)庫(kù)


          注意:

          1、在新實(shí)例上恢復(fù)再dump出來(lái)是為了避免風(fēng)險(xiǎn),如果是測(cè)試,可以直接在原庫(kù)上操作步驟 2-6

          2、只在 8.0 以前的版本有效

          跳過(guò)誤操作SQL

          跳過(guò)誤操作 SQL 一般用于執(zhí)行了無(wú)法閃回的操作比如 drop table\database

          使用備份文件恢復(fù)跳過(guò)

          不開(kāi)啟 GTID

          使用備份文件恢復(fù)的步驟和基于時(shí)間點(diǎn)恢復(fù)的操作差不多,區(qū)別在于多一個(gè)查找 binlog 操作

          舉個(gè)例子,我這里建立了兩個(gè)表 a 和 b,每分鐘插入一條數(shù)據(jù),然后做全量備份,再刪除表 b,現(xiàn)在要跳過(guò)這條 SQL。

          刪除表 b 后的數(shù)據(jù)庫(kù)狀態(tài)
          chgnqm-3306>>show tables;+------------------+| Tables_in_mytest |+------------------+| a                |+------------------+1 row in set (0.00 sec)

          1 找出備份時(shí)的日志位置
          [mysql@mysql-test ~]$ head -n 25 backup.sql | grep 'CHANGE MASTER TO MASTER_LOG_FILE'-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000034', MASTER_LOG_POS=38414;

          2 找出執(zhí)行了 drop table 語(yǔ)句的 pos 位置
          [mysql@mysql-test mysql_test]$  mysqlbinlog -vv /data/mysql_log/mysql_test/mysql-bin.000034 | grep -i -B 3 'drop table `b`';# at 120629#190818 19:48:30 server id 83  end_log_pos 120747 CRC32 0x6dd6ab2a     Query    thread_id=29488    exec_time=0    error_code=0SET TIMESTAMP=1566128910/*!*/;DROP TABLE `b` /* generated by server */

          從結(jié)果中我們可以看到 drop 所在語(yǔ)句的開(kāi)始位置是 120629,結(jié)束位置是 120747

          3 從 binglog 中提取跳過(guò)這條語(yǔ)句的其他記錄
          # 第一條的 start-position 為備份文件的 pos 位置,stop-position 為 drop 語(yǔ)句的開(kāi)始位置mysqlbinlog -vv --start-position=38414 --stop-position=120629 /data/mysql_log/mysql_test/mysql-bin.000034 > backup_inc_1.sql

          # 第二條的 start-position 為 drop 語(yǔ)句的結(jié)束位置mysqlbinlog -vv --start-position=120747 /data/mysql_log/mysql_test/mysql-bin.000034 > backup_inc_2.sql

          4 恢復(fù)備份文件
          [mysql@mysql-test ~]$ mysql -S /tmp/mysql.sock < backup.sql

          全量恢復(fù)后狀態(tài)
          chgnqm-3306>>show tables;+------------------+| Tables_in_mytest |+------------------+| a                || b                |+------------------+2 rows in set (0.00 sec)
          chgnqm-3306>>select count(*) from a;+----------+| count(*) |+----------+| 71 |+----------+1 row in set (0.00 sec)

          5 恢復(fù)增量數(shù)據(jù)
          [mysql@mysql-test ~]$ mysql -S /tmp/mysql.sock < backup_inc_1.sql[mysql@mysql-test ~]$ mysql -S /tmp/mysql.sock < backup_inc_2.sql

          恢復(fù)后狀態(tài),可以看到已經(jīng)跳過(guò)了 drop 語(yǔ)句
          chgnqm-3306>>show tables;+------------------+| Tables_in_mytest |+------------------+| a                || b                |+------------------+2 rows in set (0.00 sec)
          chgnqm-3306>>select count(*) from a;+----------+| count(*) |+----------+| 274 |+----------+1 row in set (0.00 sec)

          開(kāi)啟 GTID

          使用 GTID 可以直接跳過(guò)錯(cuò)誤的 SQL

          • 出備份時(shí)的日志位置
          • 找出執(zhí)行了 drop table 語(yǔ)句的 GTID 值
          • 導(dǎo)出備份時(shí)日志位置到最新的 binglog 日志
          • 恢復(fù)備份文件
          • 跳過(guò)這個(gè) GTID

          SET SESSION GTID_NEXT='對(duì)應(yīng)的 GTID 值';BEGIN; COMMIT;SET SESSION GTID_NEXT = AUTOMATIC;
          • 應(yīng)用步驟 3 得到的增量 binlog 日志


          使用延遲庫(kù)跳過(guò)

          不開(kāi)啟 GTID

          使用延遲庫(kù)恢復(fù)的關(guān)鍵操作在于 start slave until

          我在測(cè)試環(huán)境搭建了兩個(gè) MySQL 節(jié)點(diǎn),節(jié)點(diǎn)二延遲600秒,新建 a,b 兩個(gè)表,每秒插入一條數(shù)據(jù)模擬業(yè)務(wù)數(shù)據(jù)插入。
          localhost:3306 -> localhost:3307(delay 600)

          當(dāng)前節(jié)點(diǎn)二狀態(tài)
          chengqm-3307>>show slave status \G;...                  Master_Port: 3306                Connect_Retry: 60              Master_Log_File: mysql-bin.000039          Read_Master_Log_Pos: 15524               Relay_Log_File: mysql-relay-bin.000002                Relay_Log_Pos: 22845        Relay_Master_Log_File: mysql-bin.000038             Slave_IO_Running: Yes            Slave_SQL_Running: Yes...        Seconds_Behind_Master: 600...

          當(dāng)前節(jié)點(diǎn)二表
          chengqm-3307>>show tables;+------------------+| Tables_in_mytest |+------------------+| a                || b                |+------------------+

          在節(jié)點(diǎn)一刪除表 b
          chengqm-3306>>drop table b;Query OK, 0 rows affected (0.00 sec)
          chengqm-3306>>show tables;+------------------+| Tables_in_mytest |+------------------+| a |+------------------+1 row in set (0.00 sec)

          接下來(lái)就是跳過(guò)這條 SQL 的操作步驟

          1 延遲庫(kù)停止同步
          stop slave;

          2 找出執(zhí)行了 drop table 語(yǔ)句的前一句的 pos 位置
          [mysql@mysql-test ~]$ mysqlbinlog -vv /data/mysql_log/mysql_test/mysql-bin.000039 | grep -i -B 10 'drop table `b`';...# at 35134#190819 11:40:25 server id 83  end_log_pos 35199 CRC32 0x02771167     Anonymous_GTID    last_committed=132    sequence_number=133    rbr_only=noSET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;# at 35199#190819 11:40:25 server id 83  end_log_pos 35317 CRC32 0x50a018aa     Query    thread_id=37155    exec_time=0    error_code=0use `mytest`/*!*/;SET TIMESTAMP=1566186025/*!*/;DROP TABLE `b` /* generated by server */

          從結(jié)果中我們可以看到 drop 所在語(yǔ)句的前一句開(kāi)始位置是 35134,所以我們同步到 35134 (這個(gè)可別選錯(cuò)了)

          3 延遲庫(kù)同步到要跳過(guò)的 SQL 前一條
          change master to master_delay=0;start slave until master_log_file='mysql-bin.000039',master_log_pos=35134;

          查看狀態(tài)看到已經(jīng)同步到對(duì)應(yīng)節(jié)點(diǎn)
          chengqm-3307>>show slave status \G;...                  Master_Port: 3306                Connect_Retry: 60              Master_Log_File: mysql-bin.000039          Read_Master_Log_Pos: 65792...             Slave_IO_Running: Yes            Slave_SQL_Running: No          Exec_Master_Log_Pos: 35134...               Until_Log_File: mysql-bin.000039                Until_Log_Pos: 35134

          4 跳過(guò)一條 SQL 后開(kāi)始同步
          set global sql_slave_skip_counter=1;start slave;

          查看同步狀態(tài),刪除表 b 的語(yǔ)句已經(jīng)被跳過(guò)
          chengqm-3307>>show slave status \G;...             Slave_IO_Running: Yes            Slave_SQL_Running: Yes...1 row in set (0.00 sec)
          chengqm-3307>>show tables;+------------------+| Tables_in_mytest |+------------------+| a || b |+------------------+2 rows in set (0.00 sec)

          開(kāi)啟 GTID

          使用 GTID 跳過(guò)的步驟會(huì)簡(jiǎn)單很多,只要執(zhí)行一條和要跳過(guò)的 SQL 的 GTID 相同的事務(wù)就可以跳過(guò)了

          • 停止同步
          • 找出執(zhí)行了 drop table 語(yǔ)句的 GTID
          • 執(zhí)行這個(gè) GTID 的事務(wù)

          SET SESSION GTID_NEXT='對(duì)應(yīng)的 GTID 值';BEGIN; COMMIT;SET SESSION GTID_NEXT = AUTOMATIC;
          • 繼續(xù)同步


          閃回

          閃回操作就是反向操作,比如執(zhí)行了 delete from a where id=1,閃回就會(huì)執(zhí)行對(duì)應(yīng)的插入操作 insert into a (id,...) values(1,...),用于誤操作數(shù)據(jù),只對(duì) DML 語(yǔ)句有效,且要求 binlog 格式設(shè)為 ROW。本章介紹兩個(gè)比較好用的開(kāi)源工具

          binlog2sql

          binlog2sql 是大眾點(diǎn)評(píng)開(kāi)源的一款用于解析 binlog 的工具,可以用于生成閃回語(yǔ)句,項(xiàng)目地址 binlog2sql(https://sourl.cn/ZuNJPN)

          安裝
          wget https://github.com/danfengcao/binlog2sql/archive/master.zip -O binlog2sql.zipunzip binlog2sql.zipcd binlog2sql-master/
          # 安裝依賴(lài)pip install -r requirements.txt

          生成回滾SQL
          python binlog2sql/binlog2sql.py --flashback \-h<host> -P<port> -u<user> -p'<password>' -d<dbname> -t<table_name>\--start-file='<binlog_file>' \--start-datetime='<start_time>' \--stop-datetime='<stop_time>' > ./flashback.sql
          python binlog2sql/binlog2sql.py --flashback \-h<host> -P<port> -u<user> -p'<password>' -d<dbname> -t<table_name> \--start-file='<binlog_file>' \--start-position=<start_pos> \--stop-position=<stop_pos> > ./flashback.sql

          MyFlash

          MyFlash 是由美團(tuán)點(diǎn)評(píng)公司技術(shù)工程部開(kāi)發(fā)維護(hù)的一個(gè)回滾 DML 操作的工具,項(xiàng)目鏈接 MyFlash

          限制:

          • binlog格式必須為row,且 binlog_row_image=full
          • 僅支持5.6與5.7
          • 只能回滾DML(增、刪、改)


          安裝
          # 依賴(lài)(centos)yum install gcc*  pkg-config glib2 libgnomeui-devel -y# 下載文件wget https://github.com/Meituan-Dianping/MyFlash/archive/master.zip -O MyFlash.zipunzip MyFlash.zipcd MyFlash-master# 編譯安裝gcc -w  `pkg-config --cflags --libs glib-2.0` source/binlogParseGlib.c  -o binary/flashbackmv binary /usr/local/MyFlashln -s /usr/local/MyFlash/flashback /usr/bin/flashback

          使用

          生成回滾語(yǔ)句
          flashback --databaseNames=<dbname> --binlogFileNames=<binlog_file> --start-position=<start_pos> --stop-position=<stop_pos> 

          執(zhí)行后會(huì)生成 binlog_output_base.flashback 文件,需要用 mysqlbinlog 解析出來(lái)再使用。
          mysqlbinlog -vv binlog_output_base.flashback | mysql -u<user> -p

          ? ???

          1、來(lái)自谷歌的開(kāi)發(fā)心得:所有SQL和代碼,都沒(méi)必要藏著掖著

          2、用了這么久的 Chrome,你不會(huì)還沒(méi)掌握這個(gè)功能吧?

          3、一個(gè)比SpringBoot快44倍的Java框架

          4、QQ最近上線的兩個(gè)新功能,把我人都看傻了!

          5、SpringBoot 三大開(kāi)發(fā)工具,你都用過(guò)么?

          點(diǎn)分享

          點(diǎn)收藏

          點(diǎn)點(diǎn)贊

          點(diǎn)在看

          瀏覽 49
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  老太色HD色老太HD - 百度 | 欧美色图亚洲图片插菊花综合 | 亚洲精品乱伦视频 | 四虎A片| 欧美日韩黄色一级 |