MySQL Binlog应用场景总结

本文深入介绍Mysql Binlog的应用场景,以及如何与MQ、elasticsearch、redis等组件的保持数据最终一致。

基于binlog的主从复制

Mysql 5.0以后,支持通过binary log(二进制日志)以支持主从复制。复制允许将来自一个MySQL数据库服务器(master) 的数据复制到一个或多个其他MySQL数据库服务器(slave),以实现灾难恢复、水平扩展、统计分析、远程数据分发等功能。(注意:这里讲解主从复制主要是为了理解binlog的工作流程。)

二进制日志中存储的内容称之为事件,每一个数据库更新操作(Insert、Update、Delete,不包括Select)等都对应一个事件。

下面以mysql主从复制为例,讲解一个从库是如何从主库拉取binlog,并回放其中的event的完整流程。mysql主从复制的流程如下图所示:
在这里插入图片描述
主要分为3个步骤:

  • 第一步:master在每次准备提交事务完成数据更新前,将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log event,简称event)

  • 第二步:slave启动一个I/O线程来读取主库上binary log中的事件,并记录到slave自己的中继日志(relay log)中。

  • 第三步:slave还会起动一个SQL线程,该线程从relay log中读取事件并在备库执行,从而实现备库数据的更新。

binlog的应用场景

binlog本身就像一个螺丝刀,它能发挥什么样的作用,完全取决你怎么使用。就像你可以使用螺丝刀来修电器,也可以用其来固定家具。

读写分离

最典型的场景就是通过Mysql主从之间通过binlog复制来实现横向扩展,来实现读写分离。如下图所示:

在这里插入图片描述
在这种场景下:

  • 有一个主库Master,所有的更新操作都在master上进行

  • 同时会有多个Slave,每个Slave都连接到Master上,获取binlog在本地回放,实现数据复制。

  • 在应用层面,需要对执行的sql进行判断。所有的更新操作都通过Master(Insert、Update、Delete等),而查询操作(Select等)都在Slave上进行。由于存在多个slave,所以我们可以在slave之间做负载均衡。通常业务都会借助一些数据库中间件,如tddl、sharding-jdbc等来完成读写分离功能。

数据恢复

一些同学可能有误删除数据库记录的经历,或者因为误操作导致数据库存在大量脏数据的情况。例如笔者,曾经因为误操作污染了业务方几十万数据记录。

如何将脏数据恢复成原来的样子?如果恢复已经被删除的记录?

这些都可以通过反解binlog来完成,笔者也是通过这个手段,来恢复业务方的记录。

数据最终一致性

在实际开发中,我们经常会遇到一些需求,在数据库操作成功后,需要进行一些其他操作,如:发送一条消息到MQ中、更新缓存或者更新搜索引擎中的索引等。

如何保证数据库操作与这些行为的一致性,就成为一个难题。以数据库与redis缓存的一致性为例:操作数据库成功了,可能会更新redis失败;反之亦然。很难保证二者的完全一致。

遇到这种看似无解的问题,最好的办法是换一种思路去解决它:不要同时去更新数据库和其他组件,只是简单的更新数据库即可。

如果数据库操作成功,必然会产生binlog。之后,我们通过一个组件,来模拟的mysql的slave,拉取并解析binlog中的信息。通过解析binlog的信息,去异步的更新缓存、索引或者发送MQ消息,保证数据库与其他组件中数据的最终一致。

在这里,我们将模拟slave的组件,统一称之为binlog同步组件。你并不需要自己编写这样的一个组件,已经有很多开源的实现,例如linkedin的databus,阿里巴巴的canal,美团点评的puma等。

当我们通过binlog同步组件完成数据一致性时,此时架构可能如下图所示:

在这里插入图片描述

增量索引

通常索引分为全量索引和增量索引。对于增量索引的部分,可以通过监听binlog变化,根据binlog中包含的信息,转换成es语法,进行实时索引更新。当然,你可能并没有使用es,而是solr,这里只是以es举例。

可靠消息

可靠消息是指的是:保证本地事务与发送消息到MQ行为的一致性。一些业务使用本地事务表或者独立消息服务,来保证二者的最终一致。Apache RocketMQ在4.3版本开源了事务消息,也是用于完成此功能。事实上,这两种方案,都有一定侵入性,对业务不透明。通过订阅binlog来发送可靠消息,则是一种解耦、无侵入的方案。

缓存一致性

业务经常遇到的一个问题是,如何保证数据库中记录和缓存中数据的一致性。不妨换一种思路,只更新数据库,数据库更新成功后,通过拉取binlog来异步的更新缓存(通常是删除,让业务回源到数据库)。如果数据库更新失败,没有对应binlog,那么也不会去更新缓存,从而实现最终一致性。

可以看到,binlog是一把利器,可以保证数据库与与其他任何组件(es、mq、redis等)的最终一致。这是一种优雅的、通用的、无业务入侵的、彻底的解决方案。我们没有必要再单独的研究某一种其他组件如何与数据库保持最终一致,可以通过binlog来实现统一的解决方案。

在实际开发中,你可以简单的像上图那样,每个应用场景都模拟一个slave,各自连接到Mysql上去拉取binlog,master会给每个连接上来的slave一份完整的binlog拷贝,业务拿到各自的binlog之后进行消费,彼此之间互不影响。但是这样,有一些弊端,多个slave会给master带来一些额外管理上的开销,网卡流量也将翻倍的增长。

我们可以进行一些优化,之所以不同场景模拟多个slave来连接master获取同一份binlog,本质上要满足的是:一份binlog数据,同时提供给多个不同业务场景使用,彼此之间互不影响。

显然,消息中间件是一个很好的解决方案。现在很多主流的消息中间件,都支持consumer group的概念,如kafka、rocketmq等。同一个topic中的数据,可以由多个不同consumer group来消费,且不同的consumer group之间是相互隔离的,例如:当前消费到的位置(offset)。

因此,我们完全可以将binlog,统一都发送到MQ中,不同的应用场景使用不同的consumer group来消费,彼此之间互不影响。此时架构如下图所示:

在这里插入图片描述
通过这样方式,我们巧妙的达到了一份数据多个应用场景来使用。一般,一个Mysql实例中可能会创建多个库(Database),通常我们会将一个库的binlog放到一个对应的MQ中的Topic中。

当将binlog发送到MQ中后,我们就可以利用MQ的一些高级特性了。例如binlog发送到MQ过快,消费方来不及消费,可以利用MQ的消息堆积能力进行流量削峰。还可以利用MQ的消息回溯功能,例如一个业务需要消费历史的binlog,此时MQ中如果还有保存,那么就可以直接进行回溯。

当然,有一些binlog同步组件可能实现了类似于MQ的功能,此时你就无序再单独的使用MQ。

异地多活

一个更大的应用场景,异地多活场景下,跨数据中心之间的数据同步。这种场景的下,多个数据中心都需要写入数据,并且往对方同步。以下是一个简化的示意图:
在这里插入图片描述
这里有一些特殊的问题需要处理。典型的包括:

  • 数据冲突:双方同时插入了一个相同主键的值,那么往对方同步时,就会出现主键冲突的错误。

  • 数据回环:一个库A中插入的数据,通过binlog同步到另外一个库B中,依然会产生binlog。此时库B的数据再次同步回库A,如此反复,就形成了一个死循环。

如何解决数据冲突、数据回环,就变成了binlog同步组件要解决的问题。同样,业界也有了成熟的实现,比较知名的有阿里开源的otter,以及摩拜(已经属于美团)的DRC等。

总结

本篇文章从binlog主从复制入手,具体介绍了binlog常见的几种应用场景:读写分离、数据恢复、保证数据最终一致性以及异地多活,就像文中讲的一样,binlog本身就像一个螺丝刀,它能发挥什么样的作用,取决你如何使用,这就要求我们能够根据实际问题灵活地匹配解决手段。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值