本篇內(nèi)容介紹了“MySQL 5.7中Gtid相關(guān)內(nèi)部數(shù)據(jù)結(jié)構(gòu)有哪些”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
10年積累的做網(wǎng)站、網(wǎng)站制作經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶(hù)對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶(hù)得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先做網(wǎng)站設(shè)計(jì)后付款的網(wǎng)站建設(shè)流程,更有江達(dá)免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
單個(gè)Gtid:
e859a28b-b66d-11e7-8371-000c291f347d:1
前一部分是server_uuid,后面一部分是執(zhí)行事物的唯一標(biāo)志,通常是自增的。內(nèi)部使用Gtid這種數(shù)據(jù)結(jié)構(gòu)表示,后面會(huì)描述。
區(qū)間Gtid:
e859a28b-b66d-11e7-8371-000c291f347d:1-5
前一部分是server_uuid,后面一部分是執(zhí)行事物的唯一標(biāo)志集合,在內(nèi)部使用Gtid_set中某個(gè)Sidno對(duì)應(yīng)的Interval節(jié)點(diǎn)表示,后面會(huì)描述。
既然說(shuō)到了server_uuid這里就開(kāi)始討論server_uuid的生成。server_uuid實(shí)際上是一個(gè)32字節(jié)+1字節(jié)(/0)的字符串。Mysql啟動(dòng)的時(shí)候會(huì)調(diào)用init_server_auto_options() 讀取auto.cnf文件。如果沒(méi)有讀取到則調(diào)用generate_server_uuid()調(diào)用生成一個(gè)server_id。
實(shí)際上在這個(gè)函數(shù)里面會(huì)看到server_uuid至少和下面部分有關(guān):
1、mysql啟動(dòng)時(shí)間
2、線(xiàn)程Lwp有關(guān)
3、一個(gè)隨機(jī)的內(nèi)存地址有關(guān)
請(qǐng)看代碼片段:
const time_t save_server_start_time= server_start_time; //獲取Mysql 啟動(dòng)時(shí)間 server_start_time+= ((ulonglong)current_pid << 48) + current_pid;//加入Lwp號(hào)運(yùn)算 thd->status_var.bytes_sent= (ulonglong)thd;//這是一個(gè)內(nèi)存指針 lex_start(thd); func_uuid= new (thd->mem_root) Item_func_uuid(); func_uuid->fixed= 1; func_uuid->val_str(&uuid); //這個(gè)函數(shù)里面有具體的運(yùn)算過(guò)程
獲得這些信息后會(huì)進(jìn)入Item_func_uuid::val_str做運(yùn)算返回,有興趣的朋友可以深入看一下,最終會(huì)生成一個(gè)server_uuid并且拷貝到實(shí)際的server_uuid中如下:
strncpy(server_uuid, uuid.c_ptr(), UUID_LENGTH);
調(diào)用棧幀:
#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
binary_log::Uuid是server_uuid的內(nèi)部表示實(shí)際上核心就是一個(gè)16字節(jié)的內(nèi)存空間,如下:
/** 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之間可以互相轉(zhuǎn)換,在Sid_map中binary_log::Uuid表示的server_uuid實(shí)際上就是其sid。
本結(jié)構(gòu)是單個(gè)Gtid的內(nèi)部表示其核心元素包括:
/// SIDNO of this Gtid. rpl_sidno sidno; /// GNO of this Gtid. rpl_gno gno;
其中g(shù)no就是我們說(shuō)的事物唯一標(biāo)志,而sidno其實(shí)是server_uuid的內(nèi)部表示binary_log::Uuid (sid)通過(guò)hash算法得出的一個(gè)查找表中的值。參考函數(shù)Sid_map::add_sid本函數(shù)則根據(jù)binary_log::Uuid (sid)返回一個(gè)sidno。
既然說(shuō)到了hash算法那么需要一個(gè)內(nèi)部結(jié)構(gòu)來(lái)存儲(chǔ)這種整個(gè)hash查找表,在Mysql中使用Sid_map來(lái)作為這樣一個(gè)結(jié)構(gòu),其中包含一個(gè)可變數(shù)組和一個(gè)hash查找表其作用用來(lái)已經(jīng)在注釋里面給出。全局只有一個(gè)Sid_map。會(huì)在Gtid模塊初始化的時(shí)候分配內(nèi)存。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; //因?yàn)閟idno是一個(gè)連續(xù)的數(shù)值那么更具sidno找到sid只需要簡(jiǎn)單的做 //數(shù)組查找即可這里將node指針存入 /** Hash that maps SID to SIDNO. The keys in this array are of type rpl_sid. */ HASH _sid_to_sidno; //因?yàn)閟id是一個(gè)數(shù)據(jù)結(jié)構(gòu)其核心為bytes關(guān)鍵值存儲(chǔ)了16字節(jié)根據(jù)server_uuid //轉(zhuǎn)換而來(lái)的無(wú)規(guī)律內(nèi)存空間,需要使用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;//額外的一個(gè)關(guān)于sidno的數(shù)組,具體作用未知
這里在看一下可變數(shù)組中的指針元素Node的類(lèi)型:
struct Node { rpl_sidno sidno; //sid hash no rpl_sid sid; //sid };
其實(shí)他就是一個(gè)sidno和sid的對(duì)應(yīng)。
本結(jié)構(gòu)是一個(gè)關(guān)于某種類(lèi)型Gtid總的集合,比如我們熟知的有execute_gtid集合,purge_gtid集合。我們知道在一個(gè)execute_gtid集合中可能包含多個(gè)數(shù)據(jù)庫(kù)的Gtid也就是多個(gè)sidno同時(shí)存在的情況,并且可能存在某個(gè)數(shù)據(jù)庫(kù)的Gtid出現(xiàn)區(qū)間的情況如下:
| gtid_executed | 3558703b-de63-11e7-91c3-5254008768e3:1-6:20-30, da267088-9c22-11e7-ab56-5254008768e3:1-34 |
這里3558703b-de63-11e7-91c3-5254008768e3的Gno出現(xiàn)了多個(gè)區(qū)間:
1-6
20-30
那么這種情況下內(nèi)部表示應(yīng)該是數(shù)組加區(qū)間鏈表的方式,當(dāng)然Mysql內(nèi)部正是這樣實(shí)現(xiàn)的。我們來(lái)看核心元素:
/** Array where the N'th element contains the head pointer to the intervals of SIDNO N+1. */ Prealloced_array m_intervals;//每個(gè)sidno 包含一個(gè)Interval 單項(xiàng)鏈表,由next指針連接 這就完成了比如分割GTID的問(wèn)題 /// Linked list of free intervals. Interval *free_intervals; //空閑的interval連接在這個(gè)鏈表上 /// Linked list of chunks. Interval_chunk *chunks; //全局的一個(gè)Interval 鏈表 所有的Interval空間都連接在上面
它正是前面說(shuō)到的表示Gtid區(qū)間如下:
da267088-9c22-11e7-ab56-5254008768e3:1-34
他的內(nèi)部類(lèi)就是Interval類(lèi)結(jié)構(gòu)我們看一下核心元素就懂了:
/// 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;
非常簡(jiǎn)單起始Gno加一個(gè)next指針,標(biāo)示了一個(gè)區(qū)間。
本結(jié)構(gòu)也是在數(shù)據(jù)庫(kù)啟動(dòng)的時(shí)候和Sid_map一起進(jìn)行初始化,也是一個(gè)全局的變量。
我們熟知的參數(shù)幾個(gè)參數(shù)如下:
gtid_executed
gtid_owned
gtid_purged
都來(lái)自于次,當(dāng)然除了以上的我們常見(jiàn)的還包含了其他一些核心元素我們來(lái)具體看看:
/// 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; //對(duì)應(yīng)gtid_purged參數(shù),這個(gè)參數(shù)一般由Mysql自動(dòng)維護(hù)除非手動(dòng)設(shè)置了gtid_purged參數(shù) /* The set of GTIDs that has been executed and stored into gtid_executed table. */ Gtid_set executed_gtids; //對(duì)應(yīng)gtid_executed參數(shù),這個(gè)參數(shù)一般由Mysql主動(dòng)維護(hù) /* The set of GTIDs that exists only in gtid_executed table, not in binlog files. */ Gtid_set gtids_only_in_table; //正常來(lái)講對(duì)于主庫(kù)這個(gè)集合始終為空因?yàn)橹鲙?kù)不可能存在只在mysql.gtid_executed表而不再binlog中的gtid,但是從庫(kù)則必須開(kāi)啟log_slave_updates和binlog才會(huì)達(dá)到這個(gè)效果, //否則binlog不包含relay的Gtid的只能包含在mysql.gtid_executed表中,那么這種情況下Gtid_set gtids_only_in_table是始終存在的。具體后面還會(huì)解釋。 /* The previous GTIDs in the last binlog. */ Gtid_set previous_gtids_logged;//包含上一個(gè)binlog已經(jīng)執(zhí)行的所有的在binlog的Gtid /// The set of GTIDs that are owned by some thread. Owned_gtids owned_gtids;//當(dāng)前所有線(xiàn)程擁有的全部Gtid集合 /// The SIDNO for this server. rpl_sidno server_sidno;//就是服務(wù)器server_uuid對(duì)應(yīng)sid hash出來(lái)的sidno
這個(gè)結(jié)構(gòu)包含當(dāng)前線(xiàn)程所包含的所有正在持有的Gtid集合,為事物分配Gtid 會(huì)先將這個(gè)Gtid和線(xiàn)程號(hào)加入到給Owned_gtids然后將線(xiàn)程的thd->owned_gtid指向這個(gè)Gtid。
參考函數(shù)Gtid_state::acquire_ownership,在commit的最后階段會(huì)將這個(gè)Gtid從Owned_gtids中移除參考函數(shù)Owned_gtids::remove_gtid 并且將他加入到Gtid_state::executed_gtids中。
這個(gè)過(guò)程會(huì)在后面進(jìn)行描述。其核心元素包括:
/// Growable array of hashes. Prealloced_array sidno_to_hash;
這樣一個(gè)每個(gè)sidno都會(huì)有hash結(jié)構(gòu)其hash的內(nèi)容則是:
struct Node { /// GNO of the group. rpl_gno gno; /// Owner of the group. my_thread_id owner; };
這樣一個(gè)結(jié)構(gòu)體,我們看到其中包含了gno和線(xiàn)程ID。
本結(jié)構(gòu)主要是包含一些操作mysql.gtid_executed表的函數(shù)接口
主要包含:
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);
這些方法也是確定什么時(shí)候讀/寫(xiě)mysql.gtid_executed的斷點(diǎn)。
為了能夠用圖的方式解釋這些類(lèi)結(jié)構(gòu)之間的關(guān)系,我修改mysql.gtid_executed表和auto.cnf構(gòu)造出了這種有區(qū)間的Gtid案例,同時(shí)在源碼處增加打印代碼將啟動(dòng)完成后的get_executed_gtids/get_lost_gtids/get_gtids_only_in_table/get_previous_gtids_logged輸出到了日志。但是在線(xiàn)上情況下很難見(jiàn)到這種有區(qū)間的Gtid。
假設(shè)某一時(shí)刻我們數(shù)據(jù)庫(kù)啟動(dòng)后各種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
啟動(dòng)后我們馬上執(zhí)行了一個(gè)事物,這個(gè)事物正處于ordered_commit的flush階段由Gtid_state::acquire_ownership獲得了一個(gè)Gtid那么它正在Owned_gtids中,所以這個(gè)時(shí)候的圖如下:
“Mysql 5.7中Gtid相關(guān)內(nèi)部數(shù)據(jù)結(jié)構(gòu)有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!