轉載:邏輯日誌與物理日誌
日誌主要分爲邏輯日誌和物理日誌:
- 邏輯日誌 Logic log[1];
- 物理日誌 Physical log[2];
1. 物理日誌與邏輯日誌的存儲內容
1.1 Physical Logging
物理日誌和邏輯日誌在存儲內容上有很大區別,存儲內容是區分它們的最重要手段。
物理日誌:
- 存儲內容:存儲數據庫中特定記錄的變更,通常是 page oriented,即描述具體某一個 page 的修改操作;
- 例子:一條更新請求對應的初始值(original value)以及更新值(after value);
邏輯日誌:
- 存儲內容:存儲事務中的一個操作;
- 例子:事務中的 UPDATE、DELETE 以及 INSERT 操作。
下圖是一種典型的物理日誌:
figure1.典型的物理日誌
我們可以看到,更新操作作用於 Page42,將字段 “Kemera” 修改爲 “camera”。更新操作對應的日誌爲:
"Page 42:image at 367,2; before:'ke';after:'ca'"
其中:
- Page 42 用於說明更新操作作用的 page;
- 367:用於說明更新操作相對於 page 的 offset;
- 2:用於說明更新操作的作用長度,即 length,2 代表僅僅修改了兩個字符;
- before:‘Ke’:這裏表示 undo information,也可以稱爲 undo log;
- after:‘ca’:這裏表示 redo log information,也可以稱爲 redo log;
當然,一條物理日誌可以有多個字段的修改,下面是一個抽象版本:
(Page ID,Record Offset,length,(Filed 1, Value 1) … (Filed i, Value i) … )
注意事項:
- 物理日誌實際上以字節編碼落盤,而不是字符編碼,因此通常肉眼不可見;
- image 的含義通常指代鏡像,但這裏不是說對 page 做整個鏡像,而是對更改或增量(change/delta)操作做鏡像,before image 代表寫操作作用之前的字段副本,after image 代表寫操作作用之後的字段副本;
可見,physical log 中的一條記錄對應於狀態機(state mechine)上某一個 page 上的某些字段做了什麼改動的落盤。
1.2 Logical Logging
邏輯日誌又被稱爲 high-level logging,這是相對於物理日誌而言的。
下圖是一個典型的邏輯日誌:
Figure2.典型的邏輯日誌
在上圖中,有一張 CameraLingo 表,我們試圖糾正 itermID 爲 0 的拼寫錯誤,即將 “Kemera” 修改爲 “Cemera”。邏輯日誌的格式如下:
CameraLingo:update(0,'Kermera'=>'camera')
邏輯日誌被稱爲 high level 的原因是其更抽象,其不需要指明更新操作具體作用於哪一塊 page,因此也對底層少了一些限制。如果利用物理日誌進行宕機後的數據恢復,那麼需要確保 page 不能夠改變,但利用邏輯日誌並不在乎底層 page 是否改變。
熟悉 MySQL 的同學可以發現邏輯日誌與一條 SQL 語句非常類似,事實上確實如此,邏輯日誌的本質就是對更新語句(update query)本身的落盤。在本節的例子中,只需要指明在哪一張表上的哪一行,對哪一些字段進行什麼修改即可。邏輯日誌不用物理上的 page,而用邏輯上的表。
另一方面,得益於 high level 的抽象,一個邏輯日誌可以對應多條物理日誌,下面舉一個例子:
CameraLingo:capitaLetter(term)
上述邏輯日誌的語義是對 CameraLingo 表中所有 term 字段對應的屬性首字母大寫,如果表足夠大,此次邏輯日誌涉及多個 page 上的修改,因而需要多個物理日誌。
需要指出的是 high level 的數據結構抽象不僅僅侷限於 table,例如 key-value 也是一個典型的 high level 數據結構。
1.3 Physiological Logging
除了物理日誌以及邏輯日誌,還有一種日誌被稱爲 Physiological Logging[3],其試圖同時獲得物理日誌與邏輯日誌的優勢。
下圖是一個典型的 Physiological Logging:
Figure3.典型的 Physiological Logging
Physiological Logging 的格式也可以如下表示:
(Page ID,Record Offset,(Filed 1, Value 1) … (Filed i, Value i) … )
其中,Page ID、Record Offset 的設計源於物理日誌。(Field1,Value1) 的設計來源於邏輯日誌。
Physiological Logging 的特點是:
- 與物理日誌相同,更新操作相對於 page 進行,每一條日誌僅僅涉及一個 page 的修改;
- 與邏輯日誌相同:日誌內容爲更新語句(update query)本身,而不是狀態機某些字段更新前後的狀態。
2. 物理日誌與邏輯日誌的比較
2.1 事務併發控制
什麼是事務併發控制,爲什麼需要事務併發控制?
我們需要使用事務併發控制的原因基於以下事實(以 MySQL 爲語境解釋):
- 事務由 SQL 語句構成,每一個 SQL 語句可分解爲多個不可分隔的讀/寫操作;
- 事務的執行實際上是一連串不可分割讀寫操作的執行;
- 事務調度器負責調度不可分割讀寫操作的執行順序,它們可能來自於不同事務;
- 事務併發控制的一個目標就是實現並行化事務;
邏輯日誌很難實現一致的事務併發控制。由於邏輯日誌難以攜帶併發執行順序的信息,當同時有多個事務產生更新操作時,數據庫內部會將這些操作調度爲串行化序列執行,需要機制來保障每次回放操作的執行順序與調度產生的順序一致。
另一方面,物理日誌本身就是存儲就是基於不可分隔的更新操作,因此其存儲先後順序就代表了執行器的調度順序。而且由於很容易判斷兩個 page 是否是同一個 page,如果不是,完全可以安全並行地並行執行。
爲了實現宕機前後事務併發控制的一致性,數據庫選擇使用 Physical Logging 作爲其 Redo Log。
2.2 冪等性
冪等性在日誌上的語義是:無論日誌回放多少次,最終得到的結果保持一致。
物理日誌能夠做的冪等性,因爲其本質是對狀態機某一個字段在更新前後狀態的記錄,無論執行多少次,最終得到的狀態總是相同的。下面是一個例子:
"Page 42:image at 367,2; before:'ke';after:'ca'"
邏輯日誌並不能夠提供冪等性的語義,因爲某一個更新操作本身不具備冪等性。例如:
CameraLingo:update(0,age=>age+1)
如果 age 的原值爲 0,如果執行一次,那麼 age 更新爲 1。如果執行兩次,那麼 age 更新爲 2。
當然,如果更新操作本身是冪等的,邏輯日誌也可以是冪等的,例如:
CameraLingo:capitaLetter(term)
上述邏輯日誌無論回放多少次(至少一次),最終得到的結果也就是將首字母大寫。
2.3 數據量大小
邏輯也不是一無是處,其在日誌數據量上佔優。
來自客戶端的一條更新語句可能會對應多個 page 上的更新,因此邏輯日誌與物理日誌在日誌數量上有巨大的區別。
#查詢語句
CameraLingo:capitaLetter(term)
#物理日誌
"Page 42:image at 3,1; before:'k';after:'K'"
"Page 42:image at 22,1; before:'a';after:'A'"
....
"Page 42:image at 442,1; before:'b';after:'B'"
#邏輯日誌
CameraLingo:capitaLetter(term)
別小看!日誌數據量大小是特別重要的特性,其對以下過程都有影響:
- 磁盤 I/O 吞吐量;
- 落盤文件大小;
- 網絡帶寬;
這裏的重點是網絡帶寬。分佈式系統會通過 primary 副本向 secondary 副本發送日誌的方式來進行分佈式事務的維護,因此使用物理日誌進行傳播就不合適。例如,MySQL 就選擇邏輯日誌進行維護分佈式事務。
2.4 日誌重放效率
邏輯日誌比物理日誌在重放時有着更低的效率,這主要有兩個方面的原因:
- 額外的解釋步驟:邏輯日誌需要額外地解釋更新語句、額外查找實際 page 位置;
- 物理日誌可以併發進行:當系統判斷兩個物理日誌作用於不同的 page 時,就可以進行完全的並行處理,而邏輯日誌通常只能串行執行。
2.5 磁盤/內存式日誌系統的 trade-off
其次,基於磁盤的存儲系統與基於內存的存儲系統對邏輯日誌以及物理日誌有着不同的 trade-off(權衡),如下面兩張圖所示。
3. 工業實踐與典型案例
3.1 MySQL
MysQL 對存儲引擎層以及 sever 提供了不同的日誌方案。以 InnoDB 存儲引擎爲例。
(1)server 層的 bin log
MySQL 的 server 層使用 binlog,其屬於邏輯日誌[4]。bin log 分爲兩種類型:
- 基於語句的日誌記錄(Statement-based logging):主要記錄了該 MySQL 執行語句(包括 inserts, updates, deletes);
- 基於行的日誌記錄(Row-based logging):主要記錄了對單個行的修改,其在 MySQL 5.1.5 後引入;
無論是 SQL 更新語句還是行上具體某個修改,都是邏輯日誌,因爲都沒有涉及在具體哪一個 page 上進行修改。
爲此,MySQL 提供了三個模式進行配置:
- Statement:基於語句的 binlog 日誌記錄(statement-based replication-SBR);
- Row:基於行的 binlog 日誌記錄(row-based replication-RBR);
- Mixed:混合模式,通常基於語句,有必要的情況下基於行實現(mixed-based replication-MBR);
MySQL 從 V5.1.8 開始提供 Mixed 模式,V5.7.7 之前的版本默認是Statement 模式,之後默認使用 Row 模式, 但是在 8.0 以上版本已經默認使用 Mixed 模式了。
MySQL 在 [5] 中指出了上述兩種邏輯日誌的優缺點。
基於語句的 binlog:
- 優點:
- 技術成熟;
- 數據更少,即使一個更新操作會影響非常多的行。
- 包括任何更新語句,因此可以用於數據庫維護人員維護數據庫;
- 缺點:並非所有 SQL 語句的執行效果都支持基於語句的複製。例如調動一個函數 now() 來獲取系統時間,在不同的機器、時間上重放日誌將得到不同的結果;
基於行的 binlog:
-
優點:
- 所有更改都可以複製,包括上面提到的 now() 函數;
- 以下類型的語句所需行鎖更少,可以實現更好的併發性:
INSERT ... SELECT
-
INSERT
statements withAUTO_INCREMENT
-
UPDATE
orDELETE
statements withWHERE
clauses that do not use keys or do not change most of the examined rows.
-
缺點:
- 數據量相比基於語句的 binlog 要大很多,在備份與恢復上性能較差;
- 丟失了原有 SQL 更新語句,不利於覆盤;
(2)數據引擎層的 redo log/undo log
MySQL 的 InnoDB 存儲引擎使用 redo log 以及 undo log,它們屬於 Physiological Logging,雖然很多人認爲其屬於物理日誌。
MySQL InnoDB 的 redo log 可以分爲三種類型:作用於Page,作用於 Space 以及提供額外信息的 Logic 類型。
這裏以作用於 Page 的 redo log 的格式爲例:
可見,MySQL 的 redo log 屬於 Physiological Logging:
- 物理上,其使用 Page Number + Record Offset 來指定具體作用於哪一個 page 上的一條記錄。
- 邏輯上,使用 Field Number(Field編號)來說明更新操作作用於哪些字段;
3.2 Redis
Redis 是一個內存型 key-value NoSQL 數據庫的典型代表。
Redis 基於 AOF(Append Only File)提供持久化機制[7]。
AOF 中的每一條日誌代表 Redis 節點接收到的一條寫操作,其格式爲 Redis 命令格式,因此 AOF 屬於邏輯日誌。
Redis 基於內存快照(RDB)+check point + AOF 的方式實現持久化。
3.3 Kafka
在關係型數據庫看來,日誌不是數據本身,例如對於 MySQL 的 InnoDB 存儲引擎來說,數據本身是存儲於磁盤上的 B+Tree 樹,日誌是用於確保單機事務以及分佈式事務的一種手段。換言之,關係型數據庫提供的讀/查詢 API 不是直接讀日誌,而是直接讀 B+Tree。
但是對於 Kakfa 而言,日誌本身就是數據本身。因此就沒有必要將這類數據庫分爲邏輯日誌與物理日誌。Kakfa 的日誌一方面服務於單機事務與分佈式事務,另一方面服務於消息的讀/寫 API。
根據[8],我們可知,Kakfa 消息包括如下重要字段:
可見,Kafka 在 message-logging 中的一條消息並不是用於描述更新,其就是數據本身。
總之,當日志本身就是數據而不是描述更新操作時,不需要將日誌區分爲邏輯日誌與物理日誌。
4. 總結
4.1 概述
(1)物理日誌
格式:記錄在某個頁面的某個偏移量初修改了幾個字節的值,具體修改的內容
優點:
- 日誌是冪等的,重複執行改日誌不會導致數據發生不一致的問題;
- 可用於實現宕機前後事務併發控制的一致性。物理日誌本身就是存儲就是基於不可分隔的更新操作,因此其存儲先後順序就代表了執行器的調度順序。而且由於很容易判斷兩個 page 是否是同一個 page,如果不是,完全可以安全並行地並行執行。
- 日誌重放效率高
缺點:日誌量大;
(2)邏輯日誌
格式:記錄對於表的操作,類似update語句
優點:日誌量小;
缺點:
- 恢復時無法保證冪等性;
- 難以保證宕機前後事務併發控制的一致性。,邏輯日誌很難實現一致的事務併發控制。由於邏輯日誌難以攜帶併發執行順序的信息,當同時有多個事務產生更新操作時,數據庫內部會將這些操作調度爲串行化序列執行,需要機制來保障每次回放操作的執行順序與調度產生的順序一致。
- 日誌重放效率低;
(3)物理邏輯日誌
格式:對應頁是物理的,頁內部操作是邏輯的。即根據物理頁進行日誌記錄,根據不同的邏輯操作進行日誌寫入。
需要注意的:日誌不是冪等性;
4.2 舉例
來源地址:頁斷裂(partial write)與doublewrite技術
比如:innodb表T(c1,c2, key key_c1(c1)),插入記錄row1(1,’abc’)
邏輯日誌:
<insert OP, T, 1,’abc’>
邏輯物理日誌:
因爲表T含有索引key_c1, 一次插入操作至少涉及兩次B樹操作(我感覺是二級索引也維護了一個B+),二次B樹必然涉及至少兩個物理頁面,因此至少有兩條日誌:
<insert OP, page_no_1, log_body>
<insert OP, page_no_2, log_body>
物理日誌:
由於一次INSERT操作,物理上來說要修改頁頭信息(如,頁內的記錄數要加1),要修改相鄰記錄裏的鏈表指針,要修改Slot屬性等,因此對應邏輯物理日誌的每一條日誌,都會有N條物理日誌產生。
< group_id,file_id,page_no,offset1, value1>
< group_id,file_id,page_no,offset2, value2>
……
< group_id,file_id,page_no,offsetN, valueN>
因此對於上述一個INSERT操作,會產生一條邏輯日誌,二條邏輯物理日誌,2*N條物理日誌。從上面簡單的分析可以看出,邏輯日誌的日誌量最小,而物理日誌的日誌量最大;物理日誌是純物理的;而邏輯物理日誌則頁間物理,頁內邏輯,所謂physical-to-a-page, logical-within-a-page。
REFERENCE
- [1] What is the logical log?
- [2] Physical logging, checkpoints, and fast recovery
- [3] What to log, Physical, Logical, and Physiological Logging, Trade-Offs…
- [4]MySQL :: MySQL Internals Manual :: 20.1 Binary Log Overview
- [5]MySQL :: MySQL 8.0 Reference Manual :: 17.2.1.1 Advantages and Disadvantages of Statement-Based and Row-Based Replication
- [6]庖丁解InnoDB之REDO LOG
- [7]Redis Persistence – Redis
- [8]A Guide To The Kafka Protocol