232_mysql_复制补充

举报
alexsully 发表于 2021/11/17 13:50:08 2021/11/17
【摘要】 主从复制中使用不同的表定义 复制过程中的存储过程函数事件触发器的功能调用 复制中有limit的场景 LOAD DATA语句 Max_allowed_packet 复制临时表 复制中事务不一致

1 主从复制中使用不同的表定义

        主从之间具有不同字段数的表的复制的限制条件    

  •  源表和目标表都存在的字段, 定义的顺序必须相同
  •  源表和目标表有差异的字段, 差异字段的定义顺序必须在相同的字段后面
  •  源表和目标表有差异字段 需要设置默认值

A(a1 a2 a3)  B(a1 a2)

1.1 源表字段多于目标表字段数

Binlog_format=statement 如果SQL语句中只涉及主从相同的字段正常复制insert into b (1,2),如果SQL中字段在主从上存在差异会报错 insert into b(1,2,3)会报错

Binlog_formt=row 语句正常复制, 多出的字段 mysql自动忽略 @1 @2代表字段顺序 # 前提 binlog_row_image=full

binlog_row_image=full/minimal/noblob

(1)前镜像(before image):记录修改前的内容

(2)后镜像(after image):记录修改后的内容

full会记录所有列
noblob会记录除blob和text外的所有列
minimal只会记录需要的列

1、对于insert来说,只有后镜像没有前镜像;对于update来说,有前镜像和后镜像;对于delete来说,只有前镜像没有后镜像。

2、对于binlog_row_image=full,在有主键、有唯一索引、只有普通二级索引等场景下,insert记录所有列的后镜像,update记录所有列的前镜像和后镜像,delete记录所有列的前镜像。

3、对于binlog_row_image=minimal,在不同场景下,记录的信息是不一样的:

(1)在有主键的场景下,insert记录所有指定列的后镜像,update记录主键列的前镜像和指定更新列的后镜像,delete记录主键列的前镜像。

(2)在有唯一索引的场景下,insert记录所有指定列的后镜像,update记录唯一索引列的前镜像和指定更新列的后镜像,delete记录唯一索引列的前镜像。

(3)在只有普通二级索引的场景下,insert记录所有指定列的后镜像,update记录所有列的前镜像和指定更新列的后镜像,delete记录所有列的前镜像。

1.2 目标表字段多于源表字段数B(a1 a2)  A(a1 a2 a3)

Binlog_format=statement 如果SQL语句中只涉及主从相同的字段正常复制insert into b (1,2),如果SQL中字段在主从上存在差异会报错 insert into b(1,2,3)会报错

Binlog_formt=row 语句正常复制, 多出的字段内容会被赋予默认值

1.3不同类型字段的复制

 某些字段属性类型定义本身就不相同

  • 1.3.1 属性提升 char(10)  à char(25)  TINYINT à BIGINT, DATETIME à VARCHAR(50)
  • 1.3.2 有损转换&无损转换 目标字段无法完整写入要插入的源数据值- 转换方式 – slave_type_converions

有效值

行为

ALL_LOSSY

允许有损转换,不允许无损转换, 仅允许有损转换 或不需要转换的情况,如遇到无损转换会报错中止

例 不允许 TINYINT 字段转换为INT字段

ALL_NON_LOSSY

此模式不需要截断或者特殊处理,不允许数据截断,如果从库类型大于主库类型,是可以复制的,反过了,就不行了,从库报复制错误,复制终止

ALL_LOSSY,ALL_NON_LOSSY

所有允许的转换都会执行,而不管是不是数据丢失(有损和无损转换)

All_SIGNED

将提升后的整数类型视为有符号值(默认)

ALL_UNSIGNED

将提升后的整数类型视为无符号值

All_SIGNED ALL_UNSIGNED

如有可能将提升后的整数类型视为有符号值,否则视为无符号数

[empty]

默认值, 空串. 意味主从字段类型必须一致, 否则报错

2 复制过程中的存储过程函数事件触发器的功能调用

RC隔离级别下

 

statement复制场景

CREATE PROCEDURE, FUNCTION, EVENT, TRIGGER

DROP PROCEDURE FUNCTION, EVENT, TRIGGER

语句以Query_log_event事件记录

调存储过程,触发器报错 ERROR1665

如果是event 即使是statement模式下, 所产生的影响也是以row格式记录的

ROW&Mixed复制场景

CREATE PROCEDURE, FUNCTION, EVENT, TRIGGER

语句以Query_log_event事件记录 而且记录 “DEFINER=root@localhost”

调用存储过程所产生的操作会按row格式进行记录 SET @1=1, @2=2

DROP PROCEDURE FUNCTION, EVENT, TRIGGER

query_log_event形式记录

备注

Create event & alter event 语句,事件在从库上的状态都被设置为 slaveside_disabled,表示即使配置了事件也不会被调用,该状态可以在information_schema.events表的status查到

3 复制中有limit的场景

Update xxx set aa=’xxx’ limit 3;

Insert into xxx select aa, bb from a_table limit 3

Read-uncommited和read-commited模式下

Binlog_format为statement时, DML无法执行,报错1665

REPETABLE-READ 和 SERIALIZABLE 模式下

 

Statement

DML limit mysql会有告警提示,无法保证查询结果正确性 但正常执行

Update ….limit ; delete ….limit; insert into … select …limit  (GTID & QUERY_LOG_EVENT)

binlog中记录原始语句

Row & Mixed

Update ….limit ; delete ….limit; insert into … select …limit  (GTID & QUERY_LOG_EVENT)

Binlog中使用row模式记录 where @1=xx @2=xxx SET @1=xx @2=xx ; where @1=xxx

基于row 模式记录,能保证LIMIT 子句复制的正确性

4 LOAD DATA语句

数据库系统变量设置

Gtid_mode = on
Enfore-gtid-consistency = true
Binlog_row_image = full
Secure_file_priv = “/tmp”
Sql_mode = ‘only_full_group_by, strict_trans_tables, no_zero_in_date, no_zero_date, error_for_division_by_zero, no_auto_create_user, no_engine_substitution’

导入导出SQL

Select  * from t_name into outfile ‘/tmp/aaa.txt’

Load data infile ‘/tmp/aaa.txt’ into table table_name

 

主库执行完成后,将 /server_tmp/t.csv 文件的内容直接写到 binlog 文件中。

往 binlog 文件中写入语句 load data local infile ‘/tmp/SQL_LOAD_MB-1-0’ INTO TABLE `db2`.`t`,把这个 binlog 日志传到备库。

备库的 apply 线程在执行这个事务日志时:

a.先将 binlog 中 t.csv 文件的内容读出来,写入到本地临时目录 /tmp/SQL_LOAD_MB-1-0 中;

b.再执行 load data 语句,往备库的 db2.t 表中插入跟主库相同的数据

Statement

# Begin_load_query: file_id: 1 block_len:19

# timestamp server id xxx end_log_pos xxx Execute_load_query  thread_id =2 exec_time=0 error_code = 0

 Load DATA LOCAL INFILE ‘/tmp/SQL_LOAD_MB-1-0’ INTO TABLE ‘t_name’ FIELDS TERMINATED BY ‘/t’ ENCLOSED BY ‘’ ESCAPED BY ‘\\’ LINES TERMINATED BY ‘\n’ (`字段``字段2`)

1 load data语句在binlog中的关键字是begin_load_query executed_load_query

2 从库解析binlog begin_load_query_log_event事件时, tmpdir查询sql_load_MB前缀的文件

3 当发现execute_load_query类型事件时, 会将中继日志relay_log 数据提取出来存到临时文件夹中

4 load data数据实际存在binlog中,但mysqlbinlog statement格式 并未展示

5 mysqlbinlog 解析statement格式记录的load data时,会在tmpdir中创建一个本地临时文件, sql_load_MB_1_0文件,这个文件就是load data 导入的数据

row

### insert into xxx set @1=xx @2=xxx

就是row格式记录binlog 日志

4 Max_allowed_packet

控制一个数据包或由mysql_stmt_send_long_data()函数发送的所有参数的最大尺寸,默认是4MB

  •  包消息缓冲区初始化 net_buffer_length大小,需要时可以自动增加到 max_allowed_packet 定义的值
  •   使用大BLOB列或长字符串,则必须增加此值,最大1G 设置1024的倍数

Slave_max_allowed_packet

    设置从库SQL线程和I/O线程的最大数据包尺寸,方便基于row复制中,减少因为大事物而产生的数据包大小超过 max_allowed_packet设置的值

 Net_buffer_length

    每个客户端会话线程相关的连续缓冲区结果集缓冲区的初始大小,这两个缓冲区的初始值都是 net_buffer_length, 后面根据需要可以动态增加到 max_allowed_packet

5 复制临时表

Create temporary table test(xxx) 
Insert into test 
Drop table test 

临时表是会话独享的,会话断开,则自动删除临时表,并在binlog中写入 drop temporary table

Statement

在主库上的所有操作都会被记录在binlog中,statement格式

Drop table 会被改成 drop temporary table 语句 保证主库中不在需要的表在从库上也能被删除

Row

Drop temporary table if exists tests /*generated by server*/

除了drop操作外 都不会被记录在binlog

# 如果使用临时表,如何安全关闭slave

等到变量 slave_open_temp_tables = 0 stop  slave sql_thread  

6 复制中事务不一致

某些情况主从库数据不一致是由于主从事务不一致导致的

场景

事务半提交

(half-applied-transaction)

事务更新了不支持事务特性的表,从库重放主库事务时无法保证事务的原子性,导致事务部分提交成功,部分失败

间隙Gap

出现在多线程复制会出现,一组队列的事务在从库同时回放,队列中某个事务尚未完成,后面的事务已经完成

避免gap

Slave_parallel_type = logical_clock

Log_bin = xxx; log_slave_update = on

Slave_preserve_commit_order = 1 (该参数无法保证非事务表的DML操作顺序)

低水位情况

Master log position lag

只会在多线程复制时出现,即使没有出现gap, 仍可能出现 exec_master_log_pos = N 但是实际事务已经应用到N后面的情况.

单线程情况N 代表之前事务已经应用完成

多线程情况 每个worker都有自己应用事务的位置, 通过一定机制来计算所有worker线程已经应用完事务的位置(完成checkpoint事务的位置)在计算出exec_master_log_pos

原因

在从库复制线程运行过程中,可能存在间隙和事务半提交的情况

Mysqld 进程关闭,可能会中断应用中的事务,留下间隙或出现事务半提交的情况

Kill 复制线程(单线程kill 复制SQL线程,多线程复制时 kill 协调器线程) 会中断应用中的事务,留下间隙或出现事务半提交的情况

Applier线程(worker线程)出错时,可能会出现间隙,

  如果发生在混合事务中,则该事务处于半提交状态, 如果多线程从库 未发生错误的worker会继续执行事务,因此发生错误时候,停止所有worker线程 会需要一些时间

多线程复制 stop slave后 从库会等待填充GAP,然后执行 exec_master_log_pos值,确保不会留下gap或者低水位情况

如果relaylog最后一个事务只接收了一半,而且多线程从库的协调器线程已开始将事务调度给worker线程,则执行stop slave语句后,等待60S 后还未收到剩余事务的事件,则会发生超时(stop slave后只有60s 给事务提交或者回滚) 超时后,协调器线程会放弃并终止正在执行的事务,如果是混合事务,会出现半提交事务

单线程复制中,stop slave

如果正在执行的事务仅更新新事务表,则该事务立即被回滚

如果执行混合事务,则最多等待60s以完成事务,超过60s 未完成则会被终止,可能会造成半提交事务

事务不一致的结果和影响

  • Reset slave 会清理relay_log并重置复制的位置,如果主从复制存在gap , 执行reset slave 语句后,关于gap情况的信息丢失,就无法发现间隙问题
  • 使用GTID 复制,mysql会自动判断,如果GTID 的事务已经被应用则跳过,利用GTID 特性可以恢复主从复制

主从数据不一致,从库中缺少了某些数据

Show slave status 中字段 exec_master_log_pos 是一个低水位

如果 applier正在运行,执行 change master to 语句将失败并显示错误

ERROR 3021(HY000): This operation cannot be performed with a running slave io threadrun stop slave IO_THREAD FOR CHANNEL first

如果使用 –relay-log-recover 选项启动mysqld,则不会对该通道进行恢复,因为relay_log直接被清理,不会使用relay_log进行恢复,并打印告警信息

如果mysqldump –dump-slave一起使用,则不会记录gap, 因为备份文件产生的change master to 语句中的relay_log_pos 的位置是通过 show slave status语句中获取的 exec_master_log_pos低水位位置值。 在另一台server上应用备份文件&启动复制线程后,可能出现已经被应用过的事务通过复制被重复应用,如果启用GTID ,则可以自动跳过重复事务,如未启用,则不建议使用 –dump-slave选项

关于主库位置点信心会放在 mysql.slave_worker_info表中, start slave 语句执行时 会从该表中查询信息,以应用正确的事务。

Start slave until sql after MTS GAPS  语句也能通过该表的信息填补复制间隙中缺失的事务

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。