Mysql - Binlog
一、什么是Binlog日志
MySQL 的 Binlog 日志是一種二進制格式的日志,Binlog 記錄所有的 DDL 和 DML 語句(除了數據查詢語句SELECT、SHOW等),以 Event 的形式記錄,同時記錄語句執行時間。
Binlog 的主要作用有兩個:
因為 Binlog 詳細記錄了所有修改數據的 SQL,當某一時刻的數據誤操作而導致出問題,或者數據庫宕機數據丟失,那么可以根據 Binlog來回放歷史數據。
想要做多機備份的業務,可以去監聽當前寫庫的 Binlog 日志,同步寫庫的所有更改。
Binlog 包括兩類文件:
Binlog 日志功能默認是開啟的,線上情況下 Binlog 日志的增長速度是很快的,在 MySQL 的配置文件 my.cnf 中提供一些參數來對 Binlog 進行設置。
二、Binlog主要參數
log-bin=/home/mysql/binlog/
max_binlog_size=104857600
expire_logs_days = 7
expire_logs_days :Binlog 過期刪除不是服務定時執行,是需要借助事件觸發才執行,事件包括:
- 服務器重啟
- 服務器被更新
- 日志達到了最大日志長度 max_binlog_size
- 日志被刷新
binlog-do-db=db_name
binlog-ignore-db=db_name
sync_binlog=0
max_binlog_size :1G
- 值得注意的是:Binlog 最大和默認值是 1G,該設置并不能嚴格控制 Binlog 的大小,尤其是 Binlog比較靠近最大值而又遇到一個比較大事務時,為了保證事務的完整性不可能做切換日志的動作,只能將該事務的所有 SQL都記錄進當前日志直到事務結束。所以真實文件有時候會大于 max_binlog_size 設定值
二進制日志由配置文件的 log-bin選項負責啟用,MySQL 服務器將在數據根目錄創建兩個新文件mysql-bin.000001 和mysql-bin.index,若配置選項沒有給出文件名,MySQL 將使用主機名稱命名這兩個文件,其中 .index文件包含一份全體日志文件的清單。
- sync_binlog:這個參數決定了 Binlog 日志的更新頻率。默認 0 ,表示該操作由操作系統根據自身負載自行決定多久寫一次磁盤。
- sync_binlog = 1 表示每一條事務提交都會立刻寫盤。sync_binlog=n 表示 n 個事務提交才會寫盤。
- 根據 MySQL 文檔,寫 Binlog 的時機是:SQL transaction 執行完,但任何相關的 Locks 還未釋放或事務還未最終commit 前。這樣保證了 Binlog 記錄的操作時序與數據庫實際的數據變更順序一致。
三、Binlog實例
- 檢查 Binlog 文件是否已開啟:show variables like '%log_bin%';
MySQL 會把用戶對所有數據庫的內容和結構的修改情況記入 mysql-bin.n 文件,而不會記錄 SELECT 和沒有實際更新的 UPDATE 語句。 - 如果你不知道現在有哪些 Binlog 文件,可以使用如下命令:
show binary logs; #查看binlog列表
show master status; #查看最新的binlog
- Binlog 文件是二進制文件,強行打開看到的必然是亂碼,MySQL 提供了命令行的方式來展示 Binlog 日志:
如若該命令遇到問題,可參考該文章
看起來凌亂其實也有跡可循。Binlog 通過事件的方式來管理日志信息,可以通過 show binlog events in 的語法來查看當前 Binlog 文件對應的詳細事件信息。
Binlog 的版本是V4,可以看到日志的結束時間為 Stop。出現 Stop event 有兩種情況:
一般來說一份正常的 Binlog 日志文件會以 Rotate event 結束。當 Binlog 文件超過指定大小,Rotate event 會寫在文件最后,指向下一個 Binlog 文件。
- 對 event 查詢的數據行關鍵字段來解釋一下:
-
Pos:當前事件的開始位置,每個事件都占用固定的字節大小,結束位置(End_log_position)減去Pos,就是這個事件占用的字節數。上面的日志中我們能看到,第一個事件位置并不是從 0 開始,而是從 4。MySQL 通過文件中的前 4 個字節,來判斷這是不是一個 Binlog 文件。這種方式很常見,很多格式的文件,如 pdf、doc、jpg等,都會通常前幾個特定字符判斷是否是合法文件。
-
Event_type:表示事件的類型
-
Server_id:表示產生這個事件的 MySQL server_id,通過設置 my.cnf 中的 server-id 選項進行配置
-
End_log_position:下一個事件的開始位置
-
Info:包含事件的具體信息
四、Binlog 日志格式
針對不同的使用場景,Binlog 也提供了可定制化的服務,提供了三種模式來提供不同詳細程度的日志內容。
- Statement 模式:基于 SQL 語句的復制(statement-based replication-SBR)
- Row模式:基于行的復制(row-based replication-RBR)
- Mixed 模式:混合模式復制(mixed-basedreplication-MBR)
該模式只保存一條普通的SQL語句,不涉及到執行的上下文信息。因為每臺 MySQL 數據庫的本地環境可能不一樣,那么對于依賴到本地環境的函數或者上下文處理的邏輯 SQL 去處理的時候可能同樣的語句在不同的機器上執行出來的效果不一致。比如像 sleep()函數,last_insert_id()函數,等等,這些都跟特定時間的本地環境有關。
MySQL V5.1.5 版本開始支持Row模式的 Binlog,它與 Statement 模式的區別在于它不保存具體的 SQL 語句,而是記錄具體被修改的信息。比如一條 update 語句更新10條數據,如果是 Statement 模式那就保存一條 SQL 就夠,但是 Row 模式會保存每一行分別更新了什么,有10條數據。Row 模式的優缺點就很明顯了。保存每一個更改的詳細信息必然會帶來存儲空間的快速膨脹,換來的是事件操作的詳細記錄。所以要求越高代價越高。
Mixed 模式即以上兩種模式的綜合體。既然上面兩種模式分別走了極簡和一絲不茍的極端,那是否可以區分使用場景的情況下將這兩種模式綜合起來呢?在 Mixed 模式中,一般的更新語句使用 Statement 模式來保存 Binlog,但是遇到一些函數操作,可能會影響數據準確性的操作則使用 Row 模式來保存。這種方式需要根據每一條具體的 SQL 語句來區分選擇哪種模式。
- 注意:MySQL 從 V5.1.8 開始提供 Mixed 模式,V5.7.7 之前的版本默認是Statement 模式,之后默認使用Row模式, 但是在 8.0 以上版本已經默認使用 Mixed 模式了。
五、如何通過 mysqlbinlog 命令手動恢復數據
上面說過每一條 event 都有位點信息,如果我們當前的 MySQL 庫被無操作或者誤刪除了,那么該如何通過 Binlog 來恢復到刪除之前的數據狀態呢?
首先發現誤操作之后,先停止 MySQL 服務,防止繼續更新。
接著通過 mysqlbinlog命令對二進制文件進行分析,查看誤操作之前的位點信息在哪里。
接下來肯定就是恢復數據,當前數據庫的數據已經是錯的,那么就從開始位置到誤操作之前位點的數據肯定的都是正確的;如果誤操作之后也有正常的數據進來,這一段時間的位點數據也要備份。
比如說:誤操作的位點開始值為 501,誤操作結束的位置為705,之后到800的位點都是正確數據。那么從 0 - 500 ,706 - 800 都是有效數據,接著我們就可以進行數據恢復了。先將數據庫備份并清空。
0 - 500 的數據:
mysqlbinlog --start-position=0 --stop-position=500 bin-log.000003 > /root/back.sql;上面命令的作用就是將 0 -500 位點的數據恢復到自定義的 SQL 文件中。同理 706 - 800 的數據也是一樣操作。之后我們執行這兩個 SQL 文件就行了。
六、Binlog 事件類型
上面我們說到了 Binlog 日志中的事件,不同的操作會對應著不同的事件類型,且不同的 Binlog 日志模式同一個操作的事件類型也不同,下面我們一起看看常見的事件類型。
首先我們看看源碼中的事件類型定義:源碼位置:/libbinlogevents/include/binlog_event.h
enum Log_event_type {/**Every time you update this enum (when you add a type), you have tofix Format_description_event::Format_description_event().*/UNKNOWN_EVENT= 0,START_EVENT_V3= 1,QUERY_EVENT= 2,STOP_EVENT= 3,ROTATE_EVENT= 4,INTVAR_EVENT= 5,LOAD_EVENT= 6,SLAVE_EVENT= 7,CREATE_FILE_EVENT= 8,APPEND_BLOCK_EVENT= 9,EXEC_LOAD_EVENT= 10,DELETE_FILE_EVENT= 11,/**NEW_LOAD_EVENT is like LOAD_EVENT except that it has a longersql_ex, allowing multibyte TERMINATED BY etc; both types share thesame class (Load_event)*/NEW_LOAD_EVENT= 12,RAND_EVENT= 13,USER_VAR_EVENT= 14,FORMAT_DESCRIPTION_EVENT= 15,XID_EVENT= 16,BEGIN_LOAD_QUERY_EVENT= 17,EXECUTE_LOAD_QUERY_EVENT= 18,TABLE_MAP_EVENT = 19,/**The PRE_GA event numbers were used for 5.1.0 to 5.1.15 and aretherefore obsolete.*/PRE_GA_WRITE_ROWS_EVENT = 20,PRE_GA_UPDATE_ROWS_EVENT = 21,PRE_GA_DELETE_ROWS_EVENT = 22,/**The V1 event numbers are used from 5.1.16 until mysql-trunk-xx*/WRITE_ROWS_EVENT_V1 = 23,UPDATE_ROWS_EVENT_V1 = 24,DELETE_ROWS_EVENT_V1 = 25,/**Something out of the ordinary happened on the master*/INCIDENT_EVENT= 26,/**Heartbeat event to be send by master at its idle timeto ensure master's online status to slave*/HEARTBEAT_LOG_EVENT= 27,/**In some situations, it is necessary to send over ignorabledata to the slave: data that a slave can handle in case thereis code for handling it, but which can be ignored if it is notrecognized.*/IGNORABLE_LOG_EVENT= 28,ROWS_QUERY_LOG_EVENT= 29,/** Version 2 of the Row events */WRITE_ROWS_EVENT = 30,UPDATE_ROWS_EVENT = 31,DELETE_ROWS_EVENT = 32,GTID_LOG_EVENT= 33,ANONYMOUS_GTID_LOG_EVENT= 34,PREVIOUS_GTIDS_LOG_EVENT= 35,TRANSACTION_CONTEXT_EVENT= 36,VIEW_CHANGE_EVENT= 37,/* Prepared XA transaction terminal event similar to Xid */XA_PREPARE_LOG_EVENT= 38,/**Add new events here - right above this comment!Existing events (except ENUM_END_EVENT) should never change their numbers*/ENUM_END_EVENT /* end marker */ };這么多的事件類型我們就不一一介紹,挑出來一些常用的來看看。
FORMAT_DESCRIPTION_EVENT 是 Binlog V4 中為了取代之前版本中的 START_EVENT_V3 事件而引入的。它是 Binlog 文件中的第一個事件,而且,該事件只會在 Binlog 中出現一次。MySQL 根據FORMAT_DESCRIPTION_EVENT 的定義來解析其它事件。它通常指定了 MySQL 的版本,Binlog 的版本,該 Binlog 文件的創建時間。
QUERY_EVENT 類型的事件通常在以下幾種情況下使用:
在事務提交時,不管是 STATEMENT 還 是ROW 格式的 Binlog,都會在末尾添加一個 XID_EVENT 事件代表事務的結束。該事件記錄了該事務的 ID,在 MySQL 進行崩潰恢復時,根據事務在 Binlog 中的提交情況來決定是否提交存儲引擎中狀態為 prepared 的事務。
對于 ROW 格式的 Binlog,所有的 DML 語句都是記錄在 ROWS_EVENT 中。ROWS_EVENT分為三種:
- WRITE_ROWS_EVENT:對應insert
對于 insert 操作,WRITE_ROWS_EVENT 包含了要插入的數據。 - UPDATE_ROWS_EVENT:對應update
對于 update 操作,UPDATE_ROWS_EVENT 不僅包含了修改后的數據,還包含了修改前的值。 - DELETE_ROWS_EVENT:對應delete
對于 delete 操作,僅僅需要指定刪除的主鍵(在沒有主鍵的情況下,會給定所有列)。
對比 QUERY_EVENT 事件,是以文本形式記錄 DML 操作的。而對于 ROWS_EVENT 事件,并不是文本形式,所以在通過 mysqlbinlog 查看基于 ROW 格式的 Binlog 時,需要指定 -vv --base64-output=decode-rows。
mysql8.0版本更詳細的Event_type詳解
參考文章
總結
以上是生活随笔為你收集整理的Mysql - Binlog的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 互联网日报 | 小米跻身千亿美金俱乐部;
- 下一篇: 看看老司机是如何提升B端产品架构能力的