介紹
undo log 和 redo log是由Inno DB存儲引擎生成的。
在MySQL服務器架構中,分為三層:連接層、服務層(server層)、執行層(存儲引擎層)
bin log
是 binary log
的縮寫,即二進制日志。
MySQL在完成一次DML操作后,Server層還會生成一條binlog,等事務提交之后, 還會將該事務執行過程中產生的所有binlog統一寫入到binlog日志文件中
binlog日志文件中保存了所有數據庫的所有表結構的變化和表數據的變化
bin log的作用
bin log主要用于下面兩個方面:
- 主從復制
對于”雙11“這種級別的千萬級高并發,單臺數據庫服務器是扛不住的!
為了提供并發處理的能力,一般會部署多態MySQL服務器,這些服務器中維護著相同的數據副本。這些MySQL服務器組成一個MySQL集群
一個非常典型的部署方案就是一主多從, 在這些MySQL服務器中,有一個主服務器Master和多臺從服務器Slave
主服務器只負責數據變更,而從服務器負責查詢,在大部分情況下,查詢是最耗時的,因為多臺從服務器負責查詢操作,分擔了并發壓力。
對于DDL、DML請求,將就給主服務器去執行
對于查詢Select請求,就交給從服務器,因為每個服務器中都維護著相同的數據副本,所以交給任意一個從服務器即可
為了讓各個從服務器中存儲的數據 和主服務器中的數據保持一致,每當我們改變了主服務器中的表結構或者表數據,就需要將這些變更信息同步給各個從服務器,此時bin log就發揮作用了
bin log中保存了所有數據庫的所有表的變化信息,從服務器只需要讀取主服務器的bin log,就能完成主從服務器之間的數據同步
- 系統備份恢復
如果不小心把整個數據庫的數據都刪除了,能使用redo log日志恢復數據嗎?
當然不能,因為redo log是Inno DB產生的日志文件,Inno DB是表級別的(在創建表時指定表的存儲引擎),當直接刪除了整個數據庫,那么這些redo log日志也不會存在了,即使這些redo log存在,也并不能將這個表的所有數據都恢復,因為redo log日志文件存在復用的情況,對于哪些已經刷新到磁盤的redo log,redo log已經沒有存在的必要性了所以會覆蓋掉,新寫的redo log會覆蓋掉之前的舊的redo log,因此redo log只能保證恢復在事務提交之前的部分數據
因為 redo log 文件是循環寫,是會邊寫邊擦除日志的,只記錄未被刷入磁盤的數據的物理日志,已經刷入磁盤的數據都會從 redo log 文件里擦除。
此時又輪到binlog發揮作用了
因為bin log記錄了所有庫的所有表的變更信息,所以可以借助binlog來完成數據恢復
配置使用binlog
查看MySQL服務器是否開啟了bin log
show variables like 'log_bin';
- 查詢到的值是
ON
,代表當前服務器已開啟binlog日志功能 - 如果為
OFF
,代表當前服務器沒有開啟bin log功能
如果當前服務器沒有開啟bin log,需要手動開啟。
關閉服務器,重新啟動服務器時添加此啟動配置項
--log-bin [=base_name]
base_name是用來記錄bin log日志文件的基名稱
bin log
日志文件會保存在MySQL的數據目錄下,bin log
日志文件不是一個文件,而是一組文件,這一組文件的命名規則是這樣的
basename.000001
basename.000002
basename.000003
basename.000004
....
以basename
作為基名稱,后面的序號遞增。
如果需要修改bin log文件的基名稱,可以利用啟動項參數修改
--log-bin [=basename]
如果沒有指定基名稱,那么MySQL服務器會默認以主機名-bin
作為binlog日志文件的基名稱。
MySQL 8.x 會默認以binlog
作為基名稱
binlog.000001
binlog.000002
....
查看binlog中的內容
binlog日志文件并不能直接查看,因為是二進制文件。
binlog中記錄數據庫發生變更的各種事件,事件類型非常多,其實并不需要刻意關注binlog中的內容。
如果真的想要查看binlog的內容,則可以借助MySQL提供的mysqlbinlog
工具
mysqlbinlog
工具作為可執行文件,存放在MySQL安裝目錄的bin目錄下
將需要查看的binlog日志文件作為mysqlbinlog
命令的參數
mysqlbinlog ./data/binlog.0000001
binlog日志文件格式
隨著MySQL版本的更新,binlog文件格式也有不同的版本,在此以v4最新版本的文件格式來講解。
一個binlog日志文件的基本格式主要由兩部分組成:
- 文件固定頭部
- 事件
- 每個binlog日志文件的前4個字節都是固定的,用來作為此文件的一個標識,不用關心。
- 每個binlog日志文件都有若干個事件組成,這些事件就是對數據庫表的變更事件。
每個事件又可以分為兩部分:
- 事件頭 event header,用來對這個事件進行描述的信息
- 真實數據 data
好了,這就是binlog日志文件的基本格式,知道這么多就行了。
兩種binlog
binlog日志文件中存儲的事件就是對數據庫變更的信息,那么如何描述這個事件又有兩種方式。
一條sql語句,如果binlog-format
不同,那么可能生成不同類型的binlog事件,大致就可以分為基于語句Statement和基于行row兩種類型的binlog
-
當以啟動選項
--binlog-format=STATEMENT
啟動MySQL服務器時,生成的binlog稱作基于語句的日志
。此時只會將一條SQL語句將會被完整的記錄到binlog中,而不管該語句影響了多少記錄。 -
當以啟動選項
--binlog-format=ROW
啟動MySQL服務器時,生成的binlog稱作基于行的日志
。此時會將該語句所改動的記錄的全部信息都記錄上。 -
當以啟動選項
--binlog-format=MIXED
啟動MySQL服務器時,生成的binlog稱作基于行的日志
。此時在通常情況下采用 基于語句的日志 ,在某些特殊情況下會自動轉為基于行的日志
簡單總結:
- 基于語句的binlog 只將更新語句是什么記錄下來了。
- 基于行的binlog 將更新語句執行過程中每一條記錄更新前后的值都記錄下來了
基于語句的binlog可能會導致主從數據不一致的情況,例如下面這條語句
INSERT INTO t(c) SELECT c FROM other_table;
這條語句是從其他表中查詢數據并插入到一張表中。
但是對于這條子查詢SELECT c FROM other_table;
,可能由于不同服務器版本、不同的系統變量導致同一條語句的結果順序不同,那么在插入時,自動生成主鍵,不同的順序最終生成的記錄的主鍵值不同。
原因是在基于語句的binlog中,只會記錄這條sql語句,從庫去執行這條sql語句,并不會關心更新前后的記錄值。
但是在基于行的binlog中,還保存了這條記錄更新前后的值,從庫在執行完sql語句后,還會將這條記錄調整到一致的狀態,從而保證主從數據一致性。
redo、undo、buffer pool、binlog的執行順序
當執行一條UPDATE語句時,redo、undo、binlog的寫入順序是怎樣的?
具體的一條記錄的更新流程如下:
-
現在B+樹中定位到該記錄(鎖定讀),如果該數據所在的頁面不再Buffer pool中,則先將這一頁數據從磁盤加載到buffer pool中
-
讀取到記錄后,判斷記錄更新前后是否一樣,一樣的話就跳過,不用更新了
-
首先更新聚簇索引記錄
更新聚簇索引記錄時:
- 首先向undo頁面寫入undo日志,因為這是在修改頁面,所以修改undo頁面前需要先記錄這次的redo日志
- 真正的更新記錄,在更新前記錄響應的redo日志
-
更新其他的二級索引
至此,一條記錄更新完成
redo 和 bin log的區別
- 適用對象不同
- binlog是MySQL的Server層實現的日志,是整個MySQL服務器級別的
- redo log是InnoDB存儲引擎實現的日志,是表級別的
-
文件格式不同
-
寫入方式不同
- binlog是追加寫,寫滿一個文件,就會新創建一個文件,不會覆蓋以前的文件
- redo log是循環寫,日志空間的大小是固定的,寫滿了就會從頭開始寫,覆蓋掉之前的redo Log內容
- 用途不同
- binlog 主要用于主從復制、備份恢復
- redo log主要用于發生故障時,表臟數據的恢復
主從復制
前面已經介紹過主從復制,MySQL的主從復制依賴于bin log
復制過程就是將binlog中的數據從主庫傳輸到從庫
這個過程是異步的,
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-rLPw0Yy5-1691478160467)(https://cdn.xiaolincoding.com/gh/xiaolincoder/mysql/how_update/%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6%E8%BF%87%E7%A8%8B.drawio.png?image_process=watermark,text_5YWs5LyX5Y-377ya5bCP5p6XY29kaW5n,type_ZnpsdHpoaw,x_10,y_10,g_se,size_20,color_0000CD,t_70,fill_0)]
MySQL主從復制的三個步驟:
- Master 寫入binlog:主服務器寫binlog日志,提交事務
- Slave 同步 binlog:把binlog復制到所有從庫上,每個從庫把binlog保存到暫存日志中
- 回訪binlog:從服務器執行完數據的更新后,記錄到自己的bin log日志文件中
具體的過程:
- MySQL 主庫在收到客戶端提交事務的請求之后,會先寫入 binlog,再提交事務,更新存儲引擎中的數據,事務提交完成后,返回給客戶端“操作成功”的響應。
- 從庫會創建一個專門的 I/O 線程,連接主庫的 log dump 線程,來接收主庫的 binlog 日志,再把 binlog 信息寫入 relay log 的中繼日志里,再返回給主庫“復制成功”的響應。
- 從庫會創建一個用于回放 binlog 的線程,去讀 relay log 中繼日志,然后回放 binlog 更新存儲引擎中的數據,最終實現主從的數據一致性。
從服務器的數量是不是越多越好?這樣分擔下來的流量不就更小,每個數據庫的壓力更小嗎?
不是的
從服務器數量增加,對于主服務器來說,IO連接的數量越多,對于每一個從服務器的連接,主服務器都需要創建一個log dump線程來專門負責與這個從服務器的請求。從服務器的數量越多,對主服務器的資源消耗越多,同時還要受限于主庫的網絡帶寬。
在實際使用中,一般遵循一主一副多從的設計,也就是一個主服務器,一個備用的主服務器(在主服務器崩潰后擔任主服務器),2-3個從服務器
巨人的肩膀
- 小林Coding——MySQL日志
- 《MySQL是怎么運行的》