我们今天简单了解一下主备库的基本原理。理解了背后设计原理,
Mysql主备基本原理
下图就是基本的主备切换流程
在状态1中,客户端的读写都直接访问节点A,而节点B是A的备库,只有将A的更新都同步过来,到本地执行,这样可以保证节点B和A的数据是相同的
当需要切换的时候,就切换成2,这个时候客户端读写访问的都是节点B,而节点A是B的备库。
在状态1中,虽然节点B没有被直接访问,但是我依然建议你把节点B,设置只读模式,这样组有以下几个好处
但是我们发现如果把备库设置成只读,那么主备如何同步更新呢,这个问题,是因为只读(readonly)设置对超级权限用户是无效的,而同步更新的线程,就拥有超级权限
接下来,我们看看同步的内部流程是什么样,如下图
可以看到在主库上执行的更新请求后,执行内部事务的更新逻辑,同时写binlog.备库B跟主库A有一个长连接,主库A内部有一个线程,专门用于服务备库B的这个长连接,一个事务日志同步的完整过程如下
分析完这个长连接的逻辑,我们现在要知道binlog里面到底是什么内容,为什么备库拿过来可以直接执行
binlog的三种格式对比
我之前介绍过binglog有两种格式,一种是statement,另外一种是row,其实我们有第三种格式mixed,其实就是前两种格式的混合
举例说明,我们建议一个表,初始化几行数据如下
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`t_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `a` (`a`),
KEY `t_modified`(`t_modified`)
) ENGINE=InnoDB;
insert into t values(1,1,'2018-11-13');
insert into t values(2,2,'2018-11-12');
insert into t values(3,3,'2018-11-11');
insert into t values(4,4,'2018-11-10');
insert into t values(5,5,'2018-11-09');
当我们执行一条delete语句,binlog是怎么记录的,如下面sql
mysql> delete from t /*comment*/ where a>=4 and t_modified<='2018-11-10' limit 1;
这里要注意的是上面的注释,如果我们用mysql客户端来做这个实验的话,要记得添加-c参数,否则客户端会自动去掉注释
当binlog_format=statemetn时,binlog里面记录的就是sql语句的原文,可以使用下面语句查看
mysql> show binlog events in 'master.000001';
命令查看binlog中的内容
我们在介绍一下上图的含义
我们在看看delete命令的执行效果
可以看到,运行这条delete产生了一个warning,原因是当前binlog设置的是statement格式,并且语句中有limit,所以这个命令可能是unsafe的
为什么是unsafe呢,这是因为delete的limit,很可能就出现主备数据不一致的情况,
由于statement格式下,记录到binlog里面的语句原文,因此可能出现主库上执行这条语句用的是索引a,而在备库执行这条sql语句的时候,却使用索引t_modified,因此mysql认为这是有风险的。
那么如果记录的格式是row.是不是没有这个问题呢
我们看到row格式里面没有sql原文,而替换成两个event,table_map和delete_rows。
实际上我们一般要用mysqlbinlog工具解析binlog日志,使用下面语句,binlog是从890这个位置开始的,索引可以用start-position参数指定
mysqlbinlog -vv data/master.000001 --start-position=8900;
上图我们可以看到下面这几个信息
我们可以看到binlog_format=row的时候,binlog里面记录了真实删除行的主键id,这样在传到备库的时候,肯定会删除id=4这一行,不会有主备删除不通行的问题.
为什么会有mixed格式的binlog
那是因为前两种格式有各自的缺点,而mixed去了折中的方案
mixed就是利用statement格式的优点同时避免了数据不一致的风险
但是越来越多的场景要求把mysql的binlog格式设置很row,这样做有很多好处,比如恢复数据
比如,即使我们执行delete语句,row格式的binlog语句与binlog里面记录所有的字段信息,这些信息保存起来,所以如果我们误删除了数据,可以直接把binlog中记录的delete转成inset,把别删除的数据插入回去就可以恢复了,其他操作同样的原理如insert,update
虽然说mixed现在场景用的不多,但是我们举个例子如下sql,说明一个问题
mysql> insert into t values(10,10, now());
如果binlog格式是mixed,上面的语句在binlog格式是row还是statement格式呢
我们发现是statement格式,但是我们还会有疑问,如果这条语句在备库上执行,会导致数据不一致,此时我们使用mysqlbinlog工具看看分析一下
可以看到binlog记录event的时候,多记录了一条命令set timestamp=1546103491,他用来预定接下来now函数的返回值,因此不管备库上什么时候执行,数据都是一样的。
我们可以发现binlog的日志有可能是依赖上下文的,因此我们不要直接把binlog的部分日志在数据库上执行,可能会导致数据不一致。
循环复制问题
文章的开头,我们看到主备的结构,是一种M-S结构,但是实际上使用的是双M结构,如下图
双M的和M-S的区别就是多了一条线,节点A和节点B总是互为主备关系,这个时候主备切换不用修改主备关系,但是双M结构还有一个问题需要解决
业务上节点A更新了一条语句,然后把生成的binlog发给节点B,节点B执行完这条更新语句后也会生成binlog,那么,如果节点A同时也是节点B的备库,此时节点B新生成的binlog,会把节点B的binlog日志拿到到节点A上执行,这不就导致死循环了吗
其实mysql是按照下面逻辑解决循环复制的问题的
按照上面逻辑就可以避免循环复制binlog的问题
如果对您有一丝丝帮助,麻烦点个关注,也欢迎转发,谢谢