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

本文涉及的产品
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
简介:

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
相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
8月前
|
存储 关系型数据库 MySQL
MySQL数据库索引的数据结构?
MySQL中默认使用B+tree索引,它是一种多路平衡搜索树,具有树高较低、检索速度快的特点。所有数据存储在叶子节点,非叶子节点仅作索引,且叶子节点形成双向链表,便于区间查询。
224 4
|
5月前
|
NoSQL 算法 Redis
【Docker】(3)学习Docker中 镜像与容器数据卷、映射关系!手把手带你安装 MySql主从同步 和 Redis三主三从集群!并且进行主从切换与扩容操作,还有分析 哈希分区 等知识点!
Union文件系统(UnionFS)是一种**分层、轻量级并且高性能的文件系统**,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem) Union 文件系统是 Docker 镜像的基础。 镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
671 5
|
6月前
|
关系型数据库 MySQL 数据管理
Mysql基础学习day03-作业
本内容包含数据库建表语句及多表查询示例,涵盖内连接、外连接、子查询及聚合统计,适用于员工与部门数据管理场景。
108 1
|
6月前
|
SQL 关系型数据库 MySQL
Mysql基础学习day01
本课程为MySQL基础学习第一天内容,涵盖MySQL概述、安装、SQL简介及其分类(DDL、DML、DQL、DCL)、数据库操作(查询、创建、使用、删除)及表操作(创建、约束、数据类型)。适合初学者入门学习数据库基本概念和操作方法。
226 6
|
6月前
|
SQL 关系型数据库 MySQL
Mysql基础学习day02-作业
本教程介绍了数据库表的创建与管理操作,包括创建员工表、插入测试数据、删除记录、更新数据以及多种查询操作,涵盖了SQL语句的基本使用方法,适合初学者学习数据库操作基础。
135 0
|
6月前
|
SQL 关系型数据库 MySQL
Mysql基础学习day03
本课程为MySQL基础学习第三天内容,主要讲解多表关系与多表查询。内容涵盖物理外键与逻辑外键的区别、一对多、一对一及多对多关系的实现方式,以及内连接、外连接、子查询等多表查询方法,并通过具体案例演示SQL语句的编写与应用。
171 0
|
6月前
|
SQL 关系型数据库 MySQL
Mysql基础学习day01-作业
本教程包含三个数据库表的创建练习:学生表(student)要求具备主键、自增长、非空、默认值及唯一约束;课程表(course)定义主键、非空唯一字段及数值精度限制;员工表(employee)包含自增主键、非空字段、默认值、唯一电话号及日期时间类型字段。每个表的结构设计均附有详细SQL代码示例。
113 0
|
6月前
|
SQL 关系型数据库 MySQL
Mysql基础学习day02
本课程为MySQL基础学习第二天内容,涵盖数据定义语言(DDL)的表查询、修改与删除操作,以及数据操作语言(DML)的增删改查功能。通过具体SQL语句与实例演示,帮助学习者掌握MySQL表结构操作及数据管理技巧。
188 0
|
12月前
|
算法 数据可视化 开发者
为什么要学习数据结构与算法
今天,我向大家介绍一门非常重要的课程——《数据结构与算法》。这门课不仅是计算机学科的核心,更是每一位开发者从“小白”迈向“高手”的必经之路。
为什么要学习数据结构与算法

推荐镜像

更多