关键词:mysql主从复制,mysql复制,MGR,mysql并行复制
目录
【1】mysql支持的复制类型
【2】mysql的主从4种同步方式介绍
(1)异步 (2)同步 (3)5.6 半同步 (4)5.7增强半同步
【3】MGR的介绍
【4】mysql并行复制
【4.1】并行复制的当前大三模式
【库间并发】【组提交】【WriteSet】
正文
【1】mysql支持的复制类型
基于binlog的3种模式(详情参考:binlog的3种日志记录模式),oracle在mysql5.5版本收购
【1.1】statement:基于语句的复制(5.5.6之前默认),主服务器执行的SQL语句,在从服务器执行同样的语句
【1.2】row:基于行的复制(5.5.7之后默认),把改变的内容复制到从库,而不是把SQL命令在从库重新执行一遍。mysql5.0就开始支持
【1.3】mixed:混合类型的复制,默认是使用 statement 语句方式复制,一旦发现基于语句无法精确复制时(比如now() 因为主从有延迟导致数据不一致)就会采用基于 row 行的方式复制。
【2】mysql的主从4种同步方式介绍
以下图片均引用自《深入浅出mysql开发、优化与管理维护》
【2.1】异步复制(和MSSQL的高性能模式镜像一样):(3.23 native)
主库只管binlog dump数据到从库,不保证主从完全一致,断电、崩溃等情况会丢失数据。
【2.2】全同步复制:
【2.2.1】核心概念:主从复制,主库要等到从库重做事务并且提交成功,接受到ACK成功确认标识后,主库才能提交事务。
【2.2.2】与半同步的区别:半同步是只需要持久化到relay log阶段即可返回ACK成功标识给主库,而全同步需要等待从库SQL进程完整的运行完该事务内容才能返回ACK成功标识。
【2.2.3】原理:
主库事务写入redo log和 binlog 文件后,binlog dump线程提取binlog数据给IO线程,IO线程把数据加载回从库的relay log文件。
从库SQL线程开启事务重做应用relay log数据操作,等从库提交完后,返回ACK确认标识,主库才会提交。
【2.3】传统半同步复制:(自5.5开始,插件)
【2.3.1】原理:
master事务commit 指令已经写入binlog(注意,这里已经提交了,再去同步,只是在等一个ACK)和 binlog 文件后,binlog dump线程提取binlog数据给IO线程,IO线程把数据加载回从库的relay log文件。
只要IO线程-》slave的relay log已经flush disk 磁盘落地,slave就返回ACK确认标识给master。
【2.3.2】特性:
这里的commit主库上是已经在 binlog、redo log 中提交了的,其他session都可以看到。
但需要等待从库返回ACK确认标识才会把事务提交到存储引擎持久化(即 ibdata、ibd等磁盘数据文件)且返回到client一个commit成功的指令。
【2.3.3】宕机情况:
master上这个事务其实是已经提交了并且落盘master的binlog了,master的其他Session是可以看到这个提交后的事务的,而slave还没有commit。
这个时候master挂了,master上的数据和slave不一致。master crash后,master根据redo重做提交了这个事务.
在切换到从后,slave因为没有commit而回滚了这个事务导致数据丢失。导致主从不一致。
【2.4】增强半同步复制(mysql 5.7.4及以上才可以用,与MSSQL镜像高安全模式相同):
与【2.3】传统半同步复制大致一样。唯一的区别就是,在事务提交过来后:
核心区别:主库会等从库在relay log 阶段持久化该事务到该文件后,接受ACK成功确认标识后,再进行提交(commit指令写入redo log)
【2.4.1】传统的半同步复制:
master 会把binlog和redo log全部写了。
【2.4.2】增强半同步复制:
master 只会写binlog,然后等slave的IO线程把事务持久化(flush disk)到 relay log 上后,返回ACK确认标识。
master收到确认标识后才会commit 写入到redo log,并返回给客户端commit成功的指令。
【2.4.3】宕机情况:
主库上这个事务因为没有写入redo log,所以这个事务被认为是未提交。master的其他session是不可以看到这个事务的。
这个时候主库挂了,master上的数据和slave一致,master crash后,slave不丢数据。
【2.5】GTID 复制(mysql 在 5.6.2 及之后开始支持GTID):
【2.5.1】GTID(Global Transaction Identifiers)概念:
对于一个已提交事务的编号,事务的唯一编号,并且是一个全局唯一的编号。GTID和事务会记录到binlog中,用来标识事务。
GTID是用来替代以前,传统复制方法(binlog+position),mysql 5.6.2开始支持GTID。
mysql支持GTID后,一个事务在集群中就不再孤单,在每一个节点中,如果存在具相同标识符的情况,可以避免同一个事务,在同一个节点出现多次的情况。
(可以初步理解成row模式的,和statement的区别,前者记得是具体做了什么事,后者记录的是位置)
GTID的出现最直接的效果就是,每一个事物在集群中具有了唯一性的意义,相对于行复制来讲数据安全性更高,故障切换更简单。
【2.5.2】简单案例解释概念:
比如,当我们一主2从,主出故障后切换到从DB1上去了,那么另外2台机器,需要重新手动构建主从;
具体为:
-- 使用传统方式构建的主库宕机重新搭建
-- 麻烦点:每台机器的Binlog名字和Postion位置点都不一样,需要重新定位文件在哪里,位置在哪里
change master to
master_host='xxx',
master_user='xxx',
master_password='xxx',
master_port='xxx',
master_log_file='xxx',
master_log_pos='xxx'; -- 使用GTID方式的主库宕机重新搭建
-- 优势点:每台机器上的GTID都是一样的,不需要管文件是哪个,位置在哪里,可以自动根据GTID定位
change master to
master_host='xxx',
master_user='xxx',
master_password='xxx',
master_port='xxx',
master_auto_postion=1;
【2.5.3】GTID的组成
GTID 是由 server_uuid:Sequence_Number 组成;
(1)server_uuid:是一个mysql实例的全局唯一表示;存放在 ${datadir}/auto.cnf
(2)Sequence_Number:是mysql内部的一个事务的标号,一个mysql实例不会重复的序列号(保证服务器内唯一),也表示在该实例上已经提交事务的数量,并且随着事务提交而递增。
(3)根据GTID可以知道事务最初是在哪个实例上提交的,方便故障排查和切换
【2.6】复制之间的区别
【3】MGR 群组复制
MGR一键部署参考脚本:https://github.com/zhaowengxing/automgr
(1)整体架构
(2)replication plugin 插件
(3)GCS
【2.3】MGR的模式
【2.3.1】单主模式(single primary mode)
(1)单主模式基本架构与形式概念
(2)单主模式的运行机制
【2.3.2】多主同步模式
(1)多主同步模式的冲突检测(核心是基于主键),这个检测就是certify
这个冲突检测就是最开始运行原理的certify
如果真有主键相同的操作执行了怎么办?先执行的提交,后执行的冲突回滚,如图
(2)多主同步模式下的限制
限制和规则
•仅InnoDB Engine(Transactional and row level lock)
•表必须有主键
•gtid-mode=ON
•binlog格式为Row-based
•对于同一个对象执行DDL和DML应在同一成员上执行,不支持在不同服务器上执行DDL
•不支持具有多级外键依赖关系的表,特别是已定义CASCADING外键约束的表
•不支持“serializable”隔离级别
【2.4】MGR的监视
(1)常用系统表监视
两个performance_schema表
•replication_group_members
•replication_group_member_stats
本地成员状态
•扩展Replication performance_schema 表
•group_replication_recovery channel information
•group_replication_applier channel information
•新的全局变量
•group_replication_primary_member
(2)MGR群组复制的高可用性
更好的容错度
群组复制的高可用性
–故障(F)所需的服务器数量(N)N = 2F + 1.
–最多支持9个成员
•允许4个成员故障。
–没有脑裂的问题
•仅当大多数成员(N/2+1)在线时,群组才可用,如下图
【2.5】 脑裂问题
(1)什么是脑裂?(2)MGR为什么会出现脑裂?
(1)什么是脑裂:我理解的就是,一个大脑控制变成了2个大脑控制,各做各的。产生了不同步,不一致的操作与选择。
(2)MGR为什么会出现脑裂:大多数情况是因为网络连接问题。如下图
检测网络分区:Performance_Schema.replication_group_members
有2个可能-》《1》真就另外3台挂掉了 《2》另外3台没挂,是因为和这2台连不上了无法通信
我们这里说《2》这种情况
即,本来MGR群组的5台机器都能互相连通,突然,S3-S5这3台机器与S1-S2机器失去了连接。
只有这S1-S2这2台机器被监控到在线,但其他3台机器是多数节点,它们也可能有连接在使用。对于S3-S5而言,S1-S2是宕机概念的。
然后对于S3-S5而言的MGR是可以正常运行的,就把其S1-S2丢失了。
这个时候S1-S2 这2台 查询可以正常运行,但不可以进行增删改(如果发生增删改,就无法再次回到MGR群主里了),如下图
如何处理呢?强制指定节点生成一个新的MGR。
【2.6】MGR读取一致性参数解析
group_replication_consistency8.0.14之后
•EVENTUAL,BEFORE,AFTER和BEFORE_AND_AFTER
(1)EVENTUAL(默认值)事务在执行之前不等待先前的事务应用,也不等待其他成员应用其更改。这是8.0.14之前的
(2)BEFORE
事务将在开始执行之前等待所有先前的事务完成。这可确保此事务将在最新的数据快照上执行,而不用管是哪个成员。
(3)AFTER
事务将等待其更改已应用于其他成员。这可确保一旦此事务完成,所有后续事务都会读取包含其更改的数据库状态,而不管它们在哪个成员上执行。
(4)BEFORE_AND_AFTER
•此事务将等到:
1)所有先前的事务在开始执行之前完成;
2)其变更已适用于其他成员。这可确保:
1)此事务将在最新的数据快照上执行;
2)一旦此事务完成,所有后续事务都会读取包含其更改的数据库状态,而不管它们在哪个成员上执行
【2.7】了解MGR节点状态
参考:https://blog.csdn.net/d6619309/article/details/70432640
【4】mysql并行复制
【4.1】并行复制的当前大三模式
【库间并发】【组提交】【WriteSet】
总的来说MySQL关于并行复制到目前为止经历过三个比较关键的时间结点“库间并发”,“组提交”,“写集合”;真可谓是*代有人才出,前
浪死在沙滩上;总的来说就后面的比前面的不知道高到哪里去了!
【库间并发】(5.6)
库间并发的理论依据是这样的 ---- 一个实例内可能会有多个库(schema),不同的库之间没有什么依赖关系,所以在slave那边为
每一个库(schema)单独起一个SQL线程,这样就能通过多线程并行复制的方式来提高主从复制的效率。
这个理论听起来没问题,但是事实上一个实例也就一个业务库,所以这种库间并发就没什么作用了;也就是说这个方式的适用场景
比较少,针对这个不足直到“组提交”才解决!
【组提交】(5.7)
组提交的理论依据是这样的 --- 如果多个事务他们能在同一时间内提交,这个就间接说明了这个几个事务锁上是没有冲突的,
也是就说他们各自持有不同的锁,互不影响;逻辑上我们几个事务看一个组,在slave以“组”为单位分配给SQL线程执行,这样
多个SQL线程就可以并行跑了;而且不在以库为并行的粒度,效果上要比“库间并发”要好一些。
这个事实上也有一些问题,因为它要求库上要有一定的并发度,不然就有可能变成每个组里面只有一个事务,这样就有串行没什么
区别了,为了解决这个问题MySQL提供了两个参数就是希望在提交时先等一等,尽可能的让组内多一些事务,以提高并行复制的效率。
“binlog_group_commit_sync_no_delay_count” 设置一个下水位,也就是说一个组要凑足多少个事务再提交;为子防止永远也凑不足
那么多个事务MySQL还以时间为维度给出了另一个参数“binlog_group_commit_sync_delay”这个参数就是最多等多久,
超过这个时间长度后就算没有凑足也提交。
亲身经历呀! 这两个参数特别难找到合的值,就算今天合适,过几天业务上有点变化后,又可能变的不合适了;如果MySQL能自己
达到一个自适应的效果就好了;这个自适用要到WriteSet才完成(WriteSet并不是通过自动调整这两个参数来完成,
它采用了完全不同的解决思路)。
【WriteSet】(8.0)
WriteSet解决了什么问题?当然是解决了“组提交”的问题啦! 说了和没说一个样,好下面我们来举个例子(比较学院派);假设你第一天
更新了id == 1 的那一行,第二天你更新了id == 2 的那一行,第三天有个slave过来同步你的数据啦! 以“组提交”的尿性,这两个更新
会被打包到不同的“组”,也就是说会有两个组;由于每个组内只有一个事务,所以逻辑上就串行了,起来!
身为DBA的你一可以看出来这两个事实上是可以打包到同一个组里来的,因为他们互不冲突,就算打包到同一个组也不引起数据的不
一致。 于是你有两个办法
办法1): 妹妹你大胆的把“binlog_group_commit_sync_no_delay_count”设置成 2,也就是说一个组至少要包含两个事务,并且把
“binlog_group_commit_sync_delay”设置成24小时以上!如果你真的做了,你就可以回家了,你的数据库太慢了(第一条update等了一天),
才完成!
办法2): 叫MySQL用一本小本子记下它最近改了什么,如果现在要改的数据和之前的数据不冲突,那么他们就可以把包到同一个组;还是
我们刚才的例子,由于第二天改的值的id==2所以它和第一天的不冲突,那么它完全可以把第二天的更新和第一天的更新打包到同一个组。
这样组里面就有两个事务了,在slave第三天回放时就会有一种并行的效果。
这本小本子这么牛逼可以做大一点吗?当然!binlog_transaction_dependency_history_size 这个参数就小本子的容量了;那我的MySQL
有这本小本子吗? 如果你的mysql比mysql-5.7.22新的话,小本子就是它生来就有的。
也就是说“WriteSet”是站在“组提交”这个巨人的基础之间建立起来的,而且是在master上做的自“适应”打包分组,所以你只要在master上
新增两个参数
binlog_transaction_dependency_tracking = WRITESET # COMMIT_ORDER
transaction_write_set_extraction = XXHASH64
在MySQL 8.0中,该版本引入了参数binlog_transaction_depandency_tracking用于控制如何决定事务的依赖关系。
该值有三个选项:
默认的COMMIT_ORDERE表示继续使用5.7中的基于组提交的方式决定事务的依赖关系;
WRITESET表示使用写集合来决定事务的依赖关系;
还有一个选项WRITESET_SESSION表示使用WriteSet来决定事务的依赖关系,但是同一个Session内的事务不会有相同的last_committed值。
理论说完了,下面我们看一下实践。
【WriteSet实践】
基于WriteSet的并行复制环境怎么搭建我这里就不说了,也就是比正常的“组提交”在master上多加两个参数,不讲了;我这里想
直接给出两种并行复制方式下的行为变化。
1): 我们要执行的目标SQL如下
create database tempdb;
use tempdb;
create table person(id int not null auto_increment primary key,name int); insert into person(name) values(1);
insert into person(name) values(2);
insert into person(name) values(3);
insert into person(name) values(5);
2): 看一下组提交对上面SQL的分组情况
3): 看write_set的对“组提交”优化后的情况
可以看到各个insert是可以并行执行的,所以它们被分到了同个组(last_committed相同);last_committed,sequence_number,
这两个值在binlog里面记着就有,我在解析binlog的时候习惯使用如下选项
mysqlbinlog -vvv --base64-output='decode-rows' mysql-bin.000002
【总结】
WriteSet是在“组提交”方式上建立起来的,一种新的并行复制实现;相比“组提交”来说更加灵活;当然,由于并发度上去了,相比“组提交”
WriteSet在性能上会更加好一些,在一些WriteSet没有办法是否冲突时,能平滑过度到“组提交”模式。