使用 GTID 的基于事务的复制。当时使用 GTID 时,事务可以在提交到原始服务器及应用到任何从库时进行标识与追踪;就是说在使用 GTID 时,在启用新从库或故障转移到新主库时,没有必要制定日志文件或日志文件位置,极大简化这些任务。因为基于 GTID 复制完全基于事务,很容易确定主从之间是否一致;只要提交到主库的使用也提交到从库,就可以保证二者的一致性。可以使用基于语句或者基于行的复制,但是推荐使用基于行的 binlog 格式。
在主从间,总是保留 GTID。通过检查从库的 binlog 可以确定应用到从库的任何事务的来源。在给定 GTID 提交到给定服务器后,任何具有相同 GTID 的后续事务将被忽略。因此提交到主库的事务只能在从库中应用一次。
GTID Format and Storage
链接:MySQL 5.7 Reference Manual / GTID Format and Storage
内容:GTID 是如何定义与创建的,以及如何在 MySQL 中表现的。
GTID 是唯一标识符,创建并关联到在原库中提交的事务。这种唯一不但是在原库中,在整个复制集群中也是唯一的。
GTID 分配 区分 客户端事务(在主库上提交的)与复制的事务(在从库上重放的):
复制的事务没有写入从库 binlog 或者
被过滤 也会保留。MySQL 系统表 mysql.gtid_executed 用于保存
应用到 MySQL 服务的所有事务分配的 GTID 值,但是排除那些保存在当前活动 binlog 文件中的。
GTID 的 自动跳过功能:在主库上提交的事务不能在从库上应用多次,这保证数据一致性。当具有相同 GTID 的事务已经提交到服务,后续具有相同 GTID 的事务将被忽略。不会产生错误,也不会执行任何语句。
如果具有给定 GTID 的事务已经在执行,但是还没有提交或回滚,此时该服务器上任何尝试启动的具有相同 GTID 的并发事务将被阻塞。此时服务器既不会开始执行并发事务,也不会将控制权返回给客户端。当那个事务首次提交或者回滚后,具有相同 GTID 被阻塞的并行会话才会被处理。当首次尝试被回滚,某个并行会话接着尝试事务,其他所有并行会话(因 GTID 相同导致阻塞的会话)将继续阻塞。当首个尝试被提交,所有并行会话将停止被阻塞,并且自动跳过会话的所有语句。
GTID 格式如下:
GTID = source_id:transaction_id
source_id:原服务器,通常是主库的 server_uuid 参数;
transaction_id:顺序值,取决于事务在主库上提交的顺序。比如,首个提交的事务的 transaction_id 为 1;第十个提交的事务 transaction_id 为 10(transaction_id 不能为 0)。
在 mysqlbinlog 中,输出将显示事务的 GTID 值,用于标识在 Performance Schema 复制状态表中的单个事务,比如 replication_applier_status_by_worker 表。保存在 gtid_next 系统变量中的是单个 GTID 值。
GTID Sets
GTID Set 由单个或多个 单个GTID 或 GTID 范围 组成。在 MySQL 中,以多种方式使用 GTID Set:
语句 START SLAVE 的 UNTIL SQL_BEFORE_GTIDS 与 UNTIL SQL_AFTER_GTIDS 也使用 GTID Set 作为参数。
函数 GTID_SUBSET() 与 GTID_SUBTRACT() 也使用 GTID 作为参数
来自同个服务器的某个 GTID 范围可以表示但个表达式。如下代表 server_uuid 为 3E11FA47-71CA-11E1-9E33-C80AA9429562 的服务器上第一到五个事务:
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5
来自于相同服务器的多个 单个 GTID 或 GTID 范围 也可以使用单个表达式:
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-3:11:47-49
GTID Set 可以包含任意数量的 单个 GTID 或 GTID 范围,甚至可以包含来自不同服务器的 GTID 值。如下示例显示存储在已经应用多个主库事务的从库的 gtid_executed 系统变量中的 GTID Set 值:
2174B383-5441-11E8-B90A-C80AA9429562:1-3, 24DA167-0C0C-11E8-8442-00059A3C7B00:1-19
当从服务器变量返回 GTID Set 时,UUID 以字母顺序显示,数字则在合并后以升序显示。
GTID Set 语法如下:
gtid_set: uuid_set [, uuid_set] ... | '' uuid_set: uuid:interval[:interval]... uuid: hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh
mysql.gtid_executed Table
在 mysql 的 gtid_executed 中保存有 GTID 值,该表中的行(对于它所表示的每个 GTID 或 每组GTID)包含原服务器的 UUID 值、此 GUID Set 的开始事务与结束事务的 ID 值;如果只有但个 GTID 值,那开始事物与结束事务的 ID 值相同。
在 MySQL 中,该表由内部使用,在 MySQL 安装时创建,请勿修改该表。当从库禁用 binlog 时,它使从库启用 GTID 记录,并且当 binlog 丢失时,它可以保存 GTID 状态。注意事项,执行 RESET MASTER 语句会清空 mysql.gtid_executed 表。
当 gtid_mode 为 ON 或 ON_PERMISSIVE 时,才会启用 mysql.gtid_executed 表,GTID 保存的点取决与 binlog 是否开启:
log_bin = ON,不管 binlog 轮转还是 关闭服务器,服务器都会将之前写入 binlog 的全部事务的 GTID 写入 mysql.gtid_executed 表。这种情形可以应用于复制主库,或者启用 binlog 的复制从库。
如果服务器意外关机,部分在 binlog 中的 GTID 没有写入 mysql.gtid_executed 表。当恢复时,在 binglog 中的 GTID 将被写入表中。但是如果没有启用 binlog(从库),那么在重启时,由于无法访问 binlog 来恢复 GTID,因此复制无法开始。
当启用 binlog 时,mysql.gtid_executed 表并没有保存所有已执行事务的 GTID 值,应该使用 gtid_executed 系统变量,每次提交时该变量的值都会更新,用以反映数据库 GTID 状态,切勿查询 mysql.gtid_executed 表。
mysql.gtid_executed Table Compression
随着时间增长,mysql.gtid_executed 表会越来越大,MySQL 提供“压缩”功能(合并 ID 连续的事务)。
我们可以设置 gtid_executed_compression_period 系统变量,以在产生多少事务后触发压缩,默认 1000,即 1000 条事务后出发压缩。如果设置为 0 则将不会触发压缩,但是表将占用大量磁盘空间。
注意事项,如果启用 binlog,则不是使用 gtid_executed_compression_period 参数,将在 binlog 轮转时触发压缩。
表压缩由前台进程 thread/sql/compress_gtid_table 执行,多数时间在休眠。当达到 gtid_executed_compression_period 时开始执行,之后继续休眠,如果该参数为 0 则该线程持续休眠。该线程不会显示在 SHOW PROCESSLIST 中,要通过 threads 表查看:
SELECT * FROM performance_schema.threads WHERE NAME LIKE '%gtid%'\G
GTID Life Cycle
链接:MySQL 5.7 Reference Manual / GTID Life Cycle
内容:单个 GTID 的生命周期。
1)主库执行事务并提交。客户端事务被分配 GTID(主库的 UUID 与 未使用的序列号),然后 GTID 被写入 binlog 中(在事务之前写入)。如果事务被过滤,或者事务是只读的,导致无需写入 binlog,则不会分配 GTID。
2)如果为事务分配 GTID,在提交时将自动保存到 binlog 中(在事务的开始部分,作为 Gtid_log_event)。不管何时只要 binlog 论转或数据库关闭,数据库会将之前写入 binlog 的所有事务的 GTID 写入 mysql.gtid_executed 表中。
3)如果为事务分配 GTID,该 GTID 通过被添加到系统环境变量 gtid_executed 中的 GTID Set 被外部化(非原子,在事务提交后非常短暂)。该 GTID Set 代表所有已提交的 GTID 事务集合,并且被用做反映服务器状态的口令在复制中使用。当启用 binlog 时,在 gtid_executed 系统变量中的 GTID Set 是已应用事务的完整记录,但是 mysql.gtid_executed 表不是,因为最近的历史保存在 binlog 中。
4)当 binlog 传送到从库,并保存在从库的 relaylog 中时,从库读取 GTID 并设置系统变量 gtid_next 的值。这告诉从库下个事务必须使用该 GTID 进行记录。注意事项,从库设置 gtid_next 是在会话上下文中。
5)为了处理事务,从库检查目前还没有线程具有该 GTID 的所有权。在处理事务之前,通过首先读取与检查复制事务的 GTID,从库保证之前没有具有相同 GTID 事务在该从库上应用,并且没有其他会话已经读取该 GTID 但是还没有相关事物提交。因此如果多个客户端尝试同时应用相同事务,服务器只能让他们挨个执行。从库的 gtid_owned 系统变量显示当前正在使用的 GTID 值 已经拥有它的线程。如果该 GTID 已经使用,则会被忽略,并且不会产生错误。
6)如果 GTID 还没有被应用,从库将应用该复制的事务。由于 gtid_netxt 被设置为已经在主库上分配的 GTID 值,从库不会为该会话生成新的 GTID 值,而是使用 gtid_next 中的 GTID 值。
7)如果从库启用 binlog,在提交时 GTID 会自动保存到 binlog 中(在事务的最开始,作为 Gtid_log_event)。当 binlog 轮转或服务关闭,服务会将之前写入 binlog 的 GTID 写入到 mysql.gtid_executed 表。
8)如果从库禁用 binlog,则 GTID 将直接写入 mysql.gtid_executed 表。MySQL 追加语句到事物,为了插入 GTID 到表中。在这种情况下,mysql.gtid_executed 表中是应用到从库中全部事物记录。注意事项,在 MySQL 5.7 中,插入到 GTID 到表中的操作对于 DML 是原子的,但是对于 DDL 语句则不是,因此如果事物包含 DDL 语句执行后,此时服务器意外退出,可能会出现 GTID 状态不一致。对于 MySQL 8.0 两者都是原子的。
9)当复制事务在从库提交后,GTID 会被添加到从库的系统变量(gtid_executed)中的 GTID Set 中以外部化(非原子)。对于主库,该 GTID Set 表示所有已提交 GTID 事务的集合。如果从库的 binlog 被禁用,则 mysql.gtid_executed 保存的是在从库中应用的事务的完成记录。如果从库的 binlog 被启用,则某些 GTID 只被记录在 binlog 中,只有 gtid_executed 系统变量保存有完整记录。
What changes are assigned a GTID?
The gtid_next System Variable
The gtid_purged System Variable
Resetting the GTID Execution History
GTID Auto-Positioning
用于同步使用 GTID 的主库与从库的自动定位功能。
Setting Up Replication Using GTIDs
设置和启动基于 GTID 复制的通用过程。
Using GTIDs for Failover and Scaleout
当使用 GTID 复制时,配置新复制服务器的建议方法。
Restrictions on Replication with GTIDs
使用基于 GTID 复制的限制。
Stored Function Examples to Manipulate GTIDs
可用于 GTID 的存储函数
Global Transaction ID System Variables
与 GTID 有关的选项与变量。
Functions Used with Global Transaction Identifiers (GTIDs)
受 MySQL 5.7 支持的可与 GTID 共同使用的 SQL 函数。