Mysql 5.7 Gtid內(nèi)部學(xué)習(xí)(二) Gtid相關(guān)內(nèi)部數(shù)據(jù)結(jié)構(gòu)


1拟杉、 Gtid基本格式

  • 單個Gtid:
 e859a28b-b66d-11e7-8371-000c291f347d:1

前一部分是server_uuid庄涡,后面一部分是執(zhí)行事務(wù)的唯一標(biāo)志,通常是自增的搬设。內(nèi)部使用Gtid這種數(shù)據(jù)結(jié)構(gòu)表示穴店,后面會描述。

  • 區(qū)間Gtid:
e859a28b-b66d-11e7-8371-000c291f347d:1-5

前一部分是server_uuid拿穴,后面一部分是執(zhí)行事務(wù)的唯一標(biāo)志集合泣洞,在內(nèi)部使用Gtid_set中某個Sidno對應(yīng)的Interval節(jié)點表示,后面會描述默色。

2球凰、server_uuid的生成

既然說到了server_uuid這里就開始討論server_uuid的生成。server_uuid實際上是一個32字節(jié)+1字節(jié)(/0)的字符串腿宰。Mysql啟動的時候會調(diào)用init_server_auto_options() 讀取auto.cnf文件呕诉。如果沒有讀取到則調(diào)用generate_server_uuid()調(diào)用生成一個server_id。
實際上在這個函數(shù)里面會看到server_uuid至少和下面部分有關(guān):

  • 1吃度、mysql啟動時間
  • 2义钉、線程Lwp有關(guān)
  • 3、一個隨機(jī)的內(nèi)存地址有關(guān)

請看代碼片段:

  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;//這是一個內(nèi)存指針

  lex_start(thd);
  func_uuid= new (thd->mem_root) Item_func_uuid();
  func_uuid->fixed= 1;
  func_uuid->val_str(&uuid);     //這個函數(shù)里面有具體的運算過程

獲得這些信息后會進(jìn)入Item_func_uuid::val_str做運算返回规肴,有興趣的朋友可以深入看一下,最終會生成一個server_uuid并且拷貝到實際的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

3夜畴、server_uuid的內(nèi)部表示binary_log::Uuid

binary_log::Uuid是server_uuid的內(nèi)部表示實際上核心就是一個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實際上就是其sid贪绘。

4兑牡、類結(jié)構(gòu)Gtid

本結(jié)構(gòu)是單個Gtid的內(nèi)部表示其核心元素包括:

  /// SIDNO of this Gtid.
  rpl_sidno sidno;
  /// GNO of this Gtid.
  rpl_gno gno;

其中g(shù)no就是我們說的事務(wù)唯一標(biāo)志,而sidno其實是server_uuid的內(nèi)部表示binary_log::Uuid (sid)通過hash算法得出的一個查找表中的值税灌。參考函數(shù)Sid_map::add_sid本函數(shù)則根據(jù)binary_log::Uuid (sid)返回一個sidno均函。

5、類結(jié)構(gòu)Sid_map

既然說到了hash算法那么需要一個內(nèi)部結(jié)構(gòu)來存儲這種整個hash查找表菱涤,在Mysql中使用Sid_map來作為這樣一個結(jié)構(gòu)苞也,其中包含一個可變數(shù)組和一個hash查找表其作用用來已經(jīng)在注釋里面給出。全局只有一個Sid_map粘秆。會在Gtid模塊初始化的時候分配內(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<Node*, 8, true>_sidno_to_sid; //因為sidno是一個連續(xù)的數(shù)值那么更具sidno找到sid只需要簡單的做
                                                 //數(shù)組查找即可這里將node指針存入
  /**
    Hash that maps SID to SIDNO.  The keys in this array are of type
    rpl_sid.
  */
  HASH _sid_to_sidno;                           //因為sid是一個數(shù)據(jù)結(jié)構(gòu)其核心為bytes關(guān)鍵值存儲了16字節(jié)根據(jù)server_uuid
                                                //轉(zhuǎn)換而來的無規(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<rpl_sidno, 8, true> _sorted;//額外的一個關(guān)于sidno的數(shù)組,具體作用未知

這里在看一下可變數(shù)組中的指針元素Node的類型:

  struct Node
  {
    rpl_sidno sidno; //sid hash no
    rpl_sid sid; //sid
  };

其實他就是一個sidno和sid的對應(yīng)殷勘。

6此再、類結(jié)構(gòu)Gtid_set

本結(jié)構(gòu)是一個關(guān)于某種類型Gtid總的集合,比如我們熟知的有execute_gtid集合玲销,purge_gtid集合输拇。我們知道在一個execute_gtid集合中可能包含多個數(shù)據(jù)庫的Gtid也就是多個sidno同時存在的情況,并且可能存在某個數(shù)據(jù)庫的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)了多個區(qū)間:

  • 1-6
  • 20-30

那么這種情況下內(nèi)部表示應(yīng)該是數(shù)組加區(qū)間鏈表的方式贤斜,當(dāng)然Mysql內(nèi)部正是這樣實現(xiàn)的策吠。我們來看核心元素:

/**
    Array where the N'th element contains the head pointer to the
    intervals of SIDNO N+1.
  */
  Prealloced_array<Interval*, 8, true> 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的關(guān)聯(lián)類結(jié)構(gòu)Interval

它正是前面說到的表示Gtid區(qū)間如下:

  • da267088-9c22-11e7-ab56-5254008768e3:1-34

他的內(nèi)部類就是Interval類結(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;

非常簡單起始Gno加一個next指針奴曙,標(biāo)示了一個區(qū)間。

8草讶、類結(jié)構(gòu)Gtid_state

本結(jié)構(gòu)也是在數(shù)據(jù)庫啟動的時候和Sid_map一起進(jìn)行初始化洽糟,也是一個全局的變量。
我們熟知的參數(shù)幾個參數(shù)如下:

  • gtid_executed
  • gtid_owned
  • gtid_purged

都來自于次堕战,當(dāng)然除了以上的我們常見的還包含了其他一些核心元素我們來具體看看:

/// 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; //對應(yīng)gtid_purged參數(shù)坤溃,這個參數(shù)一般由Mysql自動維護(hù)除非手動設(shè)置了gtid_purged參數(shù)
  /*
    The set of GTIDs that has been executed and
    stored into gtid_executed table.
  */
  Gtid_set executed_gtids; //對應(yīng)gtid_executed參數(shù),這個參數(shù)一般由Mysql主動維護(hù)
  /*
    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才會達(dá)到這個效果薪介,
//否則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已經(jīng)執(zhí)行的所有的在binlog的Gtid
  /// The set of GTIDs that are owned by some thread.
  Owned_gtids owned_gtids;//當(dāng)前所有線程擁有的全部Gtid集合
  /// The SIDNO for this server.
  rpl_sidno server_sidno;//就是服務(wù)器server_uuid對應(yīng)sid hash出來的sidno

9、類結(jié)構(gòu) Owned_gtids

這個結(jié)構(gòu)包含當(dāng)前線程所包含的所有正在持有的Gtid集合,為事務(wù)分配Gtid 會先將這個Gtid和線程號加入到給Owned_gtids然后將線程的thd->owned_gtid指向這個Gtid缀旁。
參考函數(shù)Gtid_state::acquire_ownership记劈,在commit的最后階段會將這個Gtid從Owned_gtids中移除參考函數(shù)Owned_gtids::remove_gtid 并且將他加入到Gtid_state::executed_gtids中。

這個過程會在后面進(jìn)行描述并巍。其核心元素包括:

 /// Growable array of hashes.
  Prealloced_array<HASH*, 8, true> sidno_to_hash;

這樣一個每個sidno都會有hash結(jié)構(gòu)其hash的內(nèi)容則是:

 struct Node
  {
    /// GNO of the group.
    rpl_gno gno;
    /// Owner of the group.
    my_thread_id owner;
  };

這樣一個結(jié)構(gòu)體目木,我們看到其中包含了gno和線程ID。

10懊渡、類結(jié)構(gòu)Gtid_table_persistor

本結(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);
    這些方法也是確定什么時候讀/寫mysql.gtid_executed的斷點刽射。

10、內(nèi)部結(jié)構(gòu)圖示

為了能夠用圖的方式解釋這些類結(jié)構(gòu)之間的關(guān)系剃执,我修改mysql.gtid_executed表和auto.cnf構(gòu)造出了這種有區(qū)間的Gtid案例誓禁,同時在源碼處增加打印代碼將啟動完成后的get_executed_gtids/get_lost_gtids/get_gtids_only_in_table/get_previous_gtids_logged輸出到了日志。但是在線上情況下很難見到這種有區(qū)間的Gtid忠蝗。
假設(shè)某一時刻我們數(shù)據(jù)庫啟動后各種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

啟動后我們馬上執(zhí)行了一個事務(wù)现横,這個事務(wù)正處于ordered_commit的flush階段由Gtid_state::acquire_ownership獲得了一個Gtid那么它正在Owned_gtids中,所以這個時候的圖如下:

未命名文件.png

11、本節(jié)小結(jié)

學(xué)習(xí)完本節(jié)至少能夠?qū)W習(xí)到:

  • 1戒祠、server_uuid是什么骇两,如何生成,按照什么規(guī)則生成
  • 2姜盈、Gtid內(nèi)部是如何表示
  • 3低千、 server_uuid和Gtid內(nèi)部表示之間的聯(lián)系
  • 4、 gtid_executed/gtid_owned/gtid_purged表示了什么具體對應(yīng)哪個內(nèi)存結(jié)構(gòu)馏颂,當(dāng)然這些將在后面的文章中多次提到示血,也會加深對它的了解。

如果有源碼閱讀能力的朋友可以按照這個框架繼續(xù)深入學(xué)習(xí)救拉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末难审,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子亿絮,更是在濱河造成了極大的恐慌告喊,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件派昧,死亡現(xiàn)場離奇詭異黔姜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蒂萎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門秆吵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人五慈,你說我怎么就攤上這事纳寂。” “怎么了泻拦?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵烈疚,是天一觀的道長。 經(jīng)常有香客問我聪轿,道長,這世上最難降的妖魔是什么猾浦? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任陆错,我火速辦了婚禮,結(jié)果婚禮上金赦,老公的妹妹穿的比我還像新娘音瓷。我一直安慰自己,他們只是感情好夹抗,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布绳慎。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪杏愤。 梳的紋絲不亂的頭發(fā)上靡砌,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機(jī)與錄音珊楼,去河邊找鬼通殃。 笑死,一個胖子當(dāng)著我的面吹牛厕宗,可吹牛的內(nèi)容都是我干的画舌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼已慢,長吁一口氣:“原來是場噩夢啊……” “哼曲聂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起佑惠,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤朋腋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后兢仰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乍丈,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年把将,在試婚紗的時候發(fā)現(xiàn)自己被綠了轻专。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡察蹲,死狀恐怖请垛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洽议,我是刑警寧澤宗收,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站亚兄,受9級特大地震影響混稽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜审胚,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一匈勋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧膳叨,春花似錦洽洁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汰翠。三九已至,卻和暖如春昭雌,著一層夾襖步出監(jiān)牢的瞬間复唤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工城豁, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留苟穆,地道東北人。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓唱星,卻偏偏與公主長得像雳旅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子间聊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容