複製 - 二進制日誌解析(Tableid 生成,ON DELETE CASCADE 處理,更新事件的更改列)
我正在使用帶有 InnoDB 引擎的 MySQL 5.5。我已將 binlog 格式設置為基於行的複制。
我對複制有一些疑問:
1)tableID是如何生成的?它儲存在某個地方嗎?
2)假設我有表 T1(父),T2(子),T3(子)。T1 與其子項具有 FK(ON DELETE CASCADE) 關係:T2 和 T3。如果從 T1 中刪除一行,是否會為所有 T1、T2 和 T3 生成 DELETE 事件?
- 有沒有辦法在 UPDATE 事件的情況下獲取有關更改列的資訊?
這些都是有趣的問題,但答案並不容易得到。內部結構有時沒有很好的文件記錄,或者文件很難找到,而且不僅僅是有點令人沮喪。
第 1 部分:table_id
基於行的複制中的 table_id
TABLE_MAP_EVENT
不是通常向使用者公開的值,儘管您會在mysqlbinlog
with--verbose
和的輸出中看到它--base64-output=decode-rows
。#120820 13:42:23 server id 10 end_log_pos 681420677 Table_map: `xifc`.`action_log` mapped to number 4025219
這個值來自一個遞增的全域計數器
sql/sql_base.cc
。每次將表添加到大小受全域變數限制的打開表的記憶體中時,它都會增加open_table_cache
…因此,如果該值小於定期訪問的表的數量,您應該會看到它更頻繁地增加。在FLUSH TABLES
.由於每次更改表時似乎都會關閉和打開表,因此 table_id 顯然別無選擇,只能在每次更改表時遞增,但這並不意味著給定表的 table_id 遞增必然意味著結構發生了變化。我的測試表明,即使重命名列,即使表的所有數據類型都相同,table_id 也會發生變化。
總而言之:表的table_id 總是在表被更改時更改,有時甚至在未更改時也會更改。如果伺服器重新啟動,計數器會重置。該
TABLE_MAP
事件包含 table_schema 和 table_name。第 2 部分:
ON DELETE CASCADE
…不在 binlog 中?我驚訝地發現,與觸發器引起的行更改不同,由外鍵約束引起的子表中的級聯刪除似乎沒有出現在二進制日誌中。然後,我找到了這個解釋,稍加思考就明白為什麼這可能是真的。
我給你一個提示:
MyISAM
不做外鍵約束的原因是一樣的……也就是說,那是因為 MySQL 本身不做外鍵約束…… InnoDB 做。它們在 InnoDB 內部處理,顯然在處理二進制日誌記錄的 MySQL 伺服器部分的可見性之外,並且依賴從屬來定義相同的約束。當從伺服器上刪除行時,從伺服器上的 InnoDB 會處理它,因此從複製的角度來看,它們在二進制日誌中不是“需要的”。第 3 部分:事件中的行更改
UPDATE
和標識列當更新一行時,將
UPDATE_ROWS_EVENT
寫入一個包含相關行的前後圖像的 an。(插入僅包含後圖像,刪除僅包含前圖像)。在 MySQL 5.6.2 之前,每個事件都包含完整的前後行圖像,這是我喜歡的。可以說,僅將複製所需的內容記錄到 binlog 中節省的空間並不能證明潛在恢復數據的失去是合理的,但在 5.6.2 中引入了一個新變數binlog_row_image以允許您更改多少列的資訊價值儲存在日誌中。假設這是 set
full
,這將與舊行為相同,您從行圖像中的每一列不為 null 的行事件中獲取列值(以及 null 標記為 null 的行列),然後您得到table_map
在每個事務開始時為將涉及的每個表發送的事件中的列類型定義。從中,您可以提取列值,按它們在表中的序號位置排序,但您不會獲得列名。它們不在二進制日誌中。我一直在研究一個項目,該項目通過模擬從伺服器、連接到主伺服器(或
log_slave_updates
啟用的下游從伺服器)、請求二進制日誌流、對其進行解碼並生成事件的 JSON 表示來為後台程序生成實時事件發送到消息隊列(SQS 或 Stomp)或主題(SNS 或 Stomp)。我必須做出一個架構決定:按表中的序數位置提供列值數組,並要求使用事件的程序將它們排序…或嘗試找出列名以包含在事件中作為關聯數組(雜湊)。我決定使用列名,並通過詢問伺服器來實現這一點 - 在我為此目的保持打開的第二個連接上 -
DESCRIBE table_schema.table_name
每次我看到我以前從未見過的 table_id 時,我都會記憶體它在記憶體中,直到我看到同一張表映射到不同的 id。如果我在 binlog 中看到 a,我也會清除我的記憶體ROTATE_EVENT
,因為如果伺服器崩潰或關閉,MySQL 會輪換 binlog,並且 table_id 值重新啟動。這裡的答案與第 1 項的答案相關。每次更改表後發生行事件時,表都會顯示一個新 ID,因此如果您每次看到新 table_id 時都獲取表定義,但將定義記憶體到舊 id 直到它更改,您相當安全,儘管不完全如此:如果一個表被快速連續更改多次並且行在更改事件之間發生更改,您可能會看到一個比 binlog 時活動的表定義更新的表定義事件產生。如果您的解析器無法跟上伺服器生成事件的速度,這將變得“更真實”。當然,在 binlog 事件中,您還會看到
ALTER TABLE
事件被複製為查詢事件,但它完全按照客戶端輸入的方式複制,因此您必須解析、標記和理解它,才能使用它……但這種情況非常難以實際生產,也不太可能成為主要問題。在我的實現中,我通過使用 binlog 中的列數或表定義中的列數中的較小者來處理它,並假設主鍵是最有趣的列,並且通常在左側,並且不太可能即使遇到這種情況也會被錯誤編碼。活動表上的單個更改可以完美執行,因為我已經在 table_id 下記憶體了定義,並且仍在使用它來解碼/編碼行事件,直到我看到新的 table_map 事件,這提示我獲取新定義。在處理我的項目時——我在生產的邊緣使用它,如果它真的“完成”了,我計劃在開源許可下發布——我發現這對弄清楚一些內部結構很有幫助(儘管我不知道 Ruby)是Jeremy Cole 的 mysql_binlog的原始碼。特別是壓縮小數的解碼。:(