• 1
  • 2
  • 3
  • 4
  • 5
mysql数据库问题 首 页  »  帮助中心  »  数据库  »  mysql数据库问题
MYSQL 5.7中新增的表GTID_EXECUTED
发布日期:2016-4-20 23:4:59

  MYSQL 5.7中新增的表GTID_EXECUTED

  

  正文

  MySQL 5.6版本开启GTID模式,必须要先打开参数log_slave_updates,简单来说就是一定在从机上再记录一份二进制日志。这样的无论对性能还是存储的开销,无疑会相应的增大。而MySQL 5.7版本开始无需在GTID模式下启用参数log_slave_updates,其中最重要的原因在于5.7在mysql库下引入了新的表gtid_executed,其表结构如下所示:

  mysql> SHOW CREATE TABLE mysql.gtid_executed\G

  *************************** 1. row ***************************

  Table: gtid_executed

  Create Table: CREATE TABLE `gtid_executed` (

  `source_uuid` char(36) NOT NULL COMMENT 'uuid of the source where the transaction was originally executed.',

  `interval_start` bigint(20) NOT NULL COMMENT 'First number of interval.',

  `interval_end` bigint(20) NOT NULL COMMENT 'Last number of interval.',

  PRIMARY KEY (`source_uuid`,`interval_start`)

  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 STATS_PERSISTENT=0

  1 row in set (0.00 sec)

  简单来说,该表会记录当前执行的GTID。列source对应UUID,列interval_start/interval_end表示的是事务号。在MySQL 5.6中必须配置参数log_slave_updates的最重要原因在于当slave重启后,无法得知当前slave已经运行到的GTID位置,因为变量gtid_executed是一个内存值:

  mysql> select @@global.gtid_executed\G

  *************************** 1. row ***************************

  @@global.gtid_executed: 7af7d3ea-933b-11e5-9da7-fa163e30f9a2:1-72054

  1 row in set (0.00 sec)

  所以MySQL 5.6的处理方法就是启动时扫描最后一个二进制日志,获取当前执行到的GTID位置信息。当然,如果DBA不小心将二进制日志删除了,那么这又会带来灾难性的问题。

  因此,MySQL 5.7将gtid_executed这个值给持久化了。采用的技巧与MySQL 5.6处理SQL thread保存位置的方式一样,即将GTID值持久化保存在一张InnoDB表中,并与用户事务一起进行提交,从而实现数据的一致性:

  START TRANSACTION;

  # user statement

  ......

  INSERT INTO mysql.gtid_executed VALUES (...)

  END;

  需要注意的是表mysql.gtid_executed是在主服务器和从服务器上有进行更新的,而表slave_relay_log_info仅在从服务器上更新。

  MySQL 5.7对于表mysql.gtid_executed的更新策略也有些不同,如果没有主服务器没有开启log_bin或者从服务器没有开启log_slave_updates,其会每一个事物更新表gtid_executed,这样服务器重启后可以快速知道当前服务器执行到的GTID位置。因此,用户可能在服务器上看到类似如下的内容:

  mysql> select * from mysql.gtid_executed;

  +--------------------------------------+----------------+--------------+

  | source_uuid | interval_start | interval_end |

  +--------------------------------------+----------------+--------------+

  | 05e16691-bf69-11e5-97cf-fa163e30f9a2 | 1 | 4334294 |

  | 05e16691-bf69-11e5-97cf-fa163e30f9a2 | 4334296 | 4352984 |

  | 05e16691-bf69-11e5-97cf-fa163e30f9a2 | 4352985 | 4352985 |

  | 05e16691-bf69-11e5-97cf-fa163e30f9a2 | 4352986 | 4352986 |

  | 05e16691-bf69-11e5-97cf-fa163e30f9a2 | 4352987 | 4352987 |

  | 05e16691-bf69-11e5-97cf-fa163e30f9a2 | 4352988 | 4352988 |

  ......

  那这样岂不是表gtid_executed中的记录会疯狂增长。为此,MySQL 5.7又引入了新的线程,用来对此表进行压缩,该线程如下所示:

  mysql> select thread_id,thread_os_id,name,

  -> processlist_command,processlist_state from threads

  -> where name like '%compress%'\G

  *************************** 1. row ***************************

  thread_id: 39

  thread_os_id: 23816

  name: thread/sql/compress_gtid_table

  processlist_command: Daemon

  processlist_state: Suspending

  1 row in set (0.01 sec)

  参数gtid_executed_compression_period用来控制每执行多少个事务,对此表进行压缩,默认值为1000。因此,过一段时间后,上述的表mysql.gtid_executed会压缩成如下的内容:

  mysql> select * from mysql.gtid_executed;

  +--------------------------------------+----------------+--------------+

  | source_uuid | interval_start | interval_end |

  +--------------------------------------+----------------+--------------+

  | 05e16691-bf69-11e5-97cf-fa163e30f9a2 | 1 | 4334294 |

  | 05e16691-bf69-11e5-97cf-fa163e30f9a2 | 4334296 | 4354329 |

  ......

  如果MySQL服务器启用了二进制日志,则表mysql.gtid_executed的更新仅在二进制rotation时发生,因为发生重启等情况依旧可以通过扫描二进制日志判断得知当前运行的GTID位置。

  开启log_slave_updates后,主从服务器性能有多少的差异,这是一个比较有意思的问题。不过IMG社区群中的西毒同学,已经发现了另一个有意思的问题,即主服务器将innodb_flush_log_at_trx_commit设置为0,依然能保证crash safe。