共計 7397 個字符,預計需要花費 19 分鐘才能閱讀完成。
本篇內容介紹了“Mysql 5.7 中 Gtid 相關內部數據結構有哪些”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓丸趣 TV 小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
1、Gtid 基本格式
單個 Gtid:
e859a28b-b66d-11e7-8371-000c291f347d:1
前一部分是 server_uuid,后面一部分是執行事物的唯一標志,通常是自增的。內部使用 Gtid 這種數據結構表示,后面會描述。
區間 Gtid:
e859a28b-b66d-11e7-8371-000c291f347d:1-5
前一部分是 server_uuid,后面一部分是執行事物的唯一標志集合,在內部使用 Gtid_set 中某個 Sidno 對應的 Interval 節點表示,后面會描述。
2、server_uuid 的生成
既然說到了 server_uuid 這里就開始討論 server_uuid 的生成。server_uuid 實際上是一個 32 字節 + 1 字節 (/0) 的字符串。Mysql 啟動的時候會調用 init_server_auto_options() 讀取 auto.cnf 文件。如果沒有讀取到則調用 generate_server_uuid()調用生成一個 server_id。
實際上在這個函數里面會看到 server_uuid 至少和下面部分有關:
1、mysql 啟動時間
2、線程 Lwp 有關
3、一個隨機的內存地址有關
請看代碼片段:
const time_t save_server_start_time= server_start_time; // 獲取 Mysql 啟動時間
server_start_time+= ((ulonglong)current_pid 48) + current_pid;// 加入 Lwp 號運算
thd- status_var.bytes_sent= (ulonglong)thd;// 這是一個內存指針
lex_start(thd);
func_uuid= new (thd- mem_root) Item_func_uuid();
func_uuid- fixed= 1;
func_uuid- val_str(uuid); // 這個函數里面有具體的運算過程
獲得這些信息后會進入 Item_func_uuid::val_str 做運算返回,有興趣的朋友可以深入看一下,最終會生成一個 server_uuid 并且拷貝到實際的 server_uuid 中如下:
strncpy(server_uuid, uuid.c_ptr(), UUID_LENGTH);
調用棧幀:
#0 init_server_auto_options () at /root/mysql5.7.14/percona-server-5.7.14-7/sql/mysqld.cc:3810
#1 0x0000000000ec625e in mysqld_main (argc=97, argv=0x2e9af08) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/mysqld.cc:4962
#2 0x0000000000ebd604 in main (argc=10, argv=0x7fffffffe458) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/main.cc:25
3、server_uuid 的內部表示 binary_log::Uuid
binary_log::Uuid 是 server_uuid 的內部表示實際上核心就是一個 16 字節的內存空間,如下:
/** The number of bytes in the data of a Uuid. */
static const size_t BYTE_LENGTH= 16;
/** The data for this Uuid. */
unsigned char bytes[BYTE_LENGTH];
server_uuid 和 binary_log::Uuid 之間可以互相轉換,在 Sid_map 中 binary_log::Uuid 表示的 server_uuid 實際上就是其 sid。
4、類結構 Gtid
本結構是單個 Gtid 的內部表示其核心元素包括:
/// SIDNO of this Gtid.
rpl_sidno sidno;
/// GNO of this Gtid.
rpl_gno gno;
其中 gno 就是我們說的事物唯一標志,而 sidno 其實是 server_uuid 的內部表示 binary_log::Uuid (sid)通過 hash 算法得出的一個查找表中的值。參考函數 Sid_map::add_sid 本函數則根據 binary_log::Uuid (sid)返回一個 sidno。
5、類結構 Sid_map
既然說到了 hash 算法那么需要一個內部結構來存儲這種整個 hash 查找表,在 Mysql 中使用 Sid_map 來作為這樣一個結構,其中包含一個可變數組和一個 hash 查找表其作用用來已經在注釋里面給出。全局只有一個 Sid_map。會在 Gtid 模塊初始化的時候分配內存。Sid_map 核心元素如下:
/// Read-write lock that protects updates to the number of SIDNOs.
mutable Checkable_rwlock *sid_lock;
/**
Array that maps SIDNO to SID; the element at index N points to a
Node with SIDNO N-1.
*/
Prealloced_array_sidno_to_sid; // 因為 sidno 是一個連續的數值那么更具 sidno 找到 sid 只需要簡單的做
// 數組查找即可這里將 node 指針存入
/**
Hash that maps SID to SIDNO. The keys in this array are of type
rpl_sid.
*/
HASH _sid_to_sidno; // 因為 sid 是一個數據結構其核心為 bytes 關鍵值存儲了 16 字節根據 server_uuid
// 轉換而來的無規律內存空間,需要使用 hash 查找表快速定位
/**
Array that maps numbers in the interval [0, get_max_sidno()-1] to
SIDNOs, in order of increasing SID.
@see Sid_map::get_sorted_sidno.
*/
Prealloced_array _sorted;// 額外的一個關于 sidno 的數組,具體作用未知
這里在看一下可變數組中的指針元素 Node 的類型:
struct Node
{
rpl_sidno sidno; //sid hash no
rpl_sid sid; //sid
};
其實他就是一個 sidno 和 sid 的對應。
6、類結構 Gtid_set
本結構是一個關于某種類型 Gtid 總的集合,比如我們熟知的有 execute_gtid 集合,purge_gtid 集合。我們知道在一個 execute_gtid 集合中可能包含多個數據庫的 Gtid 也就是多個 sidno 同時存在的情況,并且可能存在某個數據庫的 Gtid 出現區間的情況如下:
| gtid_executed |
3558703b-de63-11e7-91c3-5254008768e3:1-6:20-30,
da267088-9c22-11e7-ab56-5254008768e3:1-34 |
這里 3558703b-de63-11e7-91c3-5254008768e3 的 Gno 出現了多個區間:
1-6
20-30
那么這種情況下內部表示應該是數組加區間鏈表的方式,當然 Mysql 內部正是這樣實現的。我們來看核心元素:
/**
Array where the N th element contains the head pointer to the
intervals of SIDNO N+1.
*/
Prealloced_array m_intervals;// 每個 sidno 包含一個 Interval 單項鏈表,由 next 指針連接 這就完成了比如分割 GTID 的問題
/// Linked list of free intervals.
Interval *free_intervals; // 空閑的 interval 連接在這個鏈表上
/// Linked list of chunks.
Interval_chunk *chunks; // 全局的一個 Interval 鏈表 所有的 Interval 空間都連接在上面
7、Gtid_set 的關聯類結構 Interval
它正是前面說到的表示 Gtid 區間如下:
da267088-9c22-11e7-ab56-5254008768e3:1-34
他的內部類就是 Interval 類結構我們看一下核心元素就懂了:
/// The first GNO of this interval.
rpl_gno start;
/// The first GNO after this interval.
rpl_gno end;
/// Pointer to next interval in list.
Interval *next;
非常簡單起始 Gno 加一個 next 指針,標示了一個區間。
8、類結構 Gtid_state
本結構也是在數據庫啟動的時候和 Sid_map 一起進行初始化,也是一個全局的變量。
我們熟知的參數幾個參數如下:
gtid_executed
gtid_owned
gtid_purged
都來自于次,當然除了以上的我們常見的還包含了其他一些核心元素我們來具體看看:
/// The Sid_map used by this Gtid_state.
mutable Sid_map *sid_map; // 使用 sid_map
/**
The set of GTIDs that existed in some previously purged binary log.
This is always a subset of executed_gtids.
*/
Gtid_set lost_gtids; // 對應 gtid_purged 參數,這個參數一般由 Mysql 自動維護除非手動設置了 gtid_purged 參數
/*
The set of GTIDs that has been executed and
stored into gtid_executed table.
*/
Gtid_set executed_gtids; // 對應 gtid_executed 參數,這個參數一般由 Mysql 主動維護
/*
The set of GTIDs that exists only in gtid_executed table, not in
binlog files.
*/
Gtid_set gtids_only_in_table;
// 正常來講對于主庫這個集合始終為空因為主庫不可能存在只在 mysql.gtid_executed 表而不再 binlog 中的 gtid,但是從庫則必須開啟 log_slave_updates 和 binlog 才會達到這個效果,// 否則 binlog 不包含 relay 的 Gtid 的只能包含在 mysql.gtid_executed 表中,那么這種情況下 Gtid_set gtids_only_in_table 是始終存在的。具體后面還會解釋。 /* The previous GTIDs in the last binlog. */
Gtid_set previous_gtids_logged;// 包含上一個 binlog 已經執行的所有的在 binlog 的 Gtid
/// The set of GTIDs that are owned by some thread.
Owned_gtids owned_gtids;// 當前所有線程擁有的全部 Gtid 集合
/// The SIDNO for this server.
rpl_sidno server_sidno;// 就是服務器 server_uuid 對應 sid hash 出來的 sidno
9、類結構 Owned_gtids
這個結構包含當前線程所包含的所有正在持有的 Gtid 集合, 為事物分配 Gtid 會先將這個 Gtid 和線程號加入到給 Owned_gtids 然后將線程的 thd- owned_gtid 指向這個 Gtid。
參考函數 Gtid_state::acquire_ownership,在 commit 的最后階段會將這個 Gtid 從 Owned_gtids 中移除參考函數 Owned_gtids::remove_gtid 并且將他加入到 Gtid_state::executed_gtids 中。
這個過程會在后面進行描述。其核心元素包括:
/// Growable array of hashes.
Prealloced_array sidno_to_hash;
這樣一個每個 sidno 都會有 hash 結構其 hash 的內容則是:
struct Node
{
/// GNO of the group.
rpl_gno gno;
/// Owner of the group.
my_thread_id owner;
};
這樣一個結構體,我們看到其中包含了 gno 和線程 ID。
10、類結構 Gtid_table_persistor
本結構主要是包含一些操作 mysql.gtid_executed 表的函數接口
主要包含:
Insert the gtid into table.
int save(THD *thd, const Gtid *gtid);
Insert the gtid set into table.
int save(const Gtid_set *gtid_set);
Delete all rows from the table.
int reset(THD *thd);
Fetch gtids from gtid_executed table and store them into gtid_executed set.
int fetch_gtids(Gtid_set *gtid_set);
Compress the gtid_executed table completely by employing one or more transactions.
int compress(THD *thd);
Write a gtid interval into the gtid_executed table.
int write_row(TABLE *table, const char *sid,rpl_gno gno_start, rpl_gno gno_end);
Update a gtid interval in the gtid_executed table.
int update_row(TABLE *table, const char *sid,rpl_gno gno_start, rpl_gno new_gno_end);
Delete all rows in the gtid_executed table.
int delete_all(TABLE *table);
這些方法也是確定什么時候讀 / 寫 mysql.gtid_executed 的斷點。
10、內部結構圖示
為了能夠用圖的方式解釋這些類結構之間的關系,我修改 mysql.gtid_executed 表和 auto.cnf 構造出了這種有區間的 Gtid 案例,同時在源碼處增加打印代碼將啟動完成后的 get_executed_gtids/get_lost_gtids/get_gtids_only_in_table/get_previous_gtids_logged 輸出到了日志。但是在線上情況下很難見到這種有區間的 Gtid。
假設某一時刻我們數據庫啟動后各種 Gtid 如下():
2017-12-12T04:10:42.768153Z 0 [Note] gtid_state- get_executed_gtids:
Gtid have:3558703b-de63-11e7-91c3-5254008768e3:1-35,
da267088-9c22-11e7-ab56-5254008768e3:1-34
2017-12-12T04:10:42.768191Z 0 [Note] gtid_state- get_lost_gtids:
Gtid have:3558703b-de63-11e7-91c3-5254008768e3:1-6:20-30,
da267088-9c22-11e7-ab56-5254008768e3:1-34
2017-12-12T04:10:42.768226Z 0 [Note] gtid_state- get_gtids_only_in_table:
Gtid have:3558703b-de63-11e7-91c3-5254008768e3:1-6:20-30,
da267088-9c22-11e7-ab56-5254008768e3:1-34
2017-12-12T04:10:42.768260Z 0 [Note] gtid_state- get_previous_gtids_logged:
Gtid have:3558703b-de63-11e7-91c3-5254008768e3:7-19:31-35
啟動后我們馬上執行了一個事物,這個事物正處于 ordered_commit 的 flush 階段由 Gtid_state::acquire_ownership 獲得了一個 Gtid 那么它正在 Owned_gtids 中,所以這個時候的圖如下:
“Mysql 5.7 中 Gtid 相關內部數據結構有哪些”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注丸趣 TV 網站,丸趣 TV 小編將為大家輸出更多高質量的實用文章!