本篇内容介绍了“Mysql 5.7中Gtid相关内部数据结构有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

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、一个随机的内存地址有关

请看代码片段:

consttime_tsave_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);

调用栈帧:

#0init_server_auto_options()at/root/mysql5.7.14/percona-server-5.7.14-7/sql/mysqld.cc:3810#10x0000000000ec625einmysqld_main(argc=97,argv=0x2e9af08)at/root/mysql5.7.14/percona-server-5.7.14-7/sql/mysqld.cc:4962#20x0000000000ebd604inmain(argc=10,argv=0x7fffffffe458)at/root/mysql5.7.14/percona-server-5.7.14-7/sql/main.cc:253、server_uuid的内部表示binary_log::Uuid

binary_log::Uuid是server_uuid的内部表示实际上核心就是一个16字节的内存空间,如下:

/**ThenumberofbytesinthedataofaUuid.*/staticconstsize_tBYTE_LENGTH=16;/**ThedataforthisUuid.*/unsignedcharbytes[BYTE_LENGTH];

server_uuid和binary_log::Uuid之间可以互相转换,在Sid_map中binary_log::Uuid表示的server_uuid实际上就是其sid。

4、类结构Gtid

本结构是单个Gtid的内部表示其核心元素包括:

///SIDNOofthisGtid.rpl_sidnosidno;///GNOofthisGtid.rpl_gnogno;

其中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-writelockthatprotectsupdatestothenumberofSIDNOs.mutableCheckable_rwlock*sid_lock;/**ArraythatmapsSIDNOtoSID;theelementatindexNpointstoaNodewithSIDNON-1.*/Prealloced_array_sidno_to_sid;//因为sidno是一个连续的数值那么更具sidno找到sid只需要简单的做//数组查找即可这里将node指针存入/**HashthatmapsSIDtoSIDNO.Thekeysinthisarrayareoftyperpl_sid.*/HASH_sid_to_sidno;//因为sid是一个数据结构其核心为bytes关键值存储了16字节根据server_uuid//转换而来的无规律内存空间,需要使用hash查找表快速定位/**Arraythatmapsnumbersintheinterval[0,get_max_sidno()-1]toSIDNOs,inorderofincreasingSID.@seeSid_map::get_sorted_sidno.*/Prealloced_array_sorted;//额外的一个关于sidno的数组,具体作用未知

这里在看一下可变数组中的指针元素Node的类型:

structNode{rpl_sidnosidno;//sidhashnorpl_sidsid;//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内部正是这样实现的。我们来看核心元素:

/**ArraywheretheN'thelementcontainstheheadpointertotheintervalsofSIDNON+1.*/Prealloced_arraym_intervals;//每个sidno包含一个Interval单项链表,由next指针连接这就完成了比如分割GTID的问题///Linkedlistoffreeintervals.Interval*free_intervals;//空闲的interval连接在这个链表上///Linkedlistofchunks.Interval_chunk*chunks;//全局的一个Interval链表所有的Interval空间都连接在上面7、Gtid_set的关联类结构Interval

它正是前面说到的表示Gtid区间如下:

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

他的内部类就是Interval类结构我们看一下核心元素就懂了:

///ThefirstGNOofthisinterval.rpl_gnostart;///ThefirstGNOafterthisinterval.rpl_gnoend;///Pointertonextintervalinlist.Interval*next;

非常简单起始Gno加一个next指针,标示了一个区间。

8、类结构Gtid_state

本结构也是在数据库启动的时候和Sid_map一起进行初始化,也是一个全局的变量。
我们熟知的参数几个参数如下:

gtid_executed

gtid_owned

gtid_purged

都来自于次,当然除了以上的我们常见的还包含了其他一些核心元素我们来具体看看:

///TheSid_mapusedbythisGtid_state.mutableSid_map*sid_map;//使用sid_map/**ThesetofGTIDsthatexistedinsomepreviouslypurgedbinarylog.Thisisalwaysasubsetofexecuted_gtids.*/Gtid_setlost_gtids;//对应gtid_purged参数,这个参数一般由Mysql自动维护除非手动设置了gtid_purged参数/*ThesetofGTIDsthathasbeenexecutedandstoredintogtid_executedtable.*/Gtid_setexecuted_gtids;//对应gtid_executed参数,这个参数一般由Mysql主动维护/*ThesetofGTIDsthatexistsonlyingtid_executedtable,notinbinlogfiles.*/Gtid_setgtids_only_in_table;//正常来讲对于主库这个集合始终为空因为主库不可能存在只在mysql.gtid_executed表而不再binlog中的gtid,但是从库则必须开启log_slave_updates和binlog才会达到这个效果,//否则binlog不包含relay的Gtid的只能包含在mysql.gtid_executed表中,那么这种情况下Gtid_setgtids_only_in_table是始终存在的。具体后面还会解释。/*ThepreviousGTIDsinthelastbinlog.*/Gtid_setprevious_gtids_logged;//包含上一个binlog已经执行的所有的在binlog的Gtid///ThesetofGTIDsthatareownedbysomethread.Owned_gtidsowned_gtids;//当前所有线程拥有的全部Gtid集合///TheSIDNOforthisserver.rpl_sidnoserver_sidno;//就是服务器server_uuid对应sidhash出来的sidno9、类结构 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中。

这个过程会在后面进行描述。其核心元素包括:

///Growablearrayofhashes.Prealloced_arraysidno_to_hash;

这样一个每个sidno都会有hash结构其hash的内容则是:

structNode{///GNOofthegroup.rpl_gnogno;///Ownerofthegroup.my_thread_idowner;};

这样一个结构体,我们看到其中包含了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相关内部数据结构有哪些”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!