Mysql 5.7 Gtid内部学习(二) Gtid相关内部数据结构

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介:

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<Node*, 8, true>_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<rpl_sidno, 8, true> _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<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的关联类结构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<HASH*, 8, true> 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中,所以这个时候的图如下:

未命名文件.png

11、本节小结

学习完本节至少能够学习到:

  • 1、server_uuid是什么,如何生成,按照什么规则生成
  • 2、Gtid内部是如何表示
  • 3、 server_uuid和Gtid内部表示之间的联系
  • 4、 gtid_executed/gtid_owned/gtid_purged表示了什么具体对应哪个内存结构,当然这些将在后面的文章中多次提到,也会加深对它的了解。

如果有源码阅读能力的朋友可以按照这个框架继续深入学习。

作者微信:


微信.jpg
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
数据结构与算法系列学习之串的定义和基本操作、串的储存结构、基本操作的实现、朴素模式匹配算法、KMP算法等代码举例及图解说明;【含常见的报错问题及其对应的解决方法】你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
|
1月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习(8)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之顺序表【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找等具体详解步骤以及举例说明
|
1月前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
1月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
1月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之顺序表习题精讲【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找习题精讲等具体详解步骤以及举例说明
|
1月前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构的基本概念;算法的基本概念、特性以及时间复杂度、空间复杂度等举例说明;【含常见的报错问题及其对应的解决方法】
|
2月前
|
关系型数据库 MySQL Java
Django学习二:配置mysql,创建model实例,自动创建数据库表,对mysql数据库表已经创建好的进行直接操作和实验。
这篇文章是关于如何使用Django框架配置MySQL数据库,创建模型实例,并自动或手动创建数据库表,以及对这些表进行操作的详细教程。
84 0
Django学习二:配置mysql,创建model实例,自动创建数据库表,对mysql数据库表已经创建好的进行直接操作和实验。
|
2月前
|
Java 关系型数据库 MySQL
springboot学习五:springboot整合Mybatis 连接 mysql数据库
这篇文章是关于如何使用Spring Boot整合MyBatis来连接MySQL数据库,并进行基本的增删改查操作的教程。
177 0
springboot学习五:springboot整合Mybatis 连接 mysql数据库