一、初步了解binlog
1、MySQL的二進制日志binlog可以說是MySQL最重要的日志,它記錄了所有的DDL和DML語句(除了數據查詢語句select),以事件形式記錄,還包含語句所執行的消耗的時間,MySQL的二進制日志是事務安全型的。
a、DDL
----Data Definition Language 數據庫定義語言
主要的命令有create、alter、drop等,ddl主要是用在定義或改變表(table)的結構,數據類型,表之間的連接和約束等初始工作上,他們大多在建表時候使用。
b、DML
----Data Manipulation Language 數據操縱語言
主要命令是slect,update,insert,delete,就像它的名字一樣,這4條命令是用來對數據庫里的數據進行操作的語言
2、mysqlbinlog常見的選項有一下幾個:
a、--start-datetime:從二進制日志中讀取指定等於時間戳或者晚於本地計算機的時間
b、--stop-datetime:從二進制日志中讀取指定小於時間戳或者等於本地計算機的時間 取值和上述一樣
c、--start-position:從二進制日志中讀取指定position 事件位置作為開始。
d、--stop-position:從二進制日志中讀取指定position 事件位置作為事件截至
3、一般來說開啟binlog日志大概會有1%的性能損耗。
4、binlog日志有兩個最重要的使用場景。
a、mysql主從復制:mysql replication在master端開啟binlog,master把它的二進制日志傳遞給slaves來達到master-slave數據一致的目的。
b、數據恢復:通過mysqlbinlog工具來恢復數據。
binlog日志包括兩類文件:
1)、二進制日志索引文件(文件名后綴為.index)用於記錄所有的二進制文件。
2)、二進制日志文件(文件名后綴為.00000*)記錄數據庫所有的DDL和DML(除了數據查詢語句select)語句事件。
二、開啟binlog日志
1、編輯打開mysql配置文件/application/mysql3307/my.cnf在
[mysqld]區塊添加
log-bin=mysql-bin(也可指定二進制日志生成的路徑,如:log-bin=/opt/Data/mysql-bin)
server-id=1
binlog_format=MIXED(加入此參數才能記錄到insert語句)
2、重啟mysqld服務
/application/mysql3307/bin/mysqladmin -uroot -S /application/mysql3307/logs/mysql.sock -p shutdown
nohup /application/mysql3307/bin/mysqld_safe --defaults-file=/application/mysql3307/my.cnf --user=mysql &
3、查看binlog日志是否開啟
mysql> show variables like 'log_%';

三、常用的binlog日志操作命令
1、查看所有binlog日志列表
show master logs;

2、查看master狀態,即最后(最新)一個binlog日志的編號名稱,及其最后一個操作事件pos結束點(Position)值。
show master status;

3、flush 刷新log日志,自此刻開始產生一個新編號的binlog日志文件;
flush logs;
注意:每當mysqld服務重啟時,會自動執行此命令,刷新binlog日志;在mysqlddump備份數據時加-F選項也會刷新binlog日志;
4、重置(清空)所有binlog日志
reset master;
四、查看binlog日志內容,常用有兩種方式:
1、使用mysqlbinlog自帶查看命令法
注意:
a、binlog是二進制文件,普通文件查看器cat、more、vim等都無法打開,必須使用自帶的mysqlbinlog命令查看。
b、binlog日志與數據庫文件在同目錄中。
c、在Mysql5.5以下版本使用mysqlbinlog命令時如果報錯,就加上"--no-defaults"選項
d、使用mysqlbinlog命令查看binlog日志內容,下面截取其中的一個片段分析分析:

解釋:
server id 1:數據庫主機的服務號
end_log_pos 796 :sql結束時的pos節點
thread_id=11:線程號
e、也可根據時間點查看
/home/software/mysql-5.1.72-linux-x86_64-glibc23/bin/mysqlbinlog --no-defaults mysql-bin.000720 --start-datetime="2018-09-12 18:45:00" --stop-datetime="2018-09-12:18:47:00"
2、上面這種辦法讀取出binlog日志的全文內容比較多,不容易分辨查看到pos點信息,下面介紹一種更為方便的查詢命令:
mysql> show binlog events [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count];
參數解釋:
a、IN 'log_name':指定要查詢的binlog文件名(不指定就是第一個binlog文件)
b、FROM pos:指定從哪個pos起始點開始查起(不指定就是從整個文件首個pos點開始算)
c、LIMIT【offset】:偏移量(不指定就是0)
d、row_count :查詢總條數(不指定就是所有行)

2、上面這條語句可以將指定的binlog日志文件,分成有效事件行的方式返回,並可使用limit指定pos點的起始偏移,查詢條數!
a、查詢第一個最早的binlog日志:
show binlog events\G;
b、指定查詢mysql-bin.000002這個文件
show binlog events in 'mysql-bin.000002'\G;
c、指定查詢mysql-bin.000002這個文件,從pos點:624開始查起:
show binlog events in 'mysql-bin.000002' from 624\G;
d、指定查詢mysql-bin.000002這個文件,從pos點:624開始查起,查詢10條(即10條語句)
show binlog events in 'mysql-bin.000002' from 624 limit 10\G;
e、指定查詢 mysql-bin.000002這個文件,從pos點:624開始查起,偏移2行(即中間跳過2個)查詢10條(即10條語句)。
show binlog events in 'mysql-bin.000002' from 624 limit 2,10\G;
五、利用binlog日志恢復mysql數據
1、以下對ops庫的member表進行操作,並且再創建一個庫ops1
create database ops;
create database ops1;
use ops;
CREATE TABLE IF NOT EXISTS `member` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(16) NOT NULL,`sex` enum('m','w') NOT NULL DEFAULT 'm',`age` tinyint(3) unsigned NOT NULL,`classid` char(6) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
show tables;
use ops1;
CREATE TABLE IF NOT EXISTS `member` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(16) NOT NULL,`sex` enum('m','w') NOT NULL DEFAULT 'm',`age` tinyint(3) unsigned NOT NULL,`classid` char(6) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
show tables;
事先插入兩條數據:
use ops;
insert into member(`name`,`sex`,`age`,`classid`) values('wangshibo','m',27,'cls1'),('guohuihui','w',27,'cls2');
use ops1;
insert into member(`name`,`sex`,`age`,`classid`) values('wangshibo','m',27,'cls1'),('guohuihui','w',27,'cls2');
2、下面開始進行場景模擬:
a、ops庫會在每天凌晨四點進行一次完全備份的定時計划任務,如下:
0 4 * * * /application/mysql3306/bin/mysqldump -uroot -S /application/mysql3306/logs/mysql.sock -p123456 -B -F -R -x --master-data=2 ops ops1|gzip >/application/data/backup/ops_$(date +%F).sql.gz這里我們可以手動執行一下
b、參數說明:
-B:指定數據庫
-F:刷新日志
-R:備份存儲過程等
-x:鎖表
--master-data:在備份語句里添加CHANGE MASTER語句以及binlog文件及位置點信息。
待到數據庫備份完成,就不用擔心數據丟失了,因為有完全備份數據在,由於上面在全備份的時候使用了-F選項,那么當數據備份操作剛開始的時候系統就會自動刷新log,這樣就會自動產生一個新的binlog日志,這個新的binlog日志就會用來記錄備份之后的數據庫'增刪改操作'。
查看一下:

也就是說,mysql-bin.000003是用來記錄4:00之后對數據庫的所有'增刪改操作'。
3、早上九點上班了,由於業務的需求會對數據庫進行各種'增刪改'操作。
比如:在ops庫下和ops1庫下member表內插入、修改了數據等等:
先是早上進行插入數據:
insert into ops.member(`name`,`sex`,`age`,`classid`) values('yiyi','w',20,'cls1'),('xiaoer','m',22,'cls3'),('zhangsan','w',21,'cls5'),('lisi','m',20,'cls4'),('wangwu','w',26,'cls6');
insert into ops1.member(`name`,`sex`,`age`,`classid`) values('yiyi','w',20,'cls1'),('xiaoer','m',22,'cls3'),('zhangsan','w',21,'cls5'),('lisi','m',20,'cls4'),('wangwu','w',26,'cls6');

4、中午又執行了修改數據庫操作:
update ops.member set name='李四' where id=4;
update ops1.member set name='李四' where id=4;
update ops.member set name='小二' where id=2;
update ops1.member set name='小二' where id=2;

5、在下午18:00的時候,悲劇莫名其妙的出現了!
手賤執行了drop語句,直接刪除了ops1庫!嚇尿!
drop database ops;
drop database ops1;
再手殘又創建了一個數據庫ops2並插入數據
create database ops2;
use ops2;
CREATE TABLE IF NOT EXISTS `member` (`id` int(10) unsigned NOT NULL AUTO_INCREMENT,`name` varchar(16) NOT NULL,`sex` enum('m','w') NOT NULL DEFAULT 'm',`age` tinyint(3) unsigned NOT NULL,`classid` char(6) DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into ops2.member(`name`,`sex`,`age`,`classid`) values('yiyi','w',20,'cls1'),('xiaoer','m',22,'cls3'),('zhangsan','w',21,'cls5'),('lisi','m',20,'cls4'),('wangwu','w',26,'cls6');
6、這種時候,一定不要慌張!!!
先仔細查看最后一個binlog日志,並記錄下關鍵的pos點,到底是哪個pos點的操作導致了數據庫的破壞(通常在最后幾步);
a、先備份一下最后一個binlog日志文件:
cd /application/mysql3306/mysql_data
cp -v mysql-bin.000004 /application/data/backup/
ls /application/data/backup/

b、接着執行一次刷新日志索引操作,重新開始新的binlog日志記錄文件。按理說mysql-bin.000004這個文件不會再有后續寫入了,因為便於我們分析原因及查找ops節點,以后所有數據庫操作都會寫入到下一個日志文件。
flush logs;
show master status;

7、讀取binlog日志的方法上面已經說到。
a、方法一:使用mysqlbinlog讀取binlog日志:
/application/mysql3306/bin/mysqlbinlog /application/mysql3306/mysql_data/mysql-bin.000004
b、登錄服務器,並查看(推薦此種方法)
show binlog events in 'mysql-bin.000003';

c、或者:
show binlog events in 'mysql-bin.000004'\G;

通過分析,造成庫ops數據破壞的pos點區間是介於3064-3153之間(這是按照日志區間的pos節點算的),造成庫ops1庫破壞的pos區間是介於3218-3310之間,只要恢復到相應pos點之前就可以了。
8、先把凌晨4點全備的數據恢復(建議另起一個庫,等恢復成功后再替換掉當前庫即可)
cd /application/data/backup/
gzip -d ops_2018-09-11.sql.gz
/application/mysql3307/bin/mysql -uroot -S /application/mysql3307/logs/mysql.sock -p123456 <ops_2018-09-11.sql
這樣就恢復了截止凌晨4:00前的備份數據了

但是這僅僅只是恢復了當天凌晨4點之前的數據,在4:00--18:00之間的數據還沒有恢復回來!!怎么辦呢?莫慌!這可以根據前面提到的mysql-bin.000004的新binlog日志進行恢復。
9、從binlog日志恢復數據
a、恢復命令的語法格式:
mysqlbinlog mysql-bin.0000xx | mysql -u用戶名 -p密碼 數據庫名
b、常用參數選項解釋:
--start-position=875 起始pos點
--stop-position=954 結束pos點
--start-datetime="2016-9-25 22:01:08" 起始時間點
--stop-datetime="2019-9-25 22:09:46" 結束時間點
--database=ops指定只恢復ops數據庫(一台主機上往往有多個數據庫,只限本地log日志)
c、不常用選項:
-u --user=name 連接到遠程主機的用戶名
-p --password[=name]連接到遠程主機的密碼
-h --host=name 從遠程主機上獲取binlog日志
--read-from-remote-server從某個Mysql服務器上讀取binlog日志
d、小結:實際是將讀出的binlog日志內容,通過管道符傳遞給myslq命令。這些命令,文件盡量寫成絕對路徑;
e、完全恢復(需要手動vim編輯mysql-bin.000003,將那條drop語句剔除掉)(此方法測試未通過)
/application/mysql3306/bin/mysqlbinlog /application/mysql3306/mysql_data/mysql-bin.000004 | /application/mysql3307/bin/mysql -uroot -S /application/mysql3307/logs/mysql.sock -p123456 -v ops
f、指定pos結束點恢復(部分恢復):
/application/mysql3306/bin/mysqlbinlog --stop-position=3064 --database=ops /application/mysql3306/mysql_data/mysql-bin.000002 | /application/mysql3307/bin/mysql -uroot -S /application/mysql3307/logs/mysql.sock -p123456 -v(因為加了--database=ops因此不會恢復二進制日志中關於ops1庫的相應操作,若也需要恢復ops1庫的相應操作,則再加上--database=ops1即可)
g、指定pos點區間恢復(部分恢復)
在f環節我們已經恢復到了刪庫之前的時刻,在刪庫后我們還做了創建ops2庫並創建了member表和增加了數據的操作,此時我們要跳過刪庫並且恢復到創建ops2庫和創建member表的時刻可以采用區間pos點恢復:

/application/mysql3306/bin/mysqlbinlog --start-position=3153 --stop-position=3880 /application/mysql3306/mysql_data/mysql-bin.000002 | /application/mysql3307/bin/mysql -uroot -S /application/mysql3307/logs/mysql.sock -p123456 -v
h、此時后面創建的表member恢復回來了但是庫ops1被刪除了,因為在這中間有刪除ops1庫的操作,若想繼續恢復后面表中插入的數據只需要以建表后的pos點為開始點即可恢復除刪庫之外的所有數據。
/application/mysql3306/bin/mysqlbinlog --start-position=3880 /application/mysql3306/mysql_data/mysql-bin.000002 | /application/mysql3307/bin/mysql -uroot -S /application/mysql3307/logs/mysql.sock -p123456 -v
10、另外:也可指定時間節點區間恢復(部分恢復):按時間恢復需要mysqlbinlog命令讀binlog日志內容,找時間節點。
a、/application/mysql3306/bin/mysqlbinlog /application/mysql3306/mysql_data/mysql-bin.000002 
可以看到圖中每個紅框下的時間兩個時間點都分別為事件的開始事件和結束時間
/application/mysql3306/bin/mysqlbinlog --stop-datetime="2018-09-12 10:37:58" /application/data/backup/mysql-bin.000002 | /application/mysql3307/bin/mysql -uroot -S /application/mysql3307/logs/mysql.sock -p123456 -v(此時stopdatetime不能寫到2018-09-12 10:38:01否則會更新到drop database ops這個操作,其它時間點同此步驟)
b、跳過刪庫環節恢復后面數據,可以從2018-09-12 10:38:45時間開始恢復,因為刪除ops1庫的時間不足一秒因此可以這樣干,這樣干的話庫ops1不會被刪,不過建議最好還是從下一個時間節點為開始進行恢復,即2018-09-12 11:11:22

/application/mysql3306/bin/mysqlbinlog --start-datetime="2018-09-12 10:38:45" /application/data/backup/mysql-bin.000002 | /application/mysql3307/bin/mysql -uroot -S /application/mysql3307/logs/mysql.sock -p123456 -v
c、基本原理和通過pos點恢復差不多。
六、總結:
所謂恢復,就是讓mysql將保存在binlog日志中指定段落區間的sql語句逐個重新執行一次而已。
