刪庫跑路也是個老梗了,可見在運維數據庫的過程當中誤刪除數據,或者開發的代碼有bug,形成數據的誤刪除家常便飯。不過如今也有許多用於恢復或預防誤刪除的方案,例如SQL管理系統,將要執行的SQL先交由管理員審覈,而後由管理員備份一個鏡像數據庫,在鏡像上執行該SQL,並在執行後還原鏡像。這樣通過層層把關就能夠大大減少出現誤操做的概率。node
另外,利用binlog日誌也能夠恢復誤操做的數據,因此線上運行的數據庫都會開啓binlog日誌功能。還有就是本小節要介紹的延時節點:在Replication集羣中,能夠設置一個延時節點,該節點的數據同步時間要慢於集羣中的其餘節點,當其餘節點出現誤操做後,若延時節點的數據尚未被影響就能夠從延時節點進行恢復。python
但若是現有的數據庫組建的都是PXC集羣,沒有Replication集羣能夠採用該方案嗎?也是能夠的,PXC集羣與Replication集羣並不是是互斥的,咱們能夠將PXC集羣中的某個節點設置爲Master,而後增長一個延時節點設置爲Slave,讓這兩個節點構成Replication集羣進行數據同步便可。以下所示:
mysql
本小節就簡單演示一下如何搭建這種異構集羣下的延時節點,我這裏已經事先準備好了一個PXC集羣和一個用做延時節點的數據庫:
git
這裏使用PXC集羣中的PXC-Node3
做爲Master,讓其與DelayNode
組成主從,而DelayNode
天然就是做爲延時節點了。github
關於PXC集羣和Replication集羣的搭建能夠參考以下文章,這裏因爲篇幅有限就不進行說明了:sql
接下來開始動手實踐,首先須要將這兩個節點上的MySQL服務都給停掉:數據庫
systemctl stop mysqld
主從節點的配置文件都要開啓GTID,不然沒法利用延時節點找回數據。主節點須要增長的配置以下:vim
[root@PXC-Node3 ~]# vim /etc/percona-xtradb-cluster.conf.d/mysqld.cnf [mysqld] ... # 設置節點的id server_id=3 # 開啓binlog log_bin=mysql_bin # 開啓GTID gtid_mode=ON enforce_gtid_consistency=1
從節點須要增長的配置以下:bash
[root@delay-node ~]# vim /etc/my.cnf [mysqld] ... server_id=102 log_bin=mysql_bin # 從節點須要開啓relay_log relay_log=relay_bin gtid_mode=ON enforce_gtid_consistency=1
完成配置文件的配置後,啓動這兩個節點:運維
systemctl start mysqld
接着配置Slave對Master的主從關係,進入Master的MySQL命令行終端,經過以下語句查詢Master當前正在使用的二進制日誌及當前執行二進制日誌位置:
mysql> flush logs; -- 刷新日誌 mysql> show master status; +----------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +----------------------+----------+--------------+------------------+-------------------+ | PXC-Node3-bin.000003 | 154 | | | | +----------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec)
記錄下以上執行結果後,進入Slave的MySQL命令行終端,分別執行以下語句:
mysql> stop slave; -- 中止主從同步 mysql> change master to master_log_file='PXC-Node3-bin.000003', master_log_pos=154, master_host='192.168.190.134', master_port=3306, master_user='admin', master_password='Abc_123456'; -- 配置Master節點的鏈接信息,以及從Master二進制日誌的哪一個位置開始複製 mysql> start slave; -- 啓動主從同步
master_auto_position=1
參數便可配置完主從關係後,使用show slave status\G;
語句查看主從同步狀態,Slave_IO_Running
和Slave_SQL_Running
的值均爲Yes
才能表示主從同步狀態是正常的:
主從關係配置完成後,接着測試一下主從的數據同步是否正常。在Master上執行一些SQL語句,以下:
mysql> create database test_db; mysql> use test_db; mysql> CREATE TABLE `student` ( `id` int(11) NOT NULL, `name` varchar(20) NOT NULL, PRIMARY KEY (`id`) ); mysql> INSERT INTO `test_db`.`student`(`id`, `name`) VALUES (1, 'Jack');
執行完成後,看看Slave上是否有正常同步:
mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | | test_db | +--------------------+ 5 rows in set (0.00 sec) mysql> use test_db; mysql> select * from student; +----+------+ | id | name | +----+------+ | 1 | Jack | +----+------+ 1 row in set (0.00 sec) mysql>
驗證了主從節點能正常同步數據後,咱們就能夠設置Slave節點的同步延時了。在Slave節點上分別執行以下語句:
mysql> stop slave; mysql> change master to master_delay=1200; -- 設置同步延時爲1200秒 mysql> start slave;
一樣,從新配置了主從關係後,須要確認主從同步狀態是正常的:
接着演示下延時節點的做用,首先到Master節點上,將student
表中的數據給刪除掉,模擬誤刪除的狀況:
mysql> use test_db; mysql> delete from student; -- 刪除student表中的全部數據 mysql> select * from student; -- Master上已經查詢不到數據了 Empty set (0.00 sec) mysql>
此時,由於延時同步的緣由,在Slave節點上依舊可以正常查詢到被刪除的數據:
mysql> use test_db; mysql> select * from student; +----+------+ | id | name | +----+------+ | 1 | Jack | +----+------+ 1 row in set (0.00 sec) mysql>
如今就輪到GTID上場了,咱們得先讓Slave節點跳過刪除操做的GTID,後續才能讓Master從Slave上恢復數據。不然Slave同步了該GTID的話,Slave節點上的數據也會被刪除,即使在同步以前恢復了Master的數據也會形成主從數據不一致的問題。
GTID是記錄在binlog中的,因爲誤刪除操做是在Master上進行的,因此首先在Master節點上使用show master logs;
語句查詢binlog日誌名稱:
接下來咱們須要在binlog文件中找到誤刪除操做的記錄及其GTID,由於binlog文件的序號是遞增的,因此最近的操做通常記錄在序號最大的binlog文件中。所以執行show binlog events in 'PXC-Node3-bin.000003';
語句,並從結果集中查找誤刪除操做的記錄及其GTID。以下圖所示:
在Master節點上找到誤刪除操做的GTID後,複製該GTID。而後到Slave節點上分別執行以下語句:
mysql> stop slave; -- 中止主從同步 mysql> set gtid_next='d36eaafb-c653-ee15-4458-5d6bc793bd7a:4'; -- 設置須要跳過的GTID mysql> begin; commit; -- 開啓並提交事務,即模擬Slave同步了該GTID,後續就不會再進行同步,從而達到了跳過的效果 mysql> set gtid_next='automatic'; -- 恢復gtid的設置 mysql> change master to master_delay=0; -- 設置同步延時爲0是爲了立刻進行同步跳過該GTID mysql> start slave;
完成以上操做後,此時Slave上依舊存在着誤刪除的數據:
而Master上的student
表依舊爲空:
完成以上的操做後,恢復同步延時的設置:
mysql> stop slave; mysql> change master to master_delay=1200; -- 設置同步延時爲1200秒 mysql> start slave;
讓Slave節點跳過誤刪除操做的GTID後,就能夠開始恢復Master節點的數據了。首先中止業務系統對Master節點所在的PXC集羣的讀寫操做,避免還原的過程當中形成數據混亂。而後導出Slave節點的數據:
在Master節點上建立臨時庫,這是爲了先在臨時庫驗證了數據的正確性以後再導入到業務庫中,避免出現意外:
create database temp_db;
而後導入數據:
把Master節點上的數據表重命名:
rename table test_db.student to test_db.student_bak;
把臨時庫的數據表遷移到業務庫中:
rename table temp_db.student to test_db.student;
此時就成功恢復了Master節點上誤刪除的數據:
以前也提到了除延時節點這種解決方案外,使用binlog日誌也是能夠實現數據恢復的,這種恢復數據的方式一般被稱爲日誌閃回。這裏之因此還要介紹這種方案,是由於延時節點方案存在着必定的侷限性:一旦在延時階段沒有發現問題並解決問題的話,那麼當主從數據同步後,也沒法利用從節點去實現誤刪除的恢復。
日誌閃回方案相對於延時節點方案來講要簡單一些,不須要增長額外的節點,利用當前節點就能夠恢復數據。但該方案也並不是全能,例如binlog日誌不會記錄drop table
、truncate table
等操做所刪除的數據,那麼也就沒法經過日誌恢復了。不過這兩種方案並不衝突,能夠同時使用以提升數據恢復的可能性。
日誌閃回的前提是要開啓binlog日誌,而後經過一些閃回工具將binlog日誌解析成SQL,接着將SQL中的delete
語句轉換成insert
語句,或者找到被誤刪除的數據的insert
語句。最後將這些insert
語句從新在數據庫中執行一遍,這樣就實現了數據的恢復:
閃回工具備不少,本文中採用的是binlog2sql,它是大衆點評開源的基於Python編寫的MySQL日誌閃回工具。
該工具的安裝步驟以下:
# 安裝前置工具 [root@PXC-Node3 ~]# yum install -y epel-release [root@PXC-Node3 ~]# yum install -y git python3-pip # 克隆binlog2sql的源碼庫,並進入源碼目錄 [root@PXC-Node3 ~]# git clone https://github.com/danfengcao/binlog2sql.git && cd binlog2sql # 安裝binlog2sql所依賴的Python庫 [root@PXC-Node3 ~/binlog2sql]# pip3 install -r requirements.txt
在MySQL配置文件中配置以下參數,由於binlog2sql是基於row
格式的binlog進行解析的:
[mysqld] ... binlog_format = row binlog_row_image = full
我這裏有一張商品表,該表中有以下數據:
使用delete
語句刪除該表中的數據來模擬誤刪除:
delete from flash.goods;
而後再插入一些數據,模擬誤刪除後新增的數據:
INSERT INTO `flash`.`goods`(`id`, `name`, `desc`, `status`) VALUES (6, '蘋果', 'xxxx', '1'); INSERT INTO `flash`.`goods`(`id`, `name`, `desc`, `status`) VALUES (7, '香蕉', 'xxxx', '1');
恢復前的準備工做:
由於要恢復的是商品表,因此清空商品表的所有記錄:
delete from flash.goods;
以前也提到了最近的操做通常記錄在序號最大的binlog文件中,因此得查詢數據庫中的binlog文件名:
而後使用binlog2sql解析指定的binlog日誌,具體命令以下:
[root@PXC-Node3 ~/binlog2sql]# python3 binlog2sql/binlog2sql.py -uadmin -p'Abc_123456' -dflash -tgoods --start-file='PXC-Node3-bin.000003' > /home/PXC-Node3-bin.000003.sql
binlog2sql/binlog2sql.py
:被執行的Python文件-u
:用於鏈接數據庫的帳戶-p
:數據庫帳戶的密碼-d
:指定邏輯庫的名稱-t
:指定數據表的名稱--start-file
:指定須要解析的binlog的文件名/home/PXC-Node3-bin.000003.sql
:指定將解析生成的SQL寫到哪一個文件接着查看解析出來的SQL內容:cat /home/PXC-Node3-bin.000003.sql
。這裏截取了有用的部分,以下圖,能夠看到delete
語句和insert
語句都有咱們要恢復的數據:
能獲得這些語句接下來就簡單了,要麼將delete
語句轉換成insert
語句,要麼直接複製insert
部分的SQL語句到數據庫上執行便可。我這裏就直接複製insert
語句了:
INSERT INTO `flash`.`goods`(`id`, `name`, `desc`, `status`) VALUES (1, '蛋糕', '好吃', '1'); #start 3170 end 3363 time 2020-01-27 18:00:11 INSERT INTO `flash`.`goods`(`id`, `name`, `desc`, `status`) VALUES (2, '檸檬茶', '爽過吸大麻', '1'); #start 3459 end 3664 time 2020-01-27 18:00:56 INSERT INTO `flash`.`goods`(`id`, `name`, `desc`, `status`) VALUES (3, '豆奶', '好喝', '0'); #start 3760 end 3953 time 2020-01-27 18:01:10 INSERT INTO `flash`.`goods`(`id`, `name`, `desc`, `status`) VALUES (4, '窩窩頭', '一塊錢四個', '1'); #start 4049 end 4254 time 2020-01-27 18:01:37 INSERT INTO `flash`.`goods`(`id`, `name`, `desc`, `status`) VALUES (5, '雞腿', '雞你太美', '0'); #start 4350 end 4549 time 2020-01-27 18:02:08 INSERT INTO `flash`.`goods`(`id`, `name`, `desc`, `status`) VALUES (6, '蘋果', 'xxxx', '1'); #start 5052 end 5243 time 2020-01-27 18:06:24 INSERT INTO `flash`.`goods`(`id`, `name`, `desc`, `status`) VALUES (7, '香蕉', 'xxxx', '1'); #start 5339 end 5530 time 2020-01-27 18:06:24
執行完以上SQL後,能夠看到成功恢復了商品表中被刪除的數據: