设为首页 收藏本站
查看: 1090|回复: 0

[经验分享] mysql GTID 主从复制模式

[复制链接]

尚未签到

发表于 2018-10-10 14:07:02 | 显示全部楼层 |阅读模式
mysql GTID 主从复制模式
  一、GTID的概述:

  1、全局事物标识:global transaction>  2、GTID事物是全局唯一性的,且一个事务对应一个GTID。
  3、一个GTID在一个服务器上只执行一次,避免重复执行导致数据混乱或者主从不一致。
  4、GTID用来代替classic的复制方法,不在使用binlog+pos开启复制。而是使用master_auto_postion=1的方式自动匹配GTID断点进行复制。
  5、MySQL-5.6.5开始支持的,MySQL-5.6.10后开始完善。
  6、在传统的slave端,binlog是不用开启的,但是在GTID中,slave端的binlog是必须开启的,目的是记录执行过的GTID(强制)。
  二、GTID的组成部分:
  前面是server_uuid:后面是一个序列号
  例如:server_uuid:sequence number
  7800a22c-95ae-11e4-983d-080027de205a:10
  UUID:每个mysql实例的唯一ID,由于会传递到slave,所以也可以理解为源ID。
  Sequence number:在每台MySQL服务器上都是从1开始自增长的序列,一个数值对应一个事务。
  三、GTID比传统复制的优势:
  1、更简单的实现failover,不用以前那样在需要找log_file和log_Pos。
  2、更简单的搭建主从复制。
  3、比传统复制更加安全。
  4、GTID是连续没有空洞的,因此主从库出现数据冲突时,可以用添加空事物的方式进行跳过。
  四、GTID的工作原理:
  1、master更新数据时,会在事务前产生GTID,一同记录到binlog日志中。
  2、slave端的i/o 线程将变更的binlog,写入到本地的relay log中。
  3、sql线程从relay log中获取GTID,然后对比slave端的binlog是否有记录。
  4、如果有记录,说明该GTID的事务已经执行,slave会忽略。
  5、如果没有记录,slave就会从relay log中执行该GTID的事务,并记录到binlog。
  6、在解析过程中会判断是否有主键,如果没有就用二级索引,如果没有就用全部扫描。
  要点:
  1、slave在接受master的binlog时,会校验master的GTID是否已经执行过(一个服务器只能执行一次)。
  2、为了保证主从数据的一致性,多线程只能同时执行一个GTID。
  六、使用GTID搭建mysql的主从复制的主要参数:
  [mysqld]
  #GTID:
  gtid_mode=on
  enforce_gtid_consistency=on
  server_id=2003306    #每天实例的server_id都要不一样
  #binlog
  log-bin=mysqlbin
  log-slave-updates=1   #允许下端接入slave
  binlog_format=row      #强烈建议,其他格式可能造成数据不一致
  #relay log
  skip_slave_start=1
  注意:建议使用mysql-5.6.5以上的最新版本。
  (二)、启动GTID的两种方法:
  方法一、
  1、如果是在已经跑的服务器,你需要重启一下mysql server。
  2、启动之前,一定要先关闭master的写入,保证所有slave端都已经和master端数据保持同步。
  3、所有slave需要加上skip_slave_start=1的配置参数,避免启动后还是使用老的复制协议。
  方法二、
  1、如果是新搭建的服务器,直接启动就行了。
  七、master-slave搭建的注意事项:
  (一)、使用GTID的方式,把salve端挂载master端:
  1、启动以后最好不要立即执行事务,而是先change master上。
  2、然后在执行事务,当然知不是必须的。
  3、使用下面的sql切换slave到新的master。
  stop slave;
  CHANGE MASTER TO
  MASTER_HOST='127.0.0.1',
  MASTER_PORT=3306,
  MASTER_USER='repl',
  MASTER_PASSWORD='repl',
  master_auto_position = 1;
  (二)、如果给已经运行的GTID的master端添加一个新的slave
  有两种方法:
  方法一、适用于master也是新建不久的情况。
  1、如果你的master所有的binlog还在。可以选择类似于上面的方法,安装slave,直接change master to到master端。
  2、原理是直接获取master所有的GTID并执行。
  3、优点:简单方便。
  4、缺点:如果binlog太多,数据完全同步需要时间较长,并且master一开始就启用了GTUD。
  方法二、适用于拥有较大数据的情况。(推荐)
  1、通过master或者其他slave的备份搭建新的slave。(看第三部分)
  2、原理:获取master的数据和这些数据对应的GTID范围,然后通过slave设置@@global.gtid_purged跳过备份包含的gtid。
  3、优点:是可以避免第一种方法的不足。
  4、缺点:相对来说有点复杂。
  (三)、通过备份搭建新的slave:(方法二的扩展)
  两种方法:
  方法一、mysqldump的方式:
  1、在备份的时候指定--master-data=2(来保存binlog的文件号和位置的命令)。
  2、使用mysqldump的命令在dump文件里可以看到下面两个信息:
  SET @@SESSION.SQL_LOG_BIN=0;
  SET @@GLOBAL.GTID_PURGED='7800a22c-95ae-11e4-983d-080027de205a:1-8';
  3、将备份还原到slave后,使用change master to命令挂载master端。
  注意:在mysql5.6.9以后的命令才支持这个功能。
  方法二、percona Xtrabackup
  1、Xtrabackup_binlog_info文件中,包含global.gtid_purged='XXXXXX:XXXX'的信息。
  2、然后到slave去手工的 SET GLOBAL.GTID_PURGED='XXXXXX:XXXX'。
  3、恢复备份,开启change master to 命令。
  注意:如果系统运行了很久,无法找到GTID的变好了,可以通过上面的方式进行查找。
  八、GTID如何跳过事务冲突:
  1、这个功能主要跳过事务,代替原来的set global sql_slave_skip_counter = 1。
  2、由于在这个GTID必须是连续的,正常情况同一个服务器产生的GTID是不会存在空缺的。所以不能简单的skip掉一个事务,只能通过注入空事物的方法替换掉一个实际操作事务。
  3、注入空事物的方法:
  stop slave;
  set gtid_next='xxxxxxx:N';
  begin;commit;
  set gtid_next='AUTOMAIC';
  start slave;
  4、这里的xxxxx:N 也就是你的slave sql thread报错的GTID,或者说是你想要跳过的GTID。
  九、GTID的参数注释:
  [master]>show global variables like '%gtid%';
  1、enforce_gtid_consistency:开启gtid的一些安全限制(介意开启)。
  2、gtid_executed:全局和seeeion级别都可以用。用来保存已经执行过的GTIDs。
  贴士:show  master status\G;输出结果中的Executed_Gtid_Set和gitd_executed一致。reset master时,此值会被清空。
  3、gtid_owned:全局和session级别都可用,全局表示所有服务器拥有GTIDs,session级别表示当前client拥有所有GTIDs。(此功能用的少)
  4、gtid_mode:是否开启GTID功能。
  5、gtid_purged:全局参数,设置在binlog中,已经purged的GTIDs,并且purged掉的GTIDs会包含到gtid_executed中。
  贴士:从而导致slave不会再去master请求这些GTIDs,并且Executed_Gtid_Set为空时,才可以设置此值。
  6、gtid_next:这个时session级别的参数:
  [master]>show session variables like '%gtid_next%';
  十、关于GTID的一些功能限制:
  (一)、更新非事务引擎:
  1、Case重现:
  master:对一个innodb表做一个多sql更新的事物,效果是产生一个GTID。
  slave:对应的表是MYISAM引擎,执行这个GTID的第一个语句后就会报错,因为非事务引擎一个sql就是一个事务。
  2、错误编号:
  last_Errno:1756
  3、异常恢复方案:
  (1)、简单的stop slave; start slave;就能够忽略错误。但是这个时候主从的一致性已经出现问题。需要手工的把slave差的数据补上。
  (2)、首先将引擎调整为一样的,slave也改为事务引擎。
  (二)、create table ....select statements
  1、case重现:
  master:直接执行一个create table select * from table;的sql
  2、报错:
  error 1786
  3、原理:
  由于create table ...select语句会生成两个sql,一个是DDL创建表SQL,一个是insert into 插入数据的sql。由于DDL会导致自动提交,所以这个sql至少需要两个GTID,但是GTID模式下,只能给这个sql生成一个GTID,如果强制执行会导致和上面更新非事务引擎一样的结果。
  (三)、一个sql同事操作innodb引擎和myisam引擎:
  case重现:t1表是innodb,t2表是myisam
  1、update t1,t2 set t1.id=1000,t2.id=1000 where t1.id=t2.id;
  2、报错:1785
  3、原理和第二个相同。
  (四)、在一个replication grouop 中,所有的mysql必须要统一开启或者关闭GTID功能。
  1、case重现:
  将一个未开启gtid的slave通过原始的binlog和pos方式连接到开启GTID的master。
  2、报错:
  The slave IO thread stops because the master has @@GLOBAL.GTID_MODE ON and this server has @@GLOBAL.GTID_MODE OFF。
  (五)、在一个replication group中,如果开启GTID以后,就不再允许使用classic的复制方式:
  1、case重现:
  将一个开启gtid的slave通过原始的binlog和pos方式连接到开启GTID的master。
  2、报错:

  ERROR 1776(HY000):Parameters MASTER_LOG_FILE,MASTER_LOG_POS,RELAY_LOG_FILE and>  (六)、GTID_MODE是not online的:
  需要重启才能生效,官方暂时不支持平滑的从classic replication切换到GTID replication。
  贴士:
  由于GTID开启需要重启系统,一个复制组中所有的实例必须统一开启或者关闭GTID,开启GTID以后不能在使用classic复制。
  问题:
  也就是说在线业务必须统一关闭,然后再启动,会导致服务中断。
  解决方案:
  1、针对这种情况,社区有两种对应的平滑升级的方案:
  一种是booking.com出品,这两个差别在淘宝9月份数据库月报里有说明,加了一个桥接的服务器,既可以运行GTID模式下,也可以运行classic模式下。
  另外一种是facebook.com出品。所有的slave可以在开启GTID模式的情况下,可以连接到没有开启GTID模式的master。
  2、可以关闭一个部分,停止写操作,但是读不用,将另一部分改成GTID模式。
  (七)、Temporary tables。
  1、create temporary table和drop temporary table语句一样在GTID环境下不支持。
  如果--enforce_gtid_consistency参数开启,并且autocommit=1,那么可以使用。
  (八)、关于Errant transaction
  1、Errant transaction:所谓的errant transaction也就是没有规范的从master执行,而是直接从slave执行的事务。
  2、由于GTID协议的原因,最开始已经提过(参见GTID architecture)。
  3、如果slave有errant transaction产生,由于GTID协议中的规则,很容易导致failover失败。主要有两种情况:
  a、在slave上做了无用的或者临时的errant transaction操作,如果该slave升级成为master的话,连接到它的所有数据库都会获取到这个事务。如果一样就会产生冲突。
  b、由于做了这个errant transaction这个事务以后,其他的slave还没有获取这个errant transaction的GTID,需要从master上发同步给其他的slave,但是主的binlog又被删掉了,这时将会报错。
  4、总之:尽量避免产生errant transaction。可以通过:set sql_log_bin=off的方式在slave执行sql,但是也要考虑到数据一致性。
  --------------------------------------
  ··············跳过错误
  从库已经执行过的事务是'e10c75be-5c1b-11e6-ab7c-000c296078ae:1-5',执行出错的事务是'e10c75be-5c1b-11e6-ab7c-000c296078ae:6',当前主备的数据其实是一致的,可以通过设置gtid_next跳过这个出错的事务。
  在从库上执行以下SQL:
  mysql> set gtid_next='e10c75be-5c1b-11e6-ab7c-000c296078ae:6';
  Query OK, 0 rows affected (0.00 sec)
  mysql> begin;
  Query OK, 0 rows affected (0.00 sec)
  mysql> commit;
  Query OK, 0 rows affected (0.00 sec)
  mysql> set gtid_next='AUTOMATIC';
  Query OK, 0 rows affected (0.00 sec)
  mysql> start slave;
  Query OK, 0 rows affected (0.02 sec)
  设置gtid_next的方法一次只能跳过一个事务,要批量的跳过事务可以通过设置gtid_purged完成。
  -------------------------------
  GTID与crash safe salve
  crash safe slave是MySQL 5.6提供的功能,意思是说在slave crash后,把slave重新拉起来可以继续从Master进行复制,不会出现复制错误也不会出现数据不一致。
  1、基于binlog文件位置的复制
  在基于binlog文件位置的复制下,要保证crash safe slave,配置下面的参数即可。
  relay_log_info_repository = TABLE
  relay_log_recovery = ON
  这样可行的原因是,relay_log_info_repository = TABLE时,apply event和更新relay_log_info表的操作被包含在同一个事务里,innodb要么让它们同时生效,要么同时不生效,保证位点信息和已经应用的事务精确匹配。同时relay_log_recovery = ON时,会抛弃master_log_info中记录的复制位点,根据relay_log_info的执行位置重新从Master获取binlog,这就回避了由于未同步刷盘导致的binlog文件接受位置和实际不一致以及relay log文件被截断的问题。
  在同时使用MTS(multi-threaded slave)时,为保证crash safe slave基于binlog文件位置的复制还需要设置sync_relay_log=1,因为MySQL在Crash恢复时必须先通过读取relay log补齐MTS导致的事务空洞。
  2、基于GTID的复制
  上面的设置并不适用于基于GTID的复制。在基于GTID的复制下,crash的Slave重启后,从binlog中解析的gtid_executed决定了要apply哪些binlog记录,所以binlog必须和innodb存储引擎的数据保持一致。要做到这一点,需要把sync_binlog和innodb_flush_log_at_trx_commit都设置为1,即所谓的"双1"。
  另外MySQL启动时,会从relay log文件中获取已接收的GTIDs并更新Retrieved_Gtid_Set。由于relay log文件可能不完整,所以需要抛弃已接收的relay log文件。因此relay_log_recovery = ON也是必须的。
  这样,对于基于GTID的复制,保证crash safe slave的设置就是下面这样。
  sync_binlog = 1
  innodb_flush_log_at_trx_commit = 1
  relay_log_recovery = ON
  关于如何设置以确保crash safe slave,官方文档有明确记载,见 17.3.2 Handling an Unexpected Halt of a Replication Slave。
  但是其中关于GTID的记载中存在笔误,将relay_log_recovery=1写成了relay_log_recovery=0 (#83711)。同时也没有提到必须设置"双1",但是"双1"是必要的,否则crash的Slave重启后,可能会重复应用binlog event也可能会遗漏应用binlog event(#70659)。其中遗漏应用binlog event的情况更可怕,因为Slave在不触发SQL错误的情况下就默默的和Master不一致了。
  3、设置"双1"对性能的影响
  出于安全考虑,强烈推荐设置"双1"。"双1"会增大每个事务的RT,但得益于MySQL的组提交机制,高并发下"双1"对系统整体tps的影响在可接受范围内。
  sysbench oltp.lua 10张表每张表100w记录(qps/并发数)
  对更新同一行这样无法有效并行的场景,"双1"对性能的影响非常大。
  sysbench update_non_index.lua 1张表1条记录(qps/并发数)
  对不能有效并行的Slave replay,存在同样的问题。
  通过指定tx-rate执行sysbench的update_non_index.lua脚本压测30秒,完成后检查主备延迟。
  可以发现在Slave被配置为"双1"的情况下,延迟非常严重,1000以上的QPS就会出现延迟,非"双1"下QPS到5000以上才会出现延迟(主库配置为"双1")。
  sysbench update_non_index.lua 1张表100w条记录 128并发(延迟/qps)
  以上测试环境是Percona Server 5.6运行在配置HDD的8 core虚机,由于测试结果和系统IO能力有很大关系,仅供参考。
  4、如何在非"双1"下保证crash safe slave
  如果是MySQL 5.7可以关闭log_slave_updates,这样MySQL会将已执行的GTIDs实时记录到系统表mysql.gtid_executed中,mysql.gtid_executed是和用户事务一起提交的,因此可以保证和实际的数据一致。
  log_slave_updates = OFF
  relay_log_recovery = ON
  如果是MySQL 5.6可以采用如下变通的方式。
  按照基于binlog文件复制时crash safe slave的要求设置relay_log_info_repository = TABLE
  relay_log_info_repository = TABLE
  relay_log_recovery = ON
  在Slave crash后,根据relay_log_info_repository设置相应的gitd_purged再开启复制,步骤如下。
  1.启动MySQL,但不开启复制
  mysqld --skip-slave-start
  2.在Slave上修改为基于binlog文件位置的复制
  change master to MASTER_AUTO_POSITION = 0
  3.启动slave IO线程
  start slave io_thread
  这里不能启动SQL线程,如果接受到的GTID已经在Slave的gtid_executed里了,会被Slave skip掉。
  4.检查binlog传输的开始位置(即Retrieved_Gtid_Set的值)
  show slave status\G
  假设输出的Retrieved_Gtid_Set值为e10c75be-5c1b-11e6-ab7c-000c296078ae:7-10
  5.在Master上检查gtid_executed
  show master status
  假设输出的Executed_Gtid_Set值为e10c75be-5c1b-11e6-ab7c-000c296078ae:1-10
  6.在Slave上设置gitd_purged为binlog传输位置的前面的GTID的集合
  reset master;
  set global gitd_purged='e10c75be-5c1b-11e6-ab7c-000c296078ae:1-6';
  7.修改回auto position的复制
  change master to MASTER_AUTO_POSITION = 1
  8.启动slave SQL线程
  start slave sql_thread
  但是,这种变通的方法不适合多线程复制。因为多线程复制可能产生gtid gap和Gap-free low-watermark position,这会导致Salve上重复apply已经apply过的event。后果就是数据不一致或者复制中断,除非设置binlog格式为row模式并且slave_exec_mode=IDEMPOTENT,slave_exec_mode=IDEMPOTENT允许Slave回放binlog时忽略重复键和找不到键的错误,使得binlog回放具有幂等性,但这也意味着如果真的出现了主备数据不一致也会被它忽略。
  5、MTS下特有的问题
  在同时使用MTS(slave_parallel_workers > 1)时,即使按上面crash safe slave的要求设置了基于GTID的复制,Slave crash后再重启还是会导致复制中断。
  通过强制杀掉MySQL所在虚机的方式模拟Slave宕机,然后再启动MySQL,MySQL日志中有如下错误消息:
  启动slave时也会报错
  mysql> start slave;

  ERROR 1872 (HY000): Slave failed to initialize>  出现这种现象的原因在于,relay_log_recovery=1 且 slave_parallel_workers>1的情况下,mysql启动时会进入MTS Group恢复流程,即读取relay log,尝试填补由于多线程复制导致的gap。然后relay log文件由于不是实时刷新的,在relay log文件中找不到gap对应的relay log记录(覆盖了gap的relay log起始和结束位置分别被称为低水位和高水位,低水位点即slave_relay_log_info.Relay_log_pos的值)就会报这个错。
  实际上,在GTID模式下,slave在apply event的时候可以跳过重复事件,所以可以安全的从低水位点应用日志,没必要解析relay log文件。 这看上去是一个bug,于是提交了一个bug报告#83713,目前还没有收到回复。
  作为回避方法,可以通过清除relay log文件,跳过这个错误。执行步骤如下:
  reset slave;
  change master to MASTER_AUTO_POSITION = 1
  start slave;
  在这里,单纯的调reset slave不能把状态清理干净,内部的Relay_log_info.inited标志位仍然处于未被初始化状态,此时调用start slave仍然会失败。因此需要补一刀change master。
  6、Master的crash safe
  前面一直在讲crash safe slave,Master的crash safe同样重要。 要想Master保持crash safe需要按下面的参数进行设置,否则不仅会丢失事务,gtid_executed还可能和实际的innodb存储引擎中的数据不一致。
  sync_binlog = 1
  innodb_flush_log_at_trx_commit = 1
  在Master配置为"双1"的情况下,Master crash后,如果没有发生failover,可以继续作为Master。 如果发生了failover,可以检查旧Master和新Master上由旧Master执行的事务集合是否一致。
  show master status
  如果一致,可以按MASTER_AUTO_POSITION = 1的方式将旧Master作为Slave和新Master建立复制关系。否则,考虑做事务补偿或从新Master上拉取备份进行恢复。
  在Master配置不是"双1"的情况下,在Master crash后由于难以准确知道旧Master上究竟执行了哪些事务,安全的做法是实施主备切换,并从新Master上拉取备份,把旧Master作为新Master的Slave进行恢复。


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-620040-1-1.html 上篇帖子: IT十八掌 Mysql 多表查询 下篇帖子: MySQL使用Xtrabackup进行增量热备
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表