今天主要分享饿了么底层数据的实现,给大家介绍一下整个设计和Dohuo的实现过程,我们是如何处理远程数据同步的,这个数据同步组件在我们公司叫做DRC。饿了么异地多活的背景在讲DRC或者数据复制之前,我们先来回顾一下异地多活的背景。去年我们做多活动研究的时候,整个公司的业务服务全部部署在北京机房。大概有4000台服务器,灾备机在云端,都是虚拟机。大约有3000座塔。那时候我们的业务高峰订单量接近千万,但是北京机房(IDC)基本上已经扩容不了了,也就是说我们没有多余的机架,也没有办法增加新的服务器,所以我们不得不重建一个新的机房。因此,我们在上海新建了一个机房,并于今年4月份投入使用。所以,上海机房建成后,可以在生产环境中将异地的多活动项目置灰。异地多活底层数据同步的实现这是对异地多活底层数据同步实现的简单概述。大家可以看到,我们有两个机房,一个在北京,一个在上海。这个时候我们预计北方所有的用户请求和用户流量都会进入北京机房,南方所有的用户请求和用户流量都会进入上海机房。难点在于用户可能今天在北方,明天在南方,因为他出差了,而我们分南北分片的时候,有一些区域是在边界上的,这样会加剧同样的情况出现用户流量在南北机房之间来回漂移。还有一种情况,当我们其中一个机房出现故障,比如核心交换机坏了,整个机房的服务不可用,我们希望这个机房的所有流量可以快速切换到另一个机房数据中心,从而提高饿了么整个服务的高可用性。以上所有因素都需要连接底层数据库中的数据。今天要分享的DRC项目是饿了么的异地MySQL数据库双向复制组件服务,也就是上图中红框标注的部分。多站点多活数据对底层数据的需求我们在前期调研DRC实现的时候,主要总结了三点,在后续的设计实现中,基本围绕这三点解决了问题:低,我们当时给自己定下的目标是二级。我们希望在北京机房或者上海机房写入的数据需要在1秒内同步到上海或者北京机房。整个延迟应小于1秒。我们需要保证数据的一致性。数据不会丢失或弄错。如果出现数据不一致,可能会给上层的业务服务乃至产品带来灾难性的问题。保证整个复制组件具有高吞吐量的处理能力,意味着它可以面对各种复杂的环境,比如业务流程中的数据批量操作、数据维护、数据字典变更等。这些都会在瞬间产生大量变化的数据。DRC需要面对这种情况,需要有高吞吐量来处理这些情况。在数据低延迟和一致性之间,我们认为主要是从并发数据复制的策略上解决的。安全、可靠、高效的并发策略可以保证数据的低延迟复制。当需要复制大量数据时,DRC并发处理可以在短时间内快速解决。数据一致性,用户的流量可能路由到两个机房中的任意一个,也就是说两个机房可能同时更改同一条记录,所以DRC需要处理数据冲突,最后维护数据一致性。也就是说,数据不能有错。如果有冲突,DRC本身不能自动处理冲突,我们也提供数据冲突修正平台,需要业务方共同制定数据修正规则。刚刚引入了高吞吐量。一般情况下,用户流量稳定,DRC可以应付。数据可在1秒内快速复制到对端机房。当DBA对数据库数据进行数据归档、大表DDL等操作时,这些操作会在短时间内快速产生大量变化的数据,需要我们进行复制。这些数据可能会远远超过DRC的最大处理能力,最终会导致DRCReplication延迟。因此,DRC需要与现有的DBA系统交互,提供灵活的数据归档机制。例如,当DRC有较大的复制延迟时,归档作业被终止,并且控制每轮归档的数据大小。比如DRC识别大表的DDL产生的binlog事件,过滤掉这些事件,防止这些数据传输到其他机房,占用机房的带宽资源。以上就是我们在实现异地多活数据层双向复制时,对DRC项目提出的主要需求。数据集群规模(多活改造前)这是我们北京数据中心多活前的数据规模。当时这个数据中心有250多套MySQL集群,1000多个MySQL实例,400多个Redis集群。DRC服务的目标对象就是这250套MySQL集群,因为未来在建的第二个数据中心会有对应的250套MySQL集群,我们需要把这两个集群的数据对等业务打通在电脑室。LivemoreMySQL的使用分类我们根据业务的使用,将其分为了各种DB服务类型。为什么要总结这个?因为有些类型我们不需要复制,所以我们需要识别它们。首先,第一个多活DB,我们认为它的服务需要是多活的。比如支付、下单、下单,一个机房宕机,用户流量切换到另一个新的机房。这些业务服务在新机房工作。我们称这些多活服务依赖于多活DB。我们优先将DB改造为业务的多活DB。DRC对多活DB进行双向数据复制,保证数据一致性。多活DB的优点刚才已经说了。如果机房出现故障,核心交换机出现故障,整个机房崩溃,运维人员无法登录机房,那么我们可以将用户流量切换到云端其他机房。一些业务对数据有很强的一致性要求。我稍后再说。事实上,DRC也没有办法满足数据的强一致性要求。存在数据冲突,需要引入数据修正措施。如果业务对数据有强一致性要求,比如用户注册,要求用户登录名全局唯一(DB字段可以添加唯一性约束),两个机房可能会收到同一个用户登录的注册请求同时取名。在这种情况下,DRC无法自行解决这个冲突,业务方也无法接受这个结果。我们将把这个数据库包含在GlobalDB中。它有什么特点?它的特点是单机房可写,多个机房可读,因为如果要保证数据的强一致性,就必须让所有机房的请求处理结果都写到一个固定的机房。机房停机后该DB上层业务服务损坏。例如机房宕机,用户注册功能可能无法使用。最后一种是非多活DB,比较少见,主要集中在一些后端管理平台。这种项目本身基本不是多活的,所以我们不搬这种DB,或者采用原生的主备方式。DRC整体架构设计这是DRC复制组件的整体架构设计。我们有一个叫做Replicator的组件,它从MySQL集群的Master中提取binlog日志记录,解析binlog记录并转换成我们自定义的数据,存储在一个超大的eventbuffer中,支持TB级别的容量。在目标机房,我们会部署一个Applier服务,它与Replicator服务开启一个TCP长连接,Replicator会源源不断的向Applier推送数据,Applier最终会通过JDBC。我们将使用一个Console控制节点来进行配置管理、部署管理和各种组件的HA协调。DRCReplicatorServer这是对DRCReplicatorServer组件的一个比较详细的结构描述。它主要包括一个MetaDB模块。MetaDB主要用于解决历史Binlog分析问题。当我们成功解析Binlog记录后,我们将其转换成我们自己定义的数据结构。与原来的结构相比,这个结构的Size更小。MySQL的binlogevent的定义其实在Size上是非常极端的。.但是我们可以结合自己的特点,我们会过滤掉所有不需要的事件(比如table_map_event),忽略所有可以忽略不计的数据。我们比较的结果是,需要复制的事件数据只有原始数据大小的70%。当DRCApplierServer复制和写入目标MySQL集群时,DRCApplierServer负责。它将与Replicator建立长连接,Replicator将数据推送到Applier。Applier拿到数据后还原事务,最后通过JDBC将事务重写到目标DB。在编写过程中,我们应用了并发策略。并发策略在提供复制吞吐量和减少复制延迟方面起着决定性的作用。幂等性也很重要。有很多运维操作和一些failover回滚操作,会导致数据被重复处理在某些情况下,幂等操作保证数据的重复处理不会出现问题。DRC防止循环复制大家在做复制的时候肯定会遇到解决循环复制的问题。我们在考虑这个问题的时候,查了很多资料,问了很多做过类似项目的前辈。当时,我们认为有两种方法。第一种方法一开始就被否决了,因为我们不熟悉原来的MySQL内核代码,来不及了,虽然我们知道通过MySQL内核解决循环复制是最好的和最优的。DRC本身有两种方法可以解决这个问题:一种方法是在向目标DB应用数据时关闭binlog。另一种方式是在写入目标DB时,在事务中添加额外的checkpoint表数据,记录源DB的server_id。后来我们对比了一下。第一种方式比较简单易实现,但是由于不生成Binlog记录,不支持级联复制,同时也给后续运维带来麻烦。所以我们最终选择了第二种方法,通过在事务中hack一个checkpoint数据来识别事务产生的原始服务器,在复制事务到目标DB时,DRC在解析MySQLbinlog记录时可以正确识别数据的真实来源。DRC数据一致性保证在研发设计之初,数据一致性保证是我们比较头疼的问题。并不是我们一开始就把所有的点都想好了,而是我们在做的过程中遇到了问题,我们一步一步去解决。回想起来,我们大概从三个方面来保证数据的一致性:第一,因为数据库是多活的。我们要从数据中心层面尽可能的降低数据冲突的概率,避免冲突。如何避免它们?是合理的流量分割。您可以使用用户的维度和区域的维度。流量被分割。刚才说了,北方用户的所有数据都在北京机房,这些北方用户的所有操作数据,比如下单,支付,都是在北方机房产生的,所以用户的数据变化操作在同机房是绝对安全的。我们最怕的就是同一个数据在两个机房同时或者相近的时间被修改。我们怕的就是这个问题,因为这种情况会造成数据冲突。所以我们通过合理的流量切分,保证大部分时间数据不会冲突。其次,我们认为您需要确保数据的一致性。首先,您必须确保数据不丢失。在可能发生数据丢失的情况下,我们会做一个相对安全的策略,就是回滚数据复制的时间位置,即使重复处理。数据,并避免数据丢失的可能性。但是这个时候会带来数据重复处理的问题,所以数据的幂等操作就显得尤为重要。这些都是我们避免数据冲突的方法。冲突其实是不可避免的。冲突发生后,我们如何解决呢?最后一种方式是在数据库表中隐式添加一个时间字段(数据最后一次更新的时间),该字段对业务透明,主要用于辅助DRC复制。一旦数据发生冲突,DRC复制组件就可以利用这个时间来判断两个机房或者三个机房的哪些数据是最后更新的。最新优先原则,谁的修改时间最晚,谁就将其作为最新的更新时间。允许。DRC数据复制低延迟保证刚才我们讲了数据的一致性,还有一个很重要的一点就是数据复制的低延迟保证。我们目前的延迟,包括用户高峰时段,不到1秒。只有在凌晨之后,归档、批量数据处理、DDL变更等各种操作才会导致DRC延迟出现毛刺和抖动。如果你的延迟很高,首先,在做流量切换的时候,因为运维优先考虑产品服务的可用性,如果不行的话,你的复制延迟是不会考虑的,数据复制均衡后不会切换流量,所以你的数据冲突的概率就变得非常高了。为了保证复制的低延迟,我们认为主要的策略,或者说你的主要实现方式,是并发,因为只有你使用高效安全的并发复制策略,服务才会有足够的吞吐处理能力,而你的复制通道不会受到影响。“大量”数据遭遇导致的数据积压加剧了复制延迟。我们最初采用的是表级并发,但是在很多情况下,并发策略并不能得到有效的利用。例如,在一些业务线的数据库中,90%的数据可能集中在一张表或几张表中,而且大部分表的数据量都比较小,因此不能采用基于表的并发策略。我们现在运行的是行级并发,它的容忍度更高,适应很多场景。DRC&MySQLMasterSwitching这是DRC复制组件和MySQL集群的关系图。一旦MySQL集群中的master发生主备倒换,原来的master挂掉了,DRC是怎么处理的呢?目前的解决方案是DBA系统的MHA工具会通知DRC控制中心,DRC控制中心会找到对应的复制链接,然后将复制链接从旧的Master切换到新的Master。但关键是MHA在通知前将oldMaster设置为不可写,防止DRC继续向oldMaster写入数据。DRC上线运行状态(规模)这是我们DRC上线后的运行状态。现在大约有400个复制链接。此复制链接指的是单向链接。我们提供的消息订阅连接了约17个业务方,每天产生超过1亿条消息。DRC在线运行状态(性能)这是DRC在线运行的性能监控快照。我们可以看到是上午11点到12点一个小时的表现。你会发现其实有一个DB有Glitch,其中一个复制链接出现了glitch,复制延迟最高可达4s,但大部分复制链接的延迟都在1秒左右或更短。陈永廷,饿了么框架工具部高级架构师,主要负责MySQL异地双向数据复制,支持饿了么异地多活项目。曾就职于WebEx、思科、腾讯等公司。
