MySQL 5.7 使用全局事务标识符(GTIDs)进行主从复制

使用全局事务标识符进行复制
下面解释使用全局事务标识符(GTIDs)进行基于事务的复制。当使用GTIDs时,每个事务都可以被识别和跟踪,因为它是在原始服务器上提交的, 并由任何从服务器应用;这意味着在启动一个新的slave或故障转移到一个新的master时,使用gtid时不需要引用日志文件或这些文件中的位置, 这大大简化了这些任务。因为基于gtid的复制完全是基于事务的,所以很容易确定主节点和从节点是否一致;只要在主节点上提交的所有事务也 在从节点上提交,就可以保证两者之间的一致性。你可以在GTIDs中使用基于语句或基于行的复制;不过,为了获得最佳效果,我们建议您使用基 于行的格式。

GTID概念
全局事务标识符(GTID)是与原始服务器(主服务器)上提交的每个事务相关联的唯一标识符。此标识符不仅对其发起的服务器是唯一的,而且在给 定复制设置中的所有服务器上也是唯一的。在所有事务和所有gtid之间存在1对1的映射。

以下段落提供了gtid的基本描述。更高级的概念将在后面的章节中介绍:
.GTID集

.mysql.gtid_executed表

.mysql.gtid_executed表压缩

GTID表示为一对坐标,由冒号(:)分隔,如下所示:
GTID = source_id:transaction_id

source_id标识原始服务器。通常,服务器的server_uuid用于此目的。transaction_id是一个序号,由事务在此服务器上提交的顺序决定;例如 ,要提交的第一个事务的transaction_id为1,在同一个原始服务器上要提交的第10个事务的transaction_id为10。在GTID中,事务不可能有0作 为序号。例如,在服务器上提交的第23个事务的UUID是3E11FA47-71CA-11E1-9E33-C80AA9429562,其GTID是这样的:
3E11FA47-71CA-11E1-9E33-C80AA9429562:23

这种格式用于在SHOW SLAVE STATUS等语句的输出以及二进制日志中表示gtid。当使用mysqlbinlog –base64-output=DECODE-ROWS查看日志文件 或在SHOW BINLOG EVENTS的输出中也可以看到它们。

正如在SHOW MASTER STATUS或SHOW SLAVE STATUS等语句的输出中所写的那样,来自同一服务器的gtid序列可以折叠成单个表达式,如下所示。

3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5

刚才显示的示例表示在server_uuid为3E11FA47-71CA-11E1-9E33-C80AA9429562的MySQL服务器上发起的第一个到第五个事务。

这种格式还用于提供START SLAVE选项所需的参数SQL_BEFORE_GTIDS和SQL_AFTER_GTIDS。

GTID集
GTID集合是全局事务标识符的集合,表示如下:

gtid_set:
uuid_set [, uuid_set] ...
| ''
uuid_set:
uuid:interval[:interval]...
uuid:
hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh
h:
[0-9|A-F]
interval:
n[-n]
(n >= 1)

GTID集在MySQL服务器中有多种使用方式。例如,gtid_executed和gtid_purged系统变量存储的值表示为GTID集合。此外,函数GTID_SUBSET()和 GTID_SUBTRACT()也要求输入GTID集合。当GTID集从服务器变量返回时,uuid按字母顺序排列,数值区间合并并升序排列。

gtid总是保存在master和slave之间。这意味着您总是可以通过检查任何从属服务器上应用的任何事务的二进制日志来确定其来源。此外,一旦 具有给定GTID的事务在给定服务器上提交,该服务器将忽略具有相同GTID的后续事务。因此,在主节点上提交的事务只能在从节点上应用一次, 这有助于保证一致性。

当使用gtid时,从节点不需要任何非本地的数据,例如主节点上一个文件的名称和在该文件中的位置。所有与主节点同步的必要信息都可以直接 从复制数据流中获得。GTIDs替换了之前用来确定在主从之间启动、停止或恢复数据流的点的文件偏移量对。因此,不要在CHANGE MASTER TO语 句中包含MASTER_LOG_FILE或MASTER_LOG_POS选项,这些选项用于指导从主机进行复制;相反,只需要启用MASTER_AUTO_POSITION选项。

GTID的生成和生命周期包括以下步骤:
1. 事务在主服务器上执行和提交
这个事务使用主服务器的UUID和最小的非零事务序列号分配一个GTID;GTID被写入到master的二进制日志中(紧跟在事务本身之前)。

2.在二进制日志数据传输到从服务器并存储到从服务器的中继日志之后(使用针对该进程建立的机制),从服务器读取GTID,并将其gtid_next系 统变量的值设置为该GTID。这告诉从服务器必须使用这个GTID记录下下一个事务。

注意slave在会话上下文中设置gtid_next是很重要的。

3.从服务器验证这个GTID是否已经被用来在它自己的二进制日志中记录事务。如果这个GTID没有被使用,从服务器就会写入GTID,应用事务,并 将事务写入它的二进制日志。在处理事务本身之前,通过首先读取和检查事务的GTID,从服务器不仅保证没有先前拥有该GTID的事务应用于从服 务器,而且还保证没有其他会话已经读取该GTID但尚未提交相关事务。换句话说,不允许多个客户端并发地应用相同的事务。

4.因为gtid_next不为空,所以从服务器并不试图为该事务生成一个GTID,而是将存储在该变量中的GTID,即从主服务器获得的GTID,在其二进 制日志中紧接在事务之前。

mysql.gtid_executed表
mysql.gtid_execute表是在MySQL服务器安装或升级时创建的(如果它不存在),使用如下所示的create table语句:
CREATE TABLE gtid_executed (
source_uuid CHAR(36) NOT NULL,
interval_start BIGINT(20) NOT NULL,
interval_end BIGINT(20) NOT NULL,
PRIMARY KEY (source_uuid, interval_start)
)

警告
与其他MySQL系统表一样,不要尝试自己创建或修改该表。

只有当gtid_mode为ON或ON_PERMISSIVE时,gtid才会存储在mysql.gtid_executed表中。表中存储的gtid与是否启用二进制日志记录无关。但它 们存储的方式取决于log_bin是开启还是关闭。
.如果二进制日志被禁用(log_bin为OFF),服务器将属于每个事务的GTID与表中的事务一起存储。

此外,当二进制日志被禁用时,该表会以用户可配置的速率定期压缩;看mysql.gtid_executed表压缩,以获取更多信息。

.如果启用了二进制日志记录(打开log_bin),那么除了将gtid存储在mysql.gtid_executed表之外,每当二进制日志被轮换或服务器关闭时,服 务器都会将所有写入前一个二进制日志的事务的GTIDs写入到新的二进制日志中。

在服务器意外停止的情况下,之前的二进制日志中的gtid集合不会保存到mysql中。gtid_executed表。在这种情况下,这些gtid会被添加到表中 ,并在恢复期间被添加到gtid_executed系统变量中的gtid集合中。

当启用二进制日志记录时,mysql。gtid_executed表没有提供所有已执行事务的GTIDs的完整记录。该信息由gtid_executed系统变量的全局值提供。

mysql.gtid_executed表被RESET MASTER重置。

mysql.gtid_executed表压缩
随着时间的推移,mysql.gtid_execute表可能会被许多行填充,这些行引用来自同一服务器上的单个gtid,并且其事务id组成一个序列,类似于 下面所示:

mysql> SELECT * FROM mysql.gtid_executed;
+--------------------------------------+----------------+--------------+
| source_uuid                          | interval_start | interval_end |
|--------------------------------------+----------------+--------------|
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37             | 37           |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 38             | 38           |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 39             | 39           |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40             | 40           |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 41             | 41           |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 42             | 42           |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 43             | 43           |
...

如果将该表定期压缩,可以将每组这样的行替换为跨越整个事务标识符间隔的单行,这样可以节省相当大的空间,如下所示:

+--------------------------------------+----------------+--------------+
| source_uuid                          | interval_start | interval_end |
|--------------------------------------+----------------+--------------|
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 |             37 | 43           |
...

当启用GTIDs时,服务器会对mysql.gtid_executed表周期性地执行这种类型的压缩。通过设置executed_gtids_compression_period系统变量, 可以控制压缩表之前允许发生的事务数,从而控制压缩率。这个变量的默认值是1000;这意味着,默认情况下,每1000个事务之后都会对表进行 压缩。将executed_gtid_compression_period设置为0将禁止执行压缩;但是,用户应该做好准备,如果这样做,gtid_executed表可能会需要大 量的磁盘空间

注意:
当启用二进制日志记录时,不使用executed_gtids_compression_period时。每次二进制日志轮换时,mysql.gtid_executed表都会被压缩。

压缩mysql.gtid_execute表是由一个名为thread/sql/compress_gtid_table的专用前台线程执行。这个线程没有在SHOW PROCESSLIST的输出中列 出,但是它可以被看作是线程表中的一行,如下所示:

mysql> SELECT * FROM performance_schema.threads WHERE NAME LIKE '%gtid%'\G
*************************** 1. row ***************************
          THREAD_ID: 26
               NAME: thread/sql/compress_gtid_table
               TYPE: FOREGROUND
     PROCESSLIST_ID: 1
   PROCESSLIST_USER: NULL
   PROCESSLIST_HOST: NULL
     PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Daemon
   PROCESSLIST_TIME: 631286
  PROCESSLIST_STATE: Suspending
   PROCESSLIST_INFO: NULL
   PARENT_THREAD_ID: 1
               ROLE: NULL
       INSTRUMENTED: YES
            HISTORY: YES
    CONNECTION_TYPE: NULL
       THREAD_OS_ID: 24594
1 row in set (0.00 sec)

thread/sql/compress_gtid_table线程通常休眠,直到executed_gtids_compression_period事务被执行,如前所述,然后唤醒执行 mysql.gtid_performed表压缩。然后它休眠,直到另一个executed_gtids_compression_period事务发生,然后唤醒再次执行压缩,无限地重复 这个循环。当禁用二进制日志记录时,将此值设置为0意味着线程始终处于睡眠状态,永远不会醒来。

使用gtid设置复制
对于最简单的GTID复制拓扑(由一个主节点和一个从节点组成),启动过程中的关键步骤如下:
1.如果复制已经在运行,则通过将两台服务器设置为只读来同步它们。

2.停止两个服务器。

3.启用gtid并配置正确的选项并重新启动两个服务器。
启动所描述的服务器所需的mysqld选项将在本节后面的示例中讨论。

注意:
server_uuid必须存在,gtid才能正常工作。

4.指示从服务器使用主服务器作为复制数据源并使用自动定位,然后启动从服务器。完成此步骤所需的SQL语句将在本节后面的示例中描述。

5.做一个新的备份。包含没有gtid的事务的二进制日志不能在启用gtid的服务器上使用,因此在此之前进行的备份不能用于您的新配置。

6.启动从属服务器,然后在两台服务器上再次启用读取模式,以便它们可以接受更新。

在下面的示例中,三台服务器已经分别作为主服务器和从服务器运行,使用MySQL的二进制日志基于位置的复制协议。下面的例子展示了如何在 服务器的选项文件中存储mysqld启动选项。

下面的大多数步骤都需要使用MySQL root帐户或其他具有SUPER权限的MySQL用户帐户。mysqladmin shutdown需要SUPER权限或SHUTDOWN特权。

步骤1:同步服务器。对于已经在进行复制而不是使用gtid方式的服务器才需要执行此步骤。对于新服务器,请继续执行步骤3。通过在每个服务 器上发出以下命令,设置read_only系统变量为ON使服务器只读:

mysql> show variables like '%server%id%';
+----------------+--------------------------------------+
| Variable_name  | Value                                |
+----------------+--------------------------------------+
| server_id      | 1                                    |
| server_id_bits | 32                                   |
| server_uuid    | 7877044c-a8f0-11ec-be08-005056a390e6 |
+----------------+--------------------------------------+
3 rows in set (0.01 sec)

mysql> SET @@global.read_only = ON;
Query OK, 0 rows affected (0.00 sec)


mysql> show variables like '%server%id%';
+----------------+--------------------------------------+
| Variable_name  | Value                                |
+----------------+--------------------------------------+
| server_id      | 2                                    |
| server_id_bits | 32                                   |
| server_uuid    | 1c5eb4fb-9479-11ec-8f21-005056a390e6 |
+----------------+--------------------------------------+
3 rows in set (0.06 sec)

mysql> SET @@global.read_only = ON;
Query OK, 0 rows affected (0.00 sec)


mysql> show variables like '%server%id%';
+----------------+--------------------------------------+
| Variable_name  | Value                                |
+----------------+--------------------------------------+
| server_id      | 3                                    |
| server_id_bits | 32                                   |
| server_uuid    | f08c0957-3d11-11ef-bea6-005056b9a980 |
+----------------+--------------------------------------+
3 rows in set (0.05 sec)

mysql> SET @@global.read_only = ON;
Query OK, 0 rows affected (0.00 sec)

等待所有正在进行的事务提交或回滚。然后,让从服务器追上主服务器。在继续工作之前,确保从服务器已经处理了所有的更新是极其重要的。

如果您将二进制日志用于除复制之外的任何事情,例如执行时间点备份和恢复,请等待,直到您不需要包含没有gtid的事务的旧二进制日志。理 想情况下,应该等待服务器清除所有二进制日志,并等待任何现有的备份过期。

重点
重要的是要理解,包含没有gtid的事务的日志不能在启用了gtid的服务器上使用。在继续之前,必须确保没有gtid的事务在拓扑中的任何位置都 不存在。

步骤2:
停止所有服务器。使用如下所示的mysqladmin停止每个服务器,其中username是MySQL用户的用户名,该用户有足够的权限关闭服务器:

shell> mysqladmin -uusername -p shutdown

然后在提示符下输入该用户的密码。

关闭主库

[root@localhost ~]# mysqladmin -uroot -pxxzx7817600 shutdown
mysqladmin: [Warning] Using a password on the command line interface can be insecure.

关闭两个从库

[root@localhost /]# mysqladmin -uroot -pxxzx7817600 shutdown
mysqladmin: [Warning] Using a password on the command line interface can be insecure.

[root@localhost mysql]# mysqladmin -uroot -pxxzx7817600 shutdown
mysqladmin: [Warning] Using a password on the command line interface can be insecure.

步骤3:启动所有启用gtid的服务器。要启用基于GTID的复制,每个服务器必须以启用GTID模式的方式启动,即通过将gtid_mode变量设置为ON, 并启用enforce_gtid_consistency变量以确保只记录适用于基于GTID复制的语句。此外,在配置从服务器设置之前,应以“–skip-slave-start ”选项启动从服务器。

为了使用gtid,并不是必须启用二进制日志记录,因为在MySQL 5.7.5添加了mysql.gtid_execute表,这意味着从服务器可以只使用gtid而不需要 二进制日志记录。主服务器必须始终启用二进制日志记录,以便能够进行复制。例如,要启动启用gtid但没有二进制日志记录的从服务器,请在 服务器的选项文件中配置这些变量:

gtid_mode=ON
enforce-gtid-consistency=true

修改主服务器的参数配置

[root@localhost ~]# vi /mysqlsoft/mysql/my.cnf
gtid_mode=ON
enforce-gtid-consistency=true

修改两个从服务器的参数配置

[root@localhost /]# vi /mysqlsoft/mysql/my.cnf
gtid_mode=ON
enforce-gtid-consistency=true
skip-slave-start=1

[root@localhost mysql]# vi /mysqlsoft/mysql/my.cnf
gtid_mode=ON
enforce-gtid-consistency=true
skip-slave-start=1

根据您的配置,为mysqld提供额外的选项。

启动主服务器

[root@localhost ~]# service mysqld start
Starting MySQL.... SUCCESS!

启动两个从服务器

[root@localhost /]# service mysqld start
Starting MySQL.. SUCCESS!

[root@localhost mysql]# service mysqld start
Starting MySQL.. SUCCESS!

步骤4:配置从服务器使用基于gtid的自动定位。告诉从服务器使用基于GTID事务的主服务器作为复制数据源,并使用基于GTID的自动定位,而 不是基于文件的定位。在从服务器上发出CHANGE MASTER TO语句,包括MASTER_AUTO_POSITION选项告诉从服务器,主服务器的事务是由gtid标识 的。

您可能还需要为主服务器的主机名和端口号提供适当的值,以及用于从服务器连接到主服务器的复制用户帐户的用户名和密码;如果在步骤1之前 已经设置了这些选项,并且不需要进行进一步的更改,则可以安全地从这里显示的语句中省略相应的选项。

对所有从服务器执行以下命令

mysql> show variables like '%server%id%';
+----------------+--------------------------------------+
| Variable_name  | Value                                |
+----------------+--------------------------------------+
| server_id      | 2                                    |
| server_id_bits | 32                                   |
| server_uuid    | 1c5eb4fb-9479-11ec-8f21-005056a390e6 |
+----------------+--------------------------------------+
3 rows in set (0.06 sec)

mysql> CHANGE MASTER TO  MASTER_HOST='10.138.130.250',MASTER_PORT=3306,MASTER_USER='repl',MASTER_PASSWORD='slavepass',MASTER_AUTO_POSITION=1;
Query OK, 0 rows affected, 2 warnings (0.03 sec)



mysql> show variables like '%server%id%';
+----------------+--------------------------------------+
| Variable_name  | Value                                |
+----------------+--------------------------------------+
| server_id      | 3                                    |
| server_id_bits | 32                                   |
| server_uuid    | f08c0957-3d11-11ef-bea6-005056b9a980 |
+----------------+--------------------------------------+
3 rows in set (0.00 sec)


mysql> CHANGE MASTER TO  MASTER_HOST='10.138.130.250',MASTER_PORT=3306,MASTER_USER='repl',MASTER_PASSWORD='slavepass',MASTER_AUTO_POSITION=1;
Query OK, 0 rows affected, 2 warnings (0.01 sec)

MASTER_LOG_FILE选项和MASTER_LOG_POS选项都不能在MASTER_AUTO_POSITION设置为1时使用。尝试这样做会导致CHANGE MASTER to语句失败并出现错误。

步骤5:做一个新的备份。在启用gtid之前所做的现有备份不能再在启用gtid后的这些服务器上使用。此时进行新的备份,这样就不会没有可用 的备份。

例如,您可以在进行备份的服务器上执行FLUSH LOGS。然后,要么显式地进行备份,要么等待您可能设置的任何定期备份例程的下一次迭代。
对主库执行备份

mysql> FLUSH LOGS;
Query OK, 0 rows affected (0.01 sec)

[root@localhost ~]# mysqldump -uroot -pxxzx7817600 --all-databases --master-data > dbdump20240711.db
mysqldump: [Warning] Using a password on the command line interface can be insecure.
Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions, even those that  changed suppressed parts of the database. If you don't want to restore GTIDs, pass --set-gtid-purged=OFF. To make a complete  dump, pass --all-databases --triggers --routines --events.

步骤6:启动从服务器并禁用只读模式。像这样启动slave:

mysql> start slave;
Query OK, 0 rows affected (0.01 sec)

mysql> start slave;
Query OK, 0 rows affected (0.01 sec)

只有在步骤1中将服务器配置为只读时,才需要执行以下步骤。要允许服务器再次开始接受更新,请发出以下语句:
从库

mysql> SET @@global.read_only = OFF;
Query OK, 0 rows affected (0.00 sec)

mysql> SET @@global.read_only = OFF;
Query OK, 0 rows affected (0.00 sec)

主库

mysql> SET @@global.read_only = OFF;
Query OK, 0 rows affected (0.00 sec)

基于gtid的复制现在应该正在运行,可以像以前一样在主服务器上开始(或恢复)进行操作。

下面来进行验证
主库

mysql> create table t5(id int primary key not null,name varchar(30),age int not null);
Query OK, 0 rows affected (0.07 sec)

mysql> desc t5;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   | PRI | NULL    |       |
| name  | varchar(30) | YES  |     | NULL    |       |
| age   | int(11)     | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

从库
1.

mysql> desc t5;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   | PRI | NULL    |       |
| name  | varchar(30) | YES  |     | NULL    |       |
| age   | int(11)     | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

2.

mysql> desc t5;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | NO   | PRI | NULL    |       |
| name  | varchar(30) | YES  |     | NULL    |       |
| age   | int(11)     | NO   |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

主库:

mysql> insert into t5 values(1,'jingyong',39);
Query OK, 1 row affected (0.00 sec)

mysql> select * from t5;
+----+----------+-----+
| id | name     | age |
+----+----------+-----+
|  1 | jingyong |  39 |
+----+----------+-----+
1 row in set (0.01 sec)

从库
1.

mysql> select * from t5;
+----+----------+-----+
| id | name     | age |
+----+----------+-----+
|  1 | jingyong |  39 |
+----+----------+-----+
1 row in set (0.00 sec)

2.

mysql> select * from t5;
+----+----------+-----+
| id | name     | age |
+----+----------+-----+
|  1 | jingyong |  39 |
+----+----------+-----+
1 row in set (0.00 sec)

可以看到同步正常。

发表评论

电子邮件地址不会被公开。