网易分布式数据库多活架构的演进与实践

2020 年 7 月 14 日

网易分布式数据库多活架构的演进与实践

本文由 dbaplus 社群授权转载。


大家好,今天给大家分享一些网易近几年在数据库多活方向上的工作。


我将简单介绍下为什么我们要做数据库多活,再从三个阶段介绍网易在数据库多活上做的工作。



一、数据库多活的目标


数据库多活的目标包括“容灾”和“提升处理能力”两方面。


容灾可以简单理解为当系统由于外部或内部原因出现部分不可用时,仍然能在短时间内恢复可用。而容灾最常用的手段即是备份,在数据库领域不仅需要对计算能力做备份也要对数据做备份。容灾级别可以划分为宿主机级别的容灾、机房级别的容灾和跨城的容灾。


要提升数据库的处理能力,一般有读写分离和单元化两种方式,读写分离将数据库的备份节点的读能力提供出来,其实提升了系统的利用率,而单元化则是当整个机房的资源无法容纳业务的增长时,加入更多的机房资源,扩展业务的处理能力的方法。



二、网易的产品特性


网易成立 23 年以来,孵化了大量的产品,不同的产品特性、产品发展到不同阶段对数据库多活都有不一样的需求。



像音乐、新闻这类的泛娱乐产品,它们每日要处理大量的请求,同时也会产生大量的数据,而由于其产品的独特性,它们对数据的一致性要求往往不是特别高,但对可用性要求比较高。


而严选这样的电商类应用,由于其独有的交易类数据,对数据一致性要求是比较高的,流量一般小于娱乐性应用,对可用性同样很高。


而最为极端的支付类应用,由于所有的业务都和钱挂钩,对数据一致性要求极高,流量反而最小,可用性要求同样很高。


对一致性要求的不同会导致不同的产品在选择数据库多活方案时有不一样的选择,而互联网产品对可用性的要求往往是一致的,都无法接受产品长时间不可用。


三、网易数据库使用现状


我从 DBA 同事那边要到了一份网易当前的数据库选型分布数据。



由于其稳定性和开源的特性,超过一半的数据库实例是 MySQL。而缓存则是 Redis 使用最广,数据量较大时会选用 HBase。Oracle 仍然在一些老的系统内有使用,而其他一些 NewSQL 数据库则还在测试验证阶段。


单实例的 MySQL 处理能力有限,无法满足网易内各个产品的使用需求,所以网易内大量的 MySQL 实际是挂在了自研的分布式数据库 DDB 的下面,DDB 封装了分库分表的能力,对外提供了可扩展的数据库处理能力。DDB 的实现思路和开源的 MyCat、ShardingSphere 很像,这里就不赘述了。


文章后续的多活方案都以 MySQL 作为对象描述。


四、机房内的多活


机房内的数据库多活其实是单机房内数据库的高可用方案。一个主节点提供读写能力,一个高可用从作为主实例的备份,还有一个只读从对外提供只读的能力。这里三节点的部署方式能够保证任何一个节点不可用时,都有备用节点接替其提供服务,而整个系统的处理能力不会下降。


这个方案适用发展初期的业务,提供了宿主机级别的容灾能力与读写分离的能力。



除了异步复制、半同步复制外,MySQL 从 5.7 之后便提供了 MySQL Group Replication 这种新型的复制模式,相较于传统的主从方案,MGR 提供了自动选主、数据强一致和主从皆可写入等能力。MGR 在网易内部大量产品上都有不错的推广。



无论是主从复制还是 MGR 都逃不开一个复制延迟的问题,MySQL 复制的是 Binlog,Binlog 发送到从节点到 Binlog 在从节点上回放执行有一段时间差,这就是复制延迟。


在主从切换时,从节点必须回放完堆积的 Binlog 才能对外提供服务,所以过大的复制延迟会导致不可控的主从切换时间。另外只读节点上复制延迟过大,也会导致业务读到过时的数据,可能影响到业务的正确性。


MySQL 后续版本提升了其并行复制的能力,但仍然没能完全解决复制延迟过高的问题,我们的做法是监控各个实例的 TPS 情况,当达到可能触发复制延迟时就对集群做水平拆分(通过 DDB 的扩容完成)。


业务上的话 DDB 提供了 LOADBALANCE 这样的 hint 语法,允许业务指定 SQL 能容忍的复制延迟,如果延迟超过设定的阈值,则会调度到主节点执行。



五、跨机房多活


如果业务有跨机房容灾的需求,或单一机房内资源已经无法满足业务的发展需求时就有跨机房多活的需求了。最完善的方案是使用单元化来做,但是单元化对业务的改造代价过大,所以出现了一种折中的跨机房多活方案。



此方案中业务层代码还是在每个机房部署一套,但是在数据库的写操作和对一致性要求较高的读请求会路由到主机房,而每个机房都有自己的只读实例提供一致性要求不太高的读请求。


此方案提供了简单的跨机房容灾能力,但是跨机房的读写会造成较大的延迟,且对机房间的稳定性要求也较高。


分享前我简单搭了一个实验环境,测试了在相同的并发下不同的延迟对数据库性能的影响。



当延迟上升到 3ms 时,数据库性能下降在 30%+,当延迟上升到 20ms 时,数据库性能下降在 75%+。而 3ms 和 20ms 刚好是同城多机房和跨城多机房的一个延迟标准。


从数据上看,同城情况下,强一致的同步方案仍然是可行的,而跨城的情况下,基本只能用异步的同步方案了。


六、跨机房单元化


应用仍然在每个机房对立部署,但是机房内的应用只读写本机房的数据库,为了避免多写带来的数据冲突,每个机房只写自己负责的数据,这就是跨机房单元化多活。这个方案中最重要的就是对流量的路由,每个机房负责了一部分业务流量,常见的划分方式为按照 ID Hash 划分,或者按照地域划分。


此方案解决了上面方案跨机房读写的延迟问题,并且具有更好的水平扩展性,唯一的弊端是业务改造代价较大。数据库层要提供较好的数据同步能力,缓存、消息队列、RPC 等组件都要做对应的改造。



不同单元的数据库双向同步工作我们使用自研的网易数据运河(NDC)来完成。NDC 对外主要提供了数据迁移/数据同步和数据订阅的能力,数据同步将关系型数据库的数据实时同步到异构的对端数据库或数仓中,数据订阅则将数据库的增量变更推送进消息队列,供业务方或流计算任务消费。


数据库单元化场景下主要用到 NDC 的双向同步能力。



七、解决回环复制


双向同步第一个要解决的问题即是回环复制。如果对同步的数据不加过滤仍由其在不同单元的数据库中来回复制必然消耗大量的网络带宽,最终还会导致数据不一致。


解决回环复制的核心是如何给每个增量变更标注其发生的机房,然后在同步时避免将其同步回发生的机房,就能解决回环复制的问题了。这里总结了三种解决回环复制的方案:


  • 引入额外字段: 给每个同步的表加入额外字段标注上一次对其操作的机房信息,此方案对同步的性能影响最小,切可以做到跨不同数据库类型使用,但是对业务的表有侵入。

  • GTID: 使用MySQL自己的GTID来标注事务的机房信息,这也是MySQL原生复制解决回环复制的方案,其对同步的性能影响较小,完全无侵入,但是只能用于MySQL之间的双向同步。

  • 事务中引入额外SQL: 在业务的事务中加入额外的SQL来标注此事物产生的机房,此方案也能做到跨数据源,对业务侵入较小,但是对同步的性能影响较大。


三种方案都有各自的优缺点,实现上根据需要选择即可。NDC 对三种方案都做了实现,在单元化场景下实际选择的是方案一,主要是其使用的额外字段同时是另外一个功能需要的字段,算是重复利用,没有引入额外开销。



八、解决数据冲突


正常情况下,每个单元都写自己负责的数据,不存在数据冲突,但是当某个单元不可用,要做流量切换的时候,就会出现多个单元同时写入同一行数据的情况,从而产生数据冲突。数据冲突不可避免,我们需要一个机制保证产生冲突的数据最终能保证一致。


解决数据冲突的核心思路是给每个变更赋予一个版本信息,当产生数据冲突时总是保留高版本的数据即可保证最终一致性。NDC 采用了最简单的物理时间作为变更的版本信息,在进行数据同步时,只有当对端版本低于(或等于)同步的变更时,才同步到对端。



这样的数据冲突解决方案能解决 99%以上的数据冲突,保证数据的最终一致。但是在一些特殊情况下可能失效,一个比较典型的例子就是机房间由于物理时间基准不一致,某个后发生的变更它的版本却更小,这样这个变更无法同步到对端机房,最终导致数据不一致。


解决这个问题的办法是将这种特殊数据版本信息给提升到发生变更之前的版本,保证变更能同步到其他机房。


在实践中我们发现单个机房内对同一行数据的并发修改也可能造成版本后退,当删除和插入操作同时进行时也可能出现版本未知的情况,对于这样的特殊场景都只能通过调整特殊的同步手段最终来确保数据的一致性。



九、实时数据校验


数据校验对一个数据同步系统是一个可选但又是必须的功能。在单元化的双向同步功能中我们不仅使用定期的全量校验发现系统中存在的不一致风险,还提过了实时数据校验功能更实时地发现数据不一致的风险。数据校验的覆盖范围和对数据库的性能影响是一个相悖的问题,更大的校验覆盖范围必将导致更大的数据库性能开销。产品上我们允许用户选择实时数据校验的范围,可选的范围包括:


  • 双写数据校验:所有在多个单元都发生了写入的数据都进行校验。

  • 冲突数据校验:产生了数据冲突的双写数据才进行校验。

  • 风险数据校验:存在版本回退这样的特殊数据才进行数据校验。


数据校验的实现比较简单,将需要校验的数据放入队列中,校验线程不断地去取需要校验的数据,并比对源和目标的数据是否一致即可。



十、总结


网易数据库多活经历了机房内多活、跨机房多活和跨机房单元化多活三个阶段。在数据库多活上积累了一些经验,之后也还有更长的路要走。希望这些经验能够给各位一些启发,也期待和大家在数据库多活的话题上有更多的交流。


Q&A


Q1:数据变化是通过什么工具推送到 Kafka?


A: 通过网易内部自研的数据传输工具 NDC 完成,它的工作原理与开源的 Canal 类似,模拟自己为一个 MySQL 从节点,与主节点建立复制协议获取 binlog,解析后得到行变更数据然后推送入 Kafka。


Q2:请问网易的单元化是分了两个单元嘛?


A: 是的,现在是在杭州的义桥与东冠机房做同城双单元,机房延迟大概在 2ms-3ms 之间。


Q3:什么时候用中间件复制,什么时候用数据库自带复制?


A: 原生复制是 MySQL 自身提供的复制能力,好处是不需要额外的组件部署,也很稳定。但是当需要进行跨大版本复制,原生复制的性能不满足需求,或者想自定义一些复制特性的时候就可以使用中间件来完成复制工作了,中间件复制另外的一个优点是可以自定义很多监控指标,实时监控复制的状态。


Q4:数据同步异常后校验机制落后于业务怎么办?


A: 我猜问题是想问在校验机制发现数据不一致问题之前,错误数据已经暴露给业务方时如何处理。由于校验并发现问题距离问题发生肯定是有延后的,所以这个问题没法避免,核心还是在发现问题后能够快速定位这些不一致数据对业务产生的影响,并根据实际情况来处理。


Q5:单元化具有扩展性,怎么理解?


A: 单元化的方案对单元的个数理论是没有限制的,在业务规模上来后发展到几十个、上百个单元都是可行的。


Q6:刚刚物理时间不一致,数据回退怎么解决的?


A: 由于不同机房物理时间基准存在偏差,可能在数据更新时发生更新后的数据时间戳低于更新前的时间戳,光从时间戳上看是发生了版本(物理时间即为数据的版本)回退,此时我们是通过将更新后的时间戳提升到与更新前的时间戳一样的大小,来保证此条数据能正确同步到对端单元。


Q7:说数据异常了,而业务在校验发现前已经用了脏数据怎么办?


A: 单元化方案里的双向同步策略只保证最终一致性,如果中间数据出现短暂的异常读,业务上是能容忍的,这里牵扯出一个问题,什么样的业务模块适合做单元化,我们这里一般认为更新不是特别频繁,且业务上自己有比较好的分区逻辑的业务适合做单元化。


Q8:流量切换的时候,怎么保证业务正确性?


A: 在计划内切换的场景下,上层流量控制可以保证在底层同步组件都完成数据同步后再将流量切过来,这个过程一般在 1 分钟以内,在计划外的切换过程中,无法做这个保证,所以可能产生两边数据不一致,这个需要在事后找出不一致的数据,并按照业务自身特点进行修复。


Q9:支付类数据,如何保证强一致的?


A: 支付类的数据为了保证强一致性,一般使用 MGR 这种类 Paxos 协议来完成数据同步。


Q10:这种数据的同步是解析的 binlog,然后写入 NDC 同步到异地机房吗?


A: 是的,具体可以参考 Q1 的回答。


Q11:系统重构、数据库表设计及结构都不一致,一般迁移用什么 ETL 工具?


A: 网易自研的 NDC 就能满足这类需求,开源的话 Canal 提供了比较完善的 binlog 复制解析能力,但是一些具体的复杂需求可能需要自己实现。


Q12:网易少量的 Oracle 都用在哪些业务系统上?如何做多机房双活的?


A: Oracle 现在基本就只有网易支付在用了,据我所知他们并没有在 Oracle 上做多机房双活的计划。


Q13:NDC 支持异构数据库同步吗,SQL Server Oracle MySQL?


A: 支持,现在支持的系统暂时只包括 Oracle 和 MySQL,主要还是网易内部没有使用 SQL Server。


Q14:Redis,MQ 复制用什么中间件?


A: Redis 据我所知是使用的 Redis Cluster 方案,MQ 的复制是业务自研的一个同步工具。


Q15:MGR 怎么处理 DDL?


A: MGR 对于 DDL 的复制和对于普通事务的复制没有太大区别,不过如果是大表的阻塞性 DDL,由于执行时间过长,可能造成较大的复制延迟,网易内部对于大表的 DDL 一般是用 PT-OSC 这样的在线修改表结构工具完成。


Q16:一个单元就是一个闭环的机房吗?


A: 一般是的,单元化方案里一般要求流量尽量在单元内闭环。


Q17:一个交易,做到一半,挂了,流量切换后,是把交易重新做吗?之前做的怎么处理?


A: 交易一般具有事务性,所以做到一半挂了一般理解为这个事务没有提交,切换后整个过程需要重做。


Q18:如果解决分库分表后,数据一致性的问题?本来应该放在一个事务的两个 SQL,分库分表后写往两个实例?有哪些方法可以解决,除了分布式事务,性能太低?


A: 分布式数据库中的事务基本都是按分布式事务的方式解决的,比较经典的是两阶段提交,如果觉得两阶段提交效率太低,可以业务层使用基于 MQ 的柔性事务方案。


Q19:版本提升的依据是什么呢?怎么判断是不是时间戳延迟引起的?


A: 版本回退的判断即是通过 binlog 中的时间戳列的数据得到的,所以依据就是 binlog 中的数据。


Q20:MGR 能用在异地多活场景吗?延时太大吧?对网络有哪些要求?


A: 可以用在同城单元化的方案里,同城的延迟情况对性能的影响较为可控,同城延迟一般在 3ms 以下。


Q21:Canal+RocketMQ 可以用在异地复制吗?


A: 可以。


Q22:银行级的场景下使用哪种方案做多活好啊?


A: 银行对数据库一致性要求比较高,除了一些强一致的商业方案外,MySQL 的 MGR 是一个不错的选择。


Q23:流量切换之前,做个一个操作。切换后,发现数据没同步过来,又做了,那不是做了两次吗?


A: 答案参考问题 8,计划内和计划外可能要分开讨论。


Q24:MGR 和 RAC 有比较过吗 同城双活部署的话 MGR 是否可以达到 RAC 的同步能力?


A: MGR 本质上还是日志的同步,和 RAC 存储共享的思路是完全不一样的,性能上 MGR 也肯定是不如 RAC 的,但是好像 MGR 部署门槛低。


Q25人工做 DDL,那人比数据同步慢怎么办?数据过来了,但是表还没改过来,就出错了。


A: 流程上一定是 DDL 在所有单元都执行完成后再开放给业务使用。


Q26:MGR 多活现在有在核心业务上生产的吗?


A: 网易内部暂时还没有。


作者介绍


周劲松


网易杭州研究院资深研发工程师


  • 来自网易数据科学中心,目前是网易分布式数据库DDB及网易数据运河NDC项目负责人。

  • 对数据库及相关中间件的设计和研发有丰富经验。


原文链接


https://mp.weixin.qq.com/s?__biz=MzI4NTA1MDEwNg==&mid=2650791482&idx=1&sn=b067a153deba0783bf3fd8308296b1bf&chksm=f3f96bafc48ee2b99beb3c2a5816a7f81254fc7acac9ca245ad7173fbb27905c74fee94315a6&scene=27#wechat_redirect


2020 年 7 月 14 日 10:051751

评论

发布
暂无评论
发现更多内容

ARTS 01 - 技术人的理想主义

jerry.mei

算法 Vue 练习 ARTS 打卡计划 ARTS活动

【大厂面试02期】Redis过期key是怎么样清理的?

NotFound9

Java 数据库 redis 架构 后端

手撕编译原理:汇编语言不会编

贾献华

微信小程序开发 | 如何在小程序中使用自定义 icon 图标

彭宏豪95

微信小程序 学习 编程 前端 IT

【译】5 个你需要知道的 JavaScript 小技巧

零和幺

Java 前端 技巧

深入理解JVM内存管理 - 堆和栈

NORTH

堆栈 深入理解JVM VM参数

Java 走过的创新25年

田晓旭

Java25周年

Shell 文本处理一则

wong

Shell sed grep

Flink 1.10 SQL、HiveCatalog 与事件时间整合示例

Apache Flink

大数据 flink 流计算 实时计算 大数据处理

这场大数据+AI Meetup,一次性安排了大数据当下热门话题

Apache Flink

大数据 flink 流计算 实时计算 大数据处理

带你学够浪:Go语言基础系列 - 8分钟学控制流语句

柠檬橙

golang 后台开发

什么时候去面试

escray

【Sentry搭建之 docker-compose】

卓丁

DevOps Docker-compose CI/CD sentry

奈学教育:Hadoop源码编译全流程分享

奈学教育

千万别学编译原理

池建强

编译原理

坚持ARTS-week2

王钰淇

ARTS 打卡计划

http 四种鉴权方式简介,未来可能还会出现第 5 种鉴权方式:全息生物验证

李艺

HTTP

游戏夜读 | 如何管理公司?

game1night

有的线程它死了,于是它变成一道面试题。

why技术

源码分析 jdk源码 线程池 Java 面试

缓存与存储的一致性策略:从 CPU 到分布式系统

伴鱼技术团队

缓存 系统设计 cpu 系统架构 架构模式

入门到放弃:理清前端技术概念

大伟

Java ecmascript 前端 Node

普通二本,毕业三年,北漂之后,我是怎么成为程序猿的。

why技术

个人成长 程序人生 随笔杂谈 北漂

重学 Java 设计模式:实战适配器模式

小傅哥

设计模式 小傅哥 重构 代码质量 代码坏味道

centos7分区命令parted的用法(大于2T)

唯爱

Docker 容器优雅终止方案

米开朗基杨

Docker

吉德热泵烘干机解放阳台,引领生活品质新风尚

infoq小陈

工作的创新能力

夜来妖

产品 重新理解创业 产品经理 创新突破 创新

程序员都惧怕的故障域

松花皮蛋me

Java 问题处理

LeetCode | 2. Reverse Integer 整数反转

Puran

Python C# 算法 LeetCode arts

Java 最新的JDK14.0.1调试成功

程李文华

Flink Weekly | 每周社区动态更新-20200520

Apache Flink

大数据 flink 流计算 实时计算 大数据处理

网易分布式数据库多活架构的演进与实践-InfoQ