MySQL复制

实现方式:SQL级别复制、行级别复制

基于语句复制,修改数据的每个SQL语句都会被LOG.然后语句会在同一字符集和同一上下文情况下在从服务器重放。

基于语句复制只需要在主从服务器之间传输少量的数据。反之,需要记录大量的执行上下文信息,以便从服务器实现主服务器相同环境下重放更新操作。
然而,上下文有时候无法提供。随着新SQL功能频繁加入,基于语句复制变得很难维持相应的更新。

行复制更直接一点,不需要上下文信息,只关心哪行被更新和更新了什么。
基于良好代码结构,支持行复制的修改变得比较简单。上层调用底层例程与存储引擎交互来修改数据,写日志的方式修改不需要在底层添加其他额外的代码。

条件update语句会使行复制模式产生没必要的大量更新日志和导致主从服务器网络阻塞。
涉及到大量内部行物理格式修改,甚至需要处理模式上的修改。
这种方式常常由于I/O的高负载变得不可接受。

MySQL最初支持语句复制。到了5.0版本,开发者开始考虑这种复制形式的缺点,各种充满创意的技术来处理复制时需要不同各种的上下文信息。但是,存储过程依然成为实现的难点,存储过程存在各种条件和大量的分支。
复制开发者在5.1.5版本开始支持行复制。

5.1.8之后,MySQL支持三种复制模式:行复制、语句复制、混合复制。
由配置变量binlog_format决定。
行复制:复制是物理化的,但创建表、删除表、修改表结构等依然记录SQL
语句复制:与之前版本复制方式相同,每次更新都被记录。
混合复制:由master决定选择查询语句使用哪种方式复制。

>>双线程复制

单线程复制:3.23版本
读取binlog,并在从库重放。这种方式在主从之间没有太多延迟时工作得挺好。
但是,假设从库数据更新落后了主库一天,此时主库完全不可用了,那么这一天数据就丢失了。
4.0版本,重写双线程模式。
I/O线程从主服务器读取binlog数据,保存在中继日志。
SQL线程读取中继日志,并且在从服务器执行更新。

双线程方式最大限度降低了主从不同步并且主库不可用情况造成数据丢失的风险。
不难想象,从库复制延迟经常是由于SQL线程的慢导致的,而不是I/O线程。
从库经常设计于应付高峰值的读请求,高负载读压力下,从主库获取的update重放操作被推迟,造成了数据的不同步。
另一种情形,当存在需要耗时较久的update操作时,也可能让主从同步延迟加大。

双线程方式可以把拉取更新线程与重放线程区分开,即使重放线程被大耗时的查询阻塞,拉取更新线程依旧可以从主服务器拉取binlog,保证延迟不加大,降低了丢数据的风险。

多主复制
MySQL复制一开始并未设计成多主复制支持,一个从库只能连接一个主库。
存在简单的分支支持一个从库可以从多个主库获取更新,但没有解决冲突问题。

在此同时,存在一个常规配置迂回实现多主复制。
两个服务器设计为互为主备关系。
如果可以保证执行结果与执行顺序无关或者更新被序列化的话,互为主备关系可以保持数据的一致性。
互为主备配置可以作为热备切换使用。(hot failover)


通过SQL请求理解复制

1、开启服务器Binlog开关,设置唯一服务器ID,
2、SHOW MASTER STATUS
*************************** 1. row **************************
File: laforge-bin.011
Position: 566920603
Binlog_do_db:
Binlog_ignore_db:
3、GRANT REPLICATION SLAVE ON *.* TO 'rpl_user@slave-host' IDENTIFIED BY 'rpl_pass';
4、设置从服务器ID
5、CHANGE MASTER TO MASTER_HOST='master-host', MASTER_USER='rpl_user',
MASTER_PASSWORD='rpl_pass';
6、START SLAVE
   SHOW SLAVE STATUS

Binlog 格式
研究binlog格式可以了解到很多复制的内在细节。
主要分布在sql/log_event.h和sql/log_event.cc

binlog文件由4字节的魔数开始,定义在sql/log_event.h
#define BINLOG_MAGIC "\xfe\x62\x69\x6e"
该魔数用于快速常规检查binlog文件确保合法的文件格式。

魔数 + 通用头(4字节时间戳+1字节时间类型码+4字节serverID+4字节时间长度,包括通用头+4字节时间数据位移+2字节事件标识sql/log_event.h)

通用头所有的整型都是小端模式存储
通用头通过Log_event::write_header()(sql/log_event.cc)发送

事件类型码
Start: 早期版本记录了在binlog文件的开始位置,现在使用于格式描述事件
Query: 更新了master查询
Stop: 服务器关闭
Rotate: 日志翻转
Intvar: 包含下一个查询使用的自增字段的值
Load:3.23版本表示LOAD DATA INFILE操作,新版本用新的load事件替换
Slave:无用
CreateFile:告诉从服务器创建文件用来LOAD DATA INFILE
AppendBlock: 告诉从服务器追加block,用于load data infile
ExecLoad: 执行load data infile
delete file:删除由createfile创建的文件
newload:用新格式记录load data infile
rand: 记录主服务器由rand()函数产生的随机值
uservar: 记录更新操作中用户变量
format description: 日志文件第一个事件
XID:记录提交的事务ID,在两阶段提交协议中作为提交标志
Begin load query: 合并create file与append block到一个事件
execute load query:执行查询,load data infile使用前面创建的临时文件
Table Map:包含database或者table名字与对应的数据ID,用于行复制
Write Rows: 用于行复制,包含多行写入数据
Update Rows: 用于行复制,包含多行更新数据
Delete Rows: 用于行复制,包含多行删除数据

通用头之后跟着body,数据体因不同的事件类型有很大的不同。

binlog格式显示复制相关的很多隐藏的细节。
定义简单,真正的魔鬼是实现细节。日志格式复杂度、不同event类型对应的结构体。
如何处理Log翻转?
如何处理从服务器连接断开?
如何处理时间戳相关的查询?
如何处理load data infile?
如何处理随机值相关的更新复制?
如何避免多层主备关系中的循环复制?

Log_event::write_data_header()写入长度固定的事件类型头部信息,write_data_body()事件信息。

Log_event是每个事件的基础类,Write_data_header()与write_data_body()是虚拟方法。各自数据存储相关的事件类型实现了自己的写入方式。
如查询事件的实现: Query_log_event::write_data_header() 和 Query_log_event::write_data_body()

工具mysqlbinlog

如果想对复制做一些事情:
方式一:
修改读取中继日志代码,
sql/slave.cc文件中 handle_slave_sql() -> exec_relay_log_event()为SQL线程的处理代码
难度大,容易出错,不仅仅让复制出错,可能搞挂从服务器。
方式二:
使用mysqlbinlog源码,定义与client/mysqlbinlog.cc
在dump_remote_log_entires()函数里面添加自己感兴趣事件过滤器。
简单,不对服务器存在影响,但可能加大I/O负载。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值