首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往
清单首页dba文章详情

mysql replication

复制是指将主库的ddl,dml等操作通过binlog日志,传输到复制服务器,副本进行回放这些日志,从而使得从库和主库数据保存同步的工作模式

复制的主要应用场景

1 备份

2 高可用

3 读写分离

4 分布式架构

5 迁移升级

复制架构

传统 1主1从 1主多从 级联主从 双主

演变 (增强)半同步 过滤 延时 GTID MTS(多sql线程回放relay log)

新型 多源复制(5.7+支持)

MGR 组复制 5.7.17+支持 8.0增强

复制前提

2台以上的数据库实例 具备不同的server_id,server_uuid

主库开启binlog 创建复制用户

备份 主库数据恢复到从库

告知从库复制信息

启动线程

查看复制状态

1在主库创建用户并备份给从库

create user repl@'10.0.0.%' identified with mysql_native_password by '123';

grant replication slave , replication client on *.* to repl@'10.4.7.%';

2找到备份点的位置号,在从库开启主从

mysql> change master to

-> master_host='10.4.7.11',

-> master_port=3306,

-> master_user='repl',

-> master_password='123',

-> master_log_file='mysql-bin.000007',

-> master_log_pos=156,

-> master_connect_retry=10;

mysql> start replica;

查看主从状态

show slave status \G

传统复制和GTID转换

原来的数据库可能是升级来的,可能没有开启gtid

1查看各个节点的gtid模式有没有开启

select @@enforce_gtid_consistency;

select @@gtid_mode;

2在线在所有节点测试是不是有不兼容一致性的事务存在(需要观察)

没有报错修改成on

set global enforce_gtid_consistency=warn;

3生成新的事物为匿名方式,

set global gtid_mode=off_permissive;

4修改生成新的事务使用gtid

set global gtid_mode=on_permissive;

5查看各个节点剩余匿名事务量

show status like 'ongoing_anonymous_transaction_count';

6修改gtid为on

set global gtid_mode=on;

7在从库重新连接主从

stop slave;

change master to master_auto_position=1;

start slave;

show slave status \G

主从复制原理

重要文件和线程

1主库

binlog

binlogdump线程

2从库

接受主库binlog 存放在reaylog

master.info文件和relaylog.info文件(可以放到表里面)

io线程

sql线程

1执行change master to 把主库的信息告诉从库,存放在master.info

在mysql库下

show variables like '%master%';

master_info_repository | TABLE

2start slavg 启动两个线程

3io线程读取master.info里面的信息连接主库

4主库连接层负责用户名和密码验证并且分配一个binlogdump线程

5dump线程拿到io线程提供的复制起点,去主库binlog去拿复制起点给io线程

6binlog一旦产生新的会会通知dump线程,dump传输给io线程存储到relaylog日志文件mastr.info也会随之更新

7sql线程会等待relaylog落地通知sql线程会去relaylog.info去看上次回放到哪里

show slave hosts;

主库可以查看从库注册的信息

ip地址是看不到的如果想看到

需要在配置文件加 report_host=自己的地址需要把自己的地址暴露给别的节点,在有一些高可用需要的

传统复制和gtid复制原理

1主库在记录二进制日志的时候会生成一个gtid信息

2搭建主从的时候不需要再写文件位置点了,需要加一个新的参数 master_auto_position=1

会先读取从库的binlog找有没有gtid信息,再去读relaylog的gtid信息如果没有找到,就会通知主库从1号复制

set-gtid-purged要打开备份的时候默认是auto是开启的pxb备份新版会把主库的binlog也备份走

异步和半同步的区别

rpl_semi_sync_master_wait_point= AFTER_SYNC;

主从dump线程把数据交给io线程是不会管io线程是否接收到

半同步(after_commit)是通过阻塞commit阶段实现,是通过redo,commit状态成功状态等待从库返回ack(有超时时间),

如果等待ack状态 主库宕机,极端情况从库数据,比主库多

(after_sync)

增强半同步 也是提供阻塞commit阶段实现,是通过binlog提交成功后等待从库返回ack(有超时时间) redo还未commit不管那个机器宕机都不会影响一致性

5.7加入

如果binlog一旦提交成功就会在binlog的末尾加一个xid,代表事务成功

超时就会退化成异步

会影响到事务的并发度

group commit组提交,可以一定程度降低

FLUSH 阶段

1)      持有Lock_log mutex [leader持有,follower等待]

2)      获取队列中的一组binlog(队列中的所有事务)

3)      将binlog buffer到I/O cache

4)      通知dump线程dump binlog

SYNC阶段

1)      释放Lock_log mutex,持有Lock_sync mutex[leader持有,follower等待]

2)      将一组binlog 落盘(sync动作,最耗时,假设sync_binlog为1)

COMMIT阶段

1)      释放Lock_sync mutex,持有Lock_commit mutex[leader持有,follower等待]

2)      遍历队列中的事务,逐一进行innodb commit

3)      释放Lock_commit mutex

4)      唤醒队列中等待的线程

设置参数

binlog_group_commit_sync_no_delay_count= 组内的数量

binlog_group_commit_sync_delay=多久提交一次

主从监控

主库线程查看

show processlist;

show slave hosts;

从库线程查看

show slave status\G

io线程故障

Last_IO_Errno: 0

Last_IO_Error

看从库的这俩指标

可以用远程用户连接一下

日志丢失

测试环境 关闭主从,清空连接信息,清空从库binlog,从新连接

生产环境:从新搭建主从

sql线程故障

如何把relaylog分离

代码语言:javascript
复制
show variables like '%relay%';
relay_log                 | k8s3-relay-bin 前缀名字
relay_log_basename        | /data/mysql/data_3306/k8s3-relay-bin 路径
| relay_log_info_file       | relay-log.info                             |
| relay_log_info_repository | TABLE 文件类型
relay_log_purge           | ON 回放完成的文件会自动清理

如果relaylog损坏推荐重构

回放日志出现问题:检查平台,版本,参数 SQL_mode 调整一致

被修改的对象不存在(库,表,用户)

异步复制,双主架构,导致数据不一样

从库已经有了,主库在执行

处理方式

跳过报错

1先停止从库

stop slave

2 set gtid_next='gtid号'; #下个gtid啥都没做

3begin;commit;

4set gtid_next='AUTOMATIC';

5start slave;

校验数据

双主架构设计不好

只能按照一张表的数据去校验

规避方法

从库设置只读

2不使用双主结构 pxc MGR替代

3半同步 增强半同步等

4使用pt工具相关工具校验数据并同步

主从复制延时方法 show slave status\G

Seconds_Behind_Master: 0(作为参考)

和日志时间戳有关系从库对比从库用io线程获取日志存到relaylog中把获取时间的时间戳也会存储简称ts1

从库回放日志生成的时间戳(t2)用t2减去t1就计算出来了

导致显示不准确的情况

1时间被修改

2 如果长时间事务不提交 瞬间变大 瞬间归零的现象

3 MTS回放也会导致Seconds_Behind_Master

用那些方法更准确

gtid 通过binlog的gtid判断

pt-hearbeat

定位慢在哪里了

先看执行到那个了,下个卡了

大事务的话杀掉会很慢,或者重启

那些原因会导致延时

1主库写成功了,从库拿不到日志,主库并发好多操作往binlog写发的慢,从库收的也慢可以做出组提交(5.6版本需要手动配5.7自动底层实现)

2大事务也会导致传输的慢(组提交或者大事务拆分)

3io线程写relaylog慢硬盘问题MTS

4只能一条一条回放,因为只有一个sql线程(事务回放的顺序)多sql线程一直延续到5.7.22才解决

5.6增加基于不同的库并发不同的sql,同一个库还是串行回放

5.7新增在主库并发提交的是没有冲突的,从库基于每一组并发sql对一些一组内的打一些seq号,从库回放

参数

代码语言:javascript
复制
show variables like '%para%';
| innodb_parallel_read_threads | 4        |
| slave_parallel_type          | DATABASE | 建议LOGICAL_CLOCK
| slave_parallel_workers       | 0   并发回放sql线程个数

5.7.22以后基于writeset

binlog_transaction_dependency_tracking = writeset

transaction_write_set_extraction =XXHASH64

会生成一个数据库对象,vector维特(向量)一个64位int 的变量存储已经提交事务的hash值提交的事务包含主键或者唯一键

做hash 判断当前提交的事务是否与已经提交的事务更新了同一行

当事务每次提交时,会计算修改的每个行记录的WriteSet值,然后查找哈希表中是否已经存在有同样的WriteSet

若无,WriteSet插入到哈希表,写入二进制日志的last_committed值保持不变,意味着上一个事务跟当前事务的last_committed相等,那么在slave就可以并行执行 若有,更新哈希表对应的writeset的value为sequence number,并且写入到二进制日志的last_committed值也要更新为sequnce_number。意味着,相同记录(冲突事务)回放,last_committed值必然不同,必须等待之前的一条记录回放完成后才能执行

延时从库

主从只能解决物理损坏

逻辑损坏延时从库可以延时2个小时

哪里延时

日志该拿就拿,sql线程延时判断时间戳信息

如何恢复

10点删库

延时从库只执行了8点的日志

11点我们发现的,把延时从库恢复到某一个时间点

此时我们恢复一个小时的binlog就和原来数据库数据一样

延时从库配置

mysql可以单独停止某一个线程

1 stop slave sql_thread;

2延时sql线程

change master to master_delay = 多少秒;

2启动sql线程

start slave

在show slave status;查看

代码语言:javascript
复制
      SQL_Delay: 0
      SQL_Remaining_Delay: NULL

恢复思路

1及时监控

2 立即停止延时从库sql线程关闭 需要对业务挂维护页面

3停止所有线程

4在延时从恢复数据库数据

show relaylog events in '文件';

修改延时从的参数置为0

change master to master_delay = 0 ;

start slave until sql_before_gtids = '跳过那个日志'

把a业务切到延时从

过滤复制

基本原理主机点有abc三个库只想复制a库数据

有两种方案

1在主库方面 在binlogdump线程做a库的发送只能基于库级别

两个参数

show master status;

Binlog_Do_DB 白名单

Binlog_Ignore_DB黑名单

2在从库控制在回放的时候选择想要的日志回放

show slave status \G

代码语言:javascript
复制
库级别
Replicate_Do_DB: 
Replicate_Ignore_DB: 
表级别
Replicate_Do_Table: 
Replicate_Ignore_Table: 
模糊匹配
Replicate_Wild_Do_Table: 
Replicate_Wild_Ignore_Table:
1配置文件配置并重启
2关闭sql线程 
stop slave sql_thread;
change replication filter Replicate_Do_DB = (库名,库名)
start slave sql_thread;

多源复制(MSR)

5.7以后才有

多源复制通常配合过滤复制才有

多主1从多套不同的数据库汇总到一台机器

两主1从

1创建复制用户每个主库都要创建

set sql_log_bin=0;

create user repl@'10.4.7.%' identified with mysql_native_password by '123';

grant replication slave on *.* to repl@'10.4.7.%';

2从库执行连接语句有几个主库就指定几个master

change master to master_host='10.4.7.13',

master_user='repl',

master_password='123',

master_auto_position=1 for channel 'master_2';

3启动线程几个主库就指定几个master

start slave for channel 'master_1';

4查看或者监控方法

show slave status for channel 'master_1'\G

多源复制过滤

change replication filter Replicate_Do_DB =(库名,库名) for channel 'master_2'

MGR组复制

pxc Percona XtraDB Cluster

MGC  MariaDB Galera Cluster

5.7.17+以后出现的

理念用多台机器组成一个复制组

1主库发生新事务,事务执行期间将binlog刷新到binlog cache

2生成WriteSet(db_id,database_id,table_id,主键,主键值,对应的日志)

3具体在MYSQL_BIN_LOG::prepare之后但是在MYSQL_BIN_LOG::ordered_commit之前,即事务相关的BINLOG Event还在BINLOG CACHE没有写入到BINLOG FILE前

通过gcs_module将所有的日志事件 发送给各个节点

4通过certify验证(paxos协议),通过投票机制,判断事务是否满足半数以上节点通过

5主库正常commit

6此时各个从库开始回放relaylog

1、事务操作生成的map event/query event/dml event等写入BINLOG CACHE中(内存) 2、将Write Set写入到Rpl_transaction_write_set_ctx中(内存)

在事务提交时,具体在MYSQL_BIN_LOG::prepare之后但是在MYSQL_BIN_LOG::ordered_commit之前,即事务相关的BINLOG Event还在BINLOG CACHE没有写入到BINLOG FILE前,将BINLOG CACHE中和Rpl_transaction_write_set_ctx中的数据进行处理并写入到transaction_msg中,由gcs_module负责发送transaction_msg到各个节点,等待各节点进行事务认证。

由于transaction_msg中包含BINLOG信息,并在事务认证期间发送给MGR各节点,因此无需等待主节点的BINLOG落盘后再发送给备用节点。

每个MGR群集中的节点上,都存在IO线程和SQL线程,IO线程会解析transaction_msg获取到BINLOG EVENT并保存到RELAY LOG中,再由SQL线程执行重放到辅助节点上。

从MGR复制原理上看,当主节点事务提交时,辅助节点上可能还未重放该事务对应的BINLOG,因此MGR仍属于异步复制。

图片来自网络

MGR部署过程

1生成一个集群的uuid

cat /proc/sys/kernel/random/uuid

2配置文件参数

代码语言:javascript
复制
report_host=10.0.0.51 暴露自己的ip
report_port=3306
efault_authentication_plugin=mysql_native_password
loose-group_replication_group_name="ca842376-1c50-42ac-bb57-a5adc7da7a12" 集群的uuid统一的,将来会生成统一的GTID
loose-group_replication_start_on_boot=OFF 节点启动之后是否会自动拉起MGR第一次配置不自动拉起来,搭建成功修改
loose-group_replication_local_address= "10.0.0.51:33061" 当前节点的地址加上一个端口集群内部的一个端口本地使用
loose-group_replication_group_seeds="10.0.0.51:33061,10.0.0.52:33062,10.0.0.53:33063" 整个集群的种子节点
loose-group_replication_bootstrap_group=OFF  配置是否自动引导组

loose

代表在没有MGR的时候不会影响数据库启动

3修改本地用户插件

"ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123';

4安装MGR插件

mysql -uroot -p123 -S /tmp/mysql.sock -e "INSTALL PLUGIN group_replication SONAME 'group_replication.so';"

5设置账号所有节点

代码语言:javascript
复制
SET SQL_LOG_BIN=0;
CREATE USER repl@'%' IDENTIFIED BY '123';
CREATE USER repl@'localhost' IDENTIFIED BY '123';
CREATE USER repl@'127.0.0.1' IDENTIFIED BY '123';
GRANT REPLICATION SLAVE,replication client ON *.* TO repl@'%';
grant replication slave,replication client on *.* to repl@'localhost' ;
grant replication slave,replication client on *.* to repl@'127.0.0.1' ;
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;

6启动MGR单主模式主库执行只能有一个引导节点其他节点加入就好

代码语言:javascript
复制
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='123' FOR CHANNEL 'group_replication_recovery';
SET GLOBAL group_replication_bootstrap_group=ON; 引导节点 
START GROUP_REPLICATION; 启动MGR
SET GLOBAL group_replication_bootstrap_group=OFF; 关掉引导节点
SELECT * FROM performance_schema.replication_group_members;

7加入其他节点

代码语言:javascript
复制
reset master; 清理日志
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='123' FOR CHANNEL
'group_replication_recovery';
START GROUP_REPLICATION;

其他节点加入是无感知的

代码语言:javascript
复制
reset master;
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='123' FOR CHANNEL
'group_replication_recovery';
START GROUP_REPLICATION;

SECONDARY 只读模式

PRIMARY 主模式

如果要重置节点可以使用以下命令

代码语言:javascript
复制
STOP GROUP_REPLICATION;
reset master;
SET SQL_LOG_BIN=1;
CHANGE MASTER TO MASTER_USER='repl', MASTER_PASSWORD='123' FOR CHANNEL
'group_replication_recovery';
start GROUP_REPLICATION;

单主模式切换多主模式MGR支持在线修改

代码语言:javascript
复制
读节点执行
 stop GROUP_REPLICATION;
set global group_replication_enforce_update_everywhere_checks=1;
set global group_replication_single_primary_mode=OFF;
检查
select @@group_replication_single_primary_mode,@@group_replication_enforce_update_everywhere_checks;
修改参数
 set global read_only=0;
 set global super_read_only=0;

group_replication_single_primary_mode=0 #这个参数很好理解,就是关闭单master模式 group_replication_enforce_update_everywhere_checks=1 #这个参数设置多主模式下各个节点 严格一致性检查

停止所有组复制修改参数

组复制停止了,数据库也是可以继续业务的

代码语言:javascript
复制
所有节点
stop group_replication;
set global group_replication_single_primary_mode=OFF;
set global group_replication_enforce_update_everywhere_checks=ON;

随便选取一个引导节点作为主节点启动

代码语言:javascript
复制
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;

其他节点执行

代码语言:javascript
复制
START GROUP_REPLICATION;

查看组信息

代码语言:javascript
复制
SELECT * FROM performance_schema.replication_group_members;

切换单主模式

代码语言:javascript
复制
所有节点执行
stop group_replication;
set global group_replication_enforce_update_everywhere_checks=OFF;
set global group_replication_single_primary_mode=ON;

选取一个节点作为主节点

代码语言:javascript
复制
SET GLOBAL group_replication_bootstrap_group=ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group=OFF;

其他节点执行

代码语言:javascript
复制
START GROUP_REPLICATION;

一般是单主模式,

如果主挂掉,会选择一台从作为主

在线切换主

代码语言:javascript
复制
SELECT group_replication_set_as_primary('3795b6c9-5efc-11ec-9214-000c29b96a5c');
查询到的MEMBER_ID  

在线函数修改全主模式

代码语言:javascript
复制
SELECT group_replication_switch_to_multi_primary_mode();
8.0支持

在线修改单主模式

代码语言:javascript
复制
SELECT group_replication_switch_to_single_primary_mode();
8.0支持

MGR日常运维

监控

代码语言:javascript
复制
SELECT * FROM performance_schema.replication_group_members;

如果主库宕机太久,需要把数据恢复到原来的主库

加新节点步骤

1备份恢复到新节点

2直接启动MGR 可以在线修改

MGR限制

仅支持innodb存储引擎

表中必须有主键或者null的唯一键

网络限制只支持ipv4网络

MGR忽略表锁和命名锁

多主模式下 对一个对象进行的并发ddl dml操作导致冲突部分成员节点无法检测到 最终可能导致数据不一致

不支持过滤复制

多主模式下 可能会导致死锁

MGR最多支持9个节点

不支持超大事务

MGR在8.0读写一致性的保障

代码语言:javascript
复制
group_replication_consistency EVENTUAL 默认值(最终一致性),开启事务(T2),事务执行前不会等待先序事务(T1)的回放完成,也不
会影响后序事务等待该事务回放完成。

BEFORE (本地强一致性)开启事务(T2),在开始前首先要等待先序事务(T1)的回放完成,确保此事务 将在最新的数据上执行。

AFTER(全局强一致性),开启事务(T1),只有等该事务回放完成。其他后序事务(T2)才开始执行,这样 所有后序事务都会读取包含其更改的数据库状态,而不管它们在哪个成员上执行。

BEFORE_AND_AFTER 开启事务(T2),需要等待前序事务的回放完成(T1);同时后序事务(T3)等待该 事务的回放完成;

BEFORE_ON_PRIMARY_FAILOVER,在发生切换时,连到新主的事务会被阻塞,等待先序提交的事务回放完 成;这样确保在故障切换时客户端都能读取到主服务器上的最新数据,保证了一致性

代码语言:javascript
复制
针对不同应用场景应当如何选择MGR读写一致性的相关方式,官方提供了几个参数以及与其相对应的应用场
景:
AFTER
适用场景1:写少读多的场景进行读写分离,担心读取到过期事务,可选择AFTER。
适用场景2:只读为主的集群,有RW的事务需要保证提交的事务能被其他后序事务读到最新读数据,可选择
AFTER。
BEFORE
适用场景1:应用大量写入数据,偶尔进行读取一致性数据,应当选择BEFORE。
适用场景2:有特定事务需要读写一致性,以便对敏感数据操作时,始终读取最新的数据;应当选择BEFORE。
BEFORE_AND_AFTER
适用场景:有一个读为主的集群,有RW的事务既要保证读到最新的数据,又要保证这个事务提交后,被其他后
序事务读到;在这种情况下可选择BEFORE_AND_AFTER。
下一篇
举报
领券