- [Replication](#replication) - [Configuration Replication](#configuration-replication) - [binary log file position based replication configuration overview](#binary-log-file-position-based-replication-configuration-overview) - [Replication with Global Transaction Identifiers](#replication-with-global-transaction-identifiers) - [GTID Format and Storage](#gtid-format-and-storage) - [GTID Sets](#gtid-sets) - [msyql.gtid\_executed](#msyqlgtid_executed) - [mysql.gtid\_executed table compression](#mysqlgtid_executed-table-compression) - [GTID生命周期](#gtid生命周期) - [What changes are assign a GTID](#what-changes-are-assign-a-gtid) - [Replication Implementation](#replication-implementation) - [Replication Formats](#replication-formats) - [使用statement-based和row-based replication的优缺点对比](#使用statement-based和row-based-replication的优缺点对比) - [Advantages of statement-based replication](#advantages-of-statement-based-replication) - [Disadvantages of statement-based replication](#disadvantages-of-statement-based-replication) - [Advantages of row-based replication](#advantages-of-row-based-replication) - [disadvantages of row-based replication](#disadvantages-of-row-based-replication) # Replication Replication允许数据从一个database server被复制到一个或多个mysql database servers(replicas)。replication默认是异步的;replicas并无需永久连接即可接收来自source的更新。基于configuration,可以针对所有的databases、选定的databases、甚至database中指定的tables进行replicate。 mysql中replication包含如下优点: - 可拓展方案:可以将负载分散到多个replicas,从而提升性能。在该环境中,所有的writes和updates都发生在source server。但是,reads则是可以发生在一个或多个replicas上。通过该模型,能够提升writes的性能(因为source专门用于更新),并可以通过增加replicas的数量来提高read speed - data security: 因为replica可以暂停replication过程,故而可以在replica中运行备份服务,而不会破坏对应的source data - analytics:实时数据可以在source中生成,而对信息的分析则可以发生在replica,分析过程不会影响source的性能 - long-distance data distribution:可以replication来对remote site创建一个local copy,而无需对source的永久访问 在mysql 8.4中,支持不同的replication方式。 - 传统方式基于从source的binary log中复制事件,并且需要log files和`在source和replica之间进行同步的position`。 - 新的replication方式基于global transaction identifiers(GTIDs),是事务的,并且不需要和log files、position进行交互,其大大简化了许多通用的replication任务。使用GTIDs的replication保证了source和replica的一致性,所有在source提交的事务都会在replica被应用。 mysql中的replication支持不同类型的同步。 - 原始的同步是单向、异步的复制,一个server作为source、其他一个或多个servers作为replicas。 - NDB Cluster中,支持synchronous replication - 在mysql 8.4中,支持了半同步复制,通过半同步复制,在source中执行的提交将会被阻塞,直到至少一个replica收到transaction并对transaction进行log event,且向source发送ack - mysql 8.4同样支持delayed replication,使得replica故意落后于source至少指定的时间 ## Configuration Replication ### binary log file position based replication configuration overview 在该section中,会描述mysql servers间基于binary log file position的replication方案。(source会将database的writes和updates操作以events的形式写入到binary log中)。根据database记录的变更,binary log中的信息将会以不同的logging format存储。replicas可以被配置,从source的binary log读取events,并且在replica本地的binary log中执行时间。 每个replica都会接收binary log的完整副本内容,并由replica来决定binary log中哪些statements应当被执行。除非你显式指定,否则binary log中所有的events都会在replica上被执行。如果需要,你可以对replica进行配置,仅处理应用于特定databases、tables的events。 每个replica都会记录二进制日志的坐标:其已经从source读取并处理的file name和文件中的position。这代表多个replicas可以连接到source,并执行相同binary log的不同部分。由于该过程受replicas控制,故而独立的replicas可以和source建立连接、取消连接,并且不会影响到source的操作。并且,每个replica记录了binary log的当前position,replica可以断开连接、重新连接并重启之前的过程。 source和每个replica都配置了unique ID(使用`server_id` system variable),除此之外,每个replica必须配置如下信息: - source's host name - source's log file name - position of source's file 这些可以在replica的mysql session内通过`CHANGE REPLICATION SOURCE TO`语句来管理,并且这些信息存储在replica的connection metadata repository中。 ### Replication with Global Transaction Identifiers 在该章节中,描述了使用global transaction identifiers(GTIDs)的transaction-based replication。当使用GTIDs时,每个在source上提交的事务都可以被标识和追踪,并可以被任一replica应用。 在使用GTIDs时,启动新replica或failover to a new source时并不需要参照log files或file position。由于GTIDs时完全transaction-based的,其更容易判断是否source和replicas一致,只要在source中提交的事务全部也都在replica上提交,那么source和replica则是保证一致的。 在使用GTIDs时,可以选择是statement-based的还是row-based的,推荐使用row-based format。 GTIDs在source和replica都是持久化的。总是可以通过检测binary log来判断是否source中的任一事务是否在replica上被应用。此外,一旦给定GTID的事务在给定server上被提交,那么后续任一事务如果拥有相同的GTID,其都会被server忽略。因此,在source上被提交的事务可以在replica上应用多次,冗余的应用会被忽略,其可以保证数据的一致性。 #### GTID Format and Storage 一个global transation identifier(GTID)是一个唯一标识符,该标识符的创建并和`source上提交的每一个事务`进行关联。GTID不仅在创建其的server上是唯一的,并且在给定replication拓扑的所有servers间是唯一的。 GTID的分配区分了client trasactions(在source上提交的事务)以及replicated transactions(在replica上reproduced的事务)。当client transaction在source上提交时,如果transaction被写入到binary log中,其会被分配一个新的GTID。client transactions将会被保证单调递增,并且生成的编号之间不会存在间隙。`如果client transaction没有被写入到binary log,那么其在source中并不会被分配GTID`。 Replicated transaction将会使用`事务被source server分配的GTID`。在replicated transaction开始执行前,GTID就已经存在,并且,即使replicated transaction没有写入到replica的binary log中或是被replica过滤,该GTID也会被持久化。`mysql.gtid_executed` system table将会被用于保存`应用到给定server的所有事务的GTID,但是,已经存储到当前active binary log file的除外`。 > `active binary log file`中的gtid最终也会被写入到`gtid_executed`表中,但是,对于不同的存储引擎,写入时机可能不同。对于innodb而言,在事务提交时就会写入gtid_executed表,而对于其他存储引擎而言,则会在binary log发生rotation/server shutdown后才会将active binary log中尚未写入的GTIDs写入到`mysql.gtid_executed`表 GTIDs的auto-skip功能代表一个在source上提交的事务最多只能在replica上应用一次,这有助于保证一致性。一旦拥有给定GTID的事务在给定server上被提交,任何后续执行的事务如果拥有相同的GTID,都会被server忽略。并不会抛出异常,并且事务中的statements都不会被实际执行。 如果拥有给定GTID的事务在server上已经开始执行,但是尚未提交或回滚,那么任何尝试开启一个`拥有相同GTID事务的操作都会被阻塞`。该server并不会开始执行拥有相同GTID的事务,也不会将控制权交还给client。一旦第一个事务提交或回滚,那么被阻塞的事务就可以开始执行。如果第一次执行被回滚,那么被阻塞事务可以实际执行;如果第一个事务成功提交,那么被阻塞事务并不会被实际执行,而是会被自动跳过。 GTID的表示形式如下: ``` GTID = source_id:transaction_id ``` - `source_id`表示了originating server,通常为source的`server_uuid` - `transaction_id`则是一个由`transaction在source上提交顺序`决定的序列号。例如,第一个被提交的事务,其transaction_id为1;而第十个被提交的事务,其transaction_id则是为10. - 在GTID中,transaction_id部分的值不可能为0 例如,在UUID为`3E11FA47-71CA-11E1-9E33-C80AA9429562`上的server提交的第23个事务,其GTID为 ``` 3E11FA47-71CA-11E1-9E33-C80AA9429562:23 ``` 在GTID中,序列号的上限值为`2^63 - 1, or 9223372036854775807`(signed 64-bit integer)。如果server运行时超过GTIDs,其将执行`binlog_error_action`指定的行为。当server接近该限制时,会发出一个warning message。 在mysql 8.4中,也支持tagged GTIDs,一个tagged GTID由三部分组成,通过`:`进行分隔,示例如下所示: ``` GTID = source_id:tag:transaction_id ``` 在该示例中,`source_id`和`transaction_id`的含义和先前相同,`tag`则是一个用户自定义的字符串,用于表示`specific group of transactions`。 例如,在UUID为`ed102faf-eb00-11eb-8f20-0c5415bfaa1d`的server上第117个提交的tag为`Domain_1`的事务,其GTID为 ``` ed102faf-eb00-11eb-8f20-0c5415bfaa1d:Domain_1:117 ``` 事务的GTID会展示在`mysqlbinlog`的输出中,用于分辨`performance schema replication status tables`中独立的事务,例如`replication_applier_status_by_worker`表。 `gtid_next` system variable的值是单个GTID。 ##### GTID Sets 一个GTID set由`一个或多个GTIDs`或`GTIDs`范围构成。`gtid_executed`或`gtid_purged` system variables存储的就是GTID sets。`START REPLICA`选项`UNTIL SQL_BEFORE_GTIDS`和`UNTIL SQL_AFTER_GTIDS`可令replica在处理事务时,`最多只处理到GTID set中的第一个GTID`或`在处理完GTID set中的最后一个GTID后停止`。内置的`GTID_SUBSET()`和`GTID_SUBTRACT()`函数需要`GTID sets`作为输入。 在相同server上的GTIDs范围可以按照如下形式来表示: ``` 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5 ``` 上述示例表示在uuid为`3E11FA47-71CA-11E1-9E33-C80AA9429562`的server上提交的顺序为`1~5`之间的事务。 相同server上的多个single GTIDs或ranges of GTIDs可以通过如下形式来表示 ``` 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-3:11:47-49 ``` GTID set可以包含任意`single GTIDs`和`GTIDs range`的组合,也可以包含来自不同server的GTIDs。如下实例中展示了`gtid_executed` system variable中存储的GTID set,代表replica中应用了来自超过一个source的事务: ``` 2174B383-5441-11E8-B90A-C80AA9429562:1-3, 24DA167-0C0C-11E8-8442-00059A3C7B00:1-19 ``` 当GTID set从server variables中返回时,UUID按照字母排序返回,而序列号间区间则是合并后按照升序排列。 当构建GTID set时,用户自定义tag也会被作为UUID的一部分,这代表来自相同server且tag相同的多个GTIDs可以被包含在一个表达式中,如下所示: ``` 3E11FA47-71CA-11E1-9E33-C80AA9429562:Domain_1:1-3:11:47-49 ``` 来源相同server,但是tags不同的GTIDs表示方式如下: ``` 3E11FA47-71CA-11E1-9E33-C80AA9429562:Domain_1:1-3:15-21, 3E11FA47-71CA-11E1-9E33-C80AA9429562:Domain_2:8-52 ``` GTID set的完整语法如下所示: ``` gtid_set: uuid_set [, uuid_set] ... | '' uuid_set: uuid:[tag:]interval[:interval]... uuid: hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh h: [0-9|A-F] tag: [a-z_][a-z0-9_]{0,31} interval: m[-n] (m >= 1; n > m) ``` ##### msyql.gtid_executed GTIDs被存储在`mysql.gtid_executed`表中,该表中的行用于表示GTID或GTID set,行包含信息如下 - source server的uuid - 用户自定义的tag - starting transaction IDs of the set - ending transaction IDs of the set 如果行代表single GTID,那么最后两个字段的值相同。 `mysql.gtid_executed`表在mysql server安装或升级时会被自动创建,其表结构如下: ```sql CREATE TABLE gtid_executed ( source_uuid CHAR(36) NOT NULL, interval_start BIGINT NOT NULL, interval_end BIGINT NOT NULL, gtid_tag CHAR(32) NOT NULL, PRIMARY KEY (source_uuid, gtid_tag, interval_start) ); ``` `mysql.gtid_executed`用于mysql server内部使用,其允许replica在binary logging被禁用的情况下使用GTIDs,其也可以在binary logs丢失时保留GTID的状态。如果执行`RESET BINARY LOGS AND GTIDS`,那么`mysql.gtid_executed`表将会被清空。 只有当`gtid_mode`被设置为`ON`或`ON_PERMISSIVE`时,GTIDs才会被存储到mysql.gtid_executed中。如果binary logging被禁用,或者`log_replica_updates`被禁用,server在事务提交时将会把属于个各事务的GTID和事务一起存储到buffer中,并且background threads将会周期性将buffer中的内容以entries的形式添加到`mysql.gtid_executed`表中。 对于innodb存储引擎而言,如果binary logging被启用,server更新`mysql.gtid_executed`表的方式将会和`binary logging或replica update logging被启用`时一样,都会在每个事务提交时存储GTID。对于其他存储引擎,则是在binary log rotation发生时或server shut down时更新`mysql.gtid_executed`表的内容。 如果mysql.gtid_executed表无法被写访问,并且binary log file因`reaching the maximum file size`之外的任何理由被rotated,那么current binary log file将仍被使用。并且,当client发起rotation时将会返回错误信息,并且server将会输出warning日志。如果`mysql.gtid_executed`无法被写访问,并且binary log单个文件大小达到`max_binlog_size`,那么server将会根据`binlog_error_action`设置来执行操作。如果`IGNORE_ERROR`被设置,那么server将会输出error到日志,并且binary logging将会被停止;如果`ABORT_SERVER`被设置,那么server将会shutdown。 > 因为写入到active binary log的GTIDs最终也要被写入`mysql.gtid_executed`表,但是该表若当前不可写访问,那么此时将无法触发`binary log rotation`。 > > MySQL.gtid_executed中缺失的GTIDs必须包含在active binary log file中,如果log file触发rotation,但是无法向mysql.gtid_executed中写入数据,那么rotation是不被允许的。 ##### mysql.gtid_executed table compression 随着时间的推移,mysql.gtid_executed表会填满大量行,行数据代表独立的GTID,示例如下: ``` +--------------------------------------+----------------+--------------+----------+ | source_uuid | interval_start | interval_end | gtid_tag | |--------------------------------------+----------------+--------------|----------+ | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 31 | 31 | Domain_1 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 32 | 32 | Domain_1 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 33 | 33 | Domain_1 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 34 | 34 | Domain_1 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 35 | 35 | Domain_1 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 36 | 36 | Domain_2 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37 | 37 | Domain_2 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 38 | 38 | Domain_2 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 39 | 39 | Domain_2 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40 | 40 | Domain_1 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 41 | 41 | Domain_1 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 42 | 42 | Domain_1 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 43 | 43 | Domain_1 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 44 | 44 | Domain_2 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 45 | 45 | Domain_2 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 46 | 46 | Domain_2 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 47 | 47 | Domain_1 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 48 | 48 | Domain_1 | ``` 为了节省空间,可以对gtid_executed表周期性的进行压缩,将多个`single GTID`替换为单个`GTID set`,压缩后的数据如下; ``` +--------------------------------------+----------------+--------------+----------+ | source_uuid | interval_start | interval_end | gtid_tag | |--------------------------------------+----------------+--------------|----------+ | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 31 | 35 | Domain_1 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 36 | 39 | Domain_2 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40 | 43 | Domain_1 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 44 | 46 | Domain_2 | | 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 47 | 48 | Domain_1 | ... ``` server可通过名为`thread/sql/compress_gtid_table`的前台线程来执行gtid_executed表的压缩操作,该线程并不会在`show processlist`的输出中被列出,但是可以通过查询`threads`表来查看,示例如下: ```sql 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: 1509 PROCESSLIST_STATE: Suspending PROCESSLIST_INFO: NULL PARENT_THREAD_ID: 1 ROLE: NULL INSTRUMENTED: YES HISTORY: YES CONNECTION_TYPE: NULL THREAD_OS_ID: 18677 ``` 当server启用了binary log时,上述压缩方式并不会被使用,`mysql.gtid_executed`在每次binary log rotation时会被压缩。但是,当binary logging被禁用时,`thread/sql/compress_gtid`线程处于sleep状态,每有一定数量的事务被执行时,线程会wake up并执行压缩操作。在table被压缩之前经过的事务数量,即压缩率,可由system variable `gtid_executed_compression_period`来进行控制。如果将值设置为0,代表该thread永远不会wake up。 相比于其他存储引擎,innodb事务写入`mysql.gtid_executed`的过程有所不同,在innodb存储引擎中,过程通过线程`innodb/clone_gtid_thread`来执行。此GTID持久化线程将会分组收集GTIDs,并将其刷新到`mysql.gtid_executed`表中,并对table进行压缩。如果server中既包含innodb事务,又包含non-innodb事务,那么由`compress_gtid_table`线程执行的压缩操作将会干扰`clone_gtid_thread`的工作,并对其效率进行显著下降。为此,在此场景下更推荐将`gtid_executed_compression_period`设置为0,故而`compress_gtid_table`线程将永远不被激活。 `gtid_executed_compression_period`的默认值为0,且所有事务不论其所属存储引擎,都会被`clone_gitid_thread`写入到`mysql.gtid_exec uted`中。 当server实例启动后,如果`gtid_executed_compression_preiod`被设置为非0值,并且`compress_gtid_table`线程也启动,在多数server配置中,将会对`msyql.gtid_executed`表执行显式压缩。压缩通过线程的启动触发。 #### GTID生命周期 GTID生命周期由如下步骤组成: 1. 事务在source上被执行并提交,client transaction将会被分配GTID,GTID由`source UUID`和`smallest nonzero transaction sequence number not yet used on this server`组成。GTID也会被写入到binary log中(在log中,GTID被写入在事务之前)。如果client transaction没有被写入binary log(例如,事务被过滤或事务为read-only),那么该事务不会被分配GTID 2. 如果为事务分配了GTID,那么在事务提交时GTID会被原子的持久化。在binary log中,GTID会被写入到事务开始的位置。不管何时,如果binary log发生rotation或server shutdown,server会将所有写入到binary log file中事务的GTIDs写入到`mysql.gtid_executed`表中 3. 如果为事务分配了GTID,那么GTID的外部化是`non-atomically`的(在事务提交后非常短的时间)。外部化过程会将GTID添加到`gtid_executed` system variable所代表的GTID set中(`@@GLOBAL.gtid_executed`)。该GTID set中包含`representation of the set of all committed GTID transactions`,并且在replication中会作为代表server state的token使用。 1. 当binary logging启用时,`gtid_executed` system variable代表的GTIDs set是已被应用事务的完整记录。但是`mysql.gtid_executed`表则并记录所有GTIDs,可能由部分GTIDs仍存在于active binary log file中尚未被写入到gtid_executed table 4. 在binary log data被转移到replica并存储在replica的relay log中后,replica会读取GTID的值,并且将其设置到`gtid_next` system variable中。这告知replica下一个事务将会使用`gtid_next`所代表的GTID。replica在session context中设置`gtid_next` 5. replica在处理事务前,会验证没有其他线程已经持有了`gtid_next`中GTID的所有权。通过该方法,replica可以保证该该GTID关联的事务在replica上没有被应用过,并且还能保证没有其他session已经读取该GTID但是尚未提交关联事务。故而,如果并发的尝试应用相同的事务,那么server只会让其中的一个运行。 1. replica的`gtid_owned` system variable(`@@GLOBAL.gtid_owned`)代表了当前正在使用的每个GTID和拥有该GTID的线程ID。如果GTID已经被使用过,并不会抛出错误,`auto-skip`功能会忽略该事务 6. 如果GTID尚未被使用,那么replica将会对replicated transaction进行应用。因为`gtid_next`被设置为了`由source分配的GTID`,replica将不会尝试为事务分配新的GTID,而是直接使用存储在`gtid_next`中存储的GTID 7. 如果replica上启用了binary logging,GTID将会在提交时被原子的持久化,写入到binary log中事务开始的位置。无论何时,如果binary log发生rotation或server shutdown,server会将`先前写入到binary log中事务的GTIDs`写入到`mysql.gtid_executed`表中 8. 如果replica禁用binary log,GTID将会原子的被持久化,直接被写入到`mysql.gtid_executed`表中。mysql会向事务中追加一个statement,并且将GTID插入到table中该操作是原子的,在该场景下,`mysql.gtid_executed`表记录了完整的`transactions applied on the replica`。 9. 在replicated transaction提交后很短的时间,GTID会被非原子的externalized,GTID会被添加到replica的`gtid_executed` system variable代表的GTIDs中。在source中,`gtid_executed` system variable包含了所有提交的GTID事务。 在source上被完全过滤的client transactions并不会被分配GTID,因此其不会被添加到`gtid_executed` system variable或被添加到`mysql.gtid_executed`表中。然而,replicated transaction中的GTIDs即使在replica被完全过滤,也会被持久化。 - 如果binary logging在replica开启,被过滤的事务将会作为gtid_log_event的形式被写入到binary log,后续跟着一个empty transaction,事务中仅包含BEGIN和COMMIT语句 - 如果binary logging在replica被禁用,给过滤事务的GTID将会被写入到`mysql.gtid_executed`表 将filtered-out transactions的GTIDs保留,可以确保`mysql.gtid_executed`表和`gtid_executed`system variable中的GTIDs可以被压缩。同时其也能确保replica重新连接到source时,filtered-out transactions不会被重新获取 在多线程的replica上(`replica_parallel_workers > 0`),事务可以被并行的应用,故而replicated transactions可以以不同的顺序来提交(除非`replica_preserve_commit_order = 1`)。在并行提交时,`gtid_executed`system variable中的GTIDs将包含多个GTID ranges,多个范围之间存在空隙。在多线程replicas上,只有最近被应用的事务间才会存在空隙,并且会随着replication的进行而被填充。当replication threads通过`STOP REPLICA`停止时,这些空隙会被填补。当发生shutdown event时(server failure导致),此时replication 停止,空隙仍可能保留。 ##### What changes are assign a GTID GTID生成的典型场景为`server为提交事务生成新的GTID`。然而,GTIDs可以被分配给除事务外的其他修改,且在某些场景下一个事务可以被分配多个GTIDs。 每个写入binary log的数据库修改(`DDL/DML`)都会被分配一个GTID。其包含了自动提交的变更、使用`BEGIN...COMMIT`的变更和使用`START TRANSACTION`的变更。一个GTID会被分配给数据库的`creation, alteration, deletion`操作,并且`non-table` database object例如`procedure, function, trigger, event, view, user, role, grant`的`creation, alteration, deletion`也会被分配GTID。 非事务更新和事务更新都会被分配`GTID`,额外的,对于非事务更新,如果在尝试写入binary log cahche时发生disk write failure,导致binary log中出现间隙,那么生成的日志事件将会被分配一个GTID。 ## Replication Implementation 在replication的设计中,source server会追踪其binary log中所有对databases的修改。binary log中记录了自server启动时所有修改数据库结构或内容的事件。通常,`SELECT`语句将不会被记录,其并没有对数据库结构或内容造成修改。 每个连接到source的replica都会请求binary log的副本,replica会从source处拉取数据,而不是source向replica推送数据。replica也会执行其从binary log收到的事件。`发生在source上的修改将会在replica上进行重现`。在重现过程中,会发生表创建、表结构修改、数据的新增/删除/修改等操作。 由于每个replica都是独立的,每个连接到source的replica,其对`source中binary log`内容的replaying都是独立的。此外,因为每个replica只通过请求source来接收binary log的拷贝,replica能够以其自身的节奏来读取和更新其数据副本,并且其能在不对source和其他replicas造成影响的条件下开启或停止replication过程。 ### Replication Formats 在binary log中,events根据其事件类型以不同的格式被记录。replication使用的foramts取决于事件被记录到source server的binary log中时所使用的foramt。binary log format和replciation过程中使用到的format,其关联关系如下: - 当使用statement-based binary logging时,source会将sql statements写入到binary log。从source到replica的replication将会在replica上执行该sql语句。这被称之为statement-based replication,关联了mysql statement-based binary logging format - 当使用row-based loggging时,source将会将`table rows如何变更`的事件写入到binary log中。在replica中,将会重现events对table rows所做的修改。则会被成为row-based replication - `row-based logging`是默认的方法 - 可以配置mysql使用`mix-format logging`,但使用`mix-format logging`时,将默认使用statement-based log。但是对于特定的语句,也取决于使用的存储引擎,在某些床惊吓log也会被自动切换到row-based。使用mixed format的replication被称之为mix-based replication或mix-format replication mysql server中的logging format通过`binlog_format` system variable来进行控制。该变量可以在session/global级别进行设置 - 在将variable设置为session级别时,将仅会对当前session生效,并且仅持续到当前session结束 - 将variable设置为global级别时,该设置将会对`clients that connect after the change`生效,`但对于any current client sessions,包括.发出该修改请求的session都不生效` #### 使用statement-based和row-based replication的优缺点对比 每个binary logging format都有优缺点。对大多数users,mixed replication format能够提供性能和数据完整性的最佳组合。 ##### Advantages of statement-based replication - 在使用statement-based格式时,会向log files中写入更少的数据。特别是在更新或删除大量行时,使用statement-based格式能够导致更少的空间占用。在从备份中获取和恢复时,其速度也更快 - log files包含所有造成修改的statements,可以被用于审核database ##### Disadvantages of statement-based replication - 对于Statement-based replication而言,statements并不是安全的。并非所有对数据造成修改的statements都可以使用statement-based replication。在使用statement-based replication时,任何非确定性的行为都难以复制(例如在语句中包含随机函数等非确定性行为) - 对于复杂语句,statement必须在实际对目标行执行修改前重新计算。而当使用row-based replication时,replica可以直接修改受影响行,而无需重新计算 ##### Advantages of row-based replication - 所有的修改可以被replicated,其是最安全的replication形式 ##### disadvantages of row-based replication - 相比于statement-based replication,row-based replication通常会向log中ieur更多数据,特别是当statement操作大量行数据是 - 在replica并无法查看从source接收并执行的statements。可以通过`mysqlbinlog`的`--base64-output=DECODE-ROWS`和`--verbose`选项来查看数据变更