数据库

滴滴 NewSQL 演进之 Fusion 实践

本文根据滴滴数据库存储专家余汶龙,在DTCC中国第十届数据库大会的演讲整理而成。

余汶龙

滴滴出行技术专家,曾经在VMware、淘宝、阿里云从事虚拟网络及存储领域的工作。现负责滴滴自研的NoSQL存储、NewSQL存储以及DTS项目。从零组建了Fusion存储团队,并带领团队完成了上述3个大型项目的架构设计、方案落地以及线上护航。

大家都知道,NewSQL 是对各种新型数据库的简称,这类数据库不仅具有NoSQL对海量数据处理的高扩展能力和高吞吐能力,还具有传统数据库的事务能力和SQL能力。

那么在一套成熟的NoSQL系统上,是否可以孵化出NewSQL系统来呢?答案是肯定的。

因为在滴滴,就有这么一个成熟的自研的NoSQL系统Fusion,基于他之上我们成功孵化了NewSQL系统。因此本次的分享大纲就分成3个部分,前面先讲我们在NoSQL上取得的成功,然后讲如何演变到NewSQL,最后讲这个演进方案的缺陷,指出未来的演进方向。

接下来,先看本次分享的大纲概述,如下。

这块是我们所有存储产品的整体架构视图,我们的产品都是在RocksDB引擎层基础之上构建。首先增加了网络层、集群管理层、接入层的工作,构建了我们的NoSQL存储系统Fusion,然后在Fusion的基础之上,我们整合了滴滴的调度系统和Hadoop计算平台的能力,构建了我们的DTS服务FastLoad;第3块是在Fusion的基础上,增加了schema管理、二级索引、事务、binlog等能力,构建了我们的NewSQL存储dise;第4块是面向未来规划的分布式数据库。围绕这些核心服务,我们做了一套完善的智能管控系统,它是依托于salt-stack平台,实现了用户系统和运维系统,分别解决了用户接入问题和自动化运维的问题。

接下来,我们从NoSQL讲起。

第一章:成熟的NoSQL存储系统Fusion

这块首先讲Fusion的背景介绍。可以看到Fusion是用C++自研的分布式NoSQL数据库,支持Redis协议,数据通过RocksDB落盘,现在线上业务已经接入400多个业务,覆盖了全公司;当前线上规模是300多个集群,全自动化运维跑着,没有专门的OP人员参与,总数据量达到1500TB,峰值QPS超过1400W。

接下来讲Fusion的架构,如下。讲架构之前,先看Fusion的诞生背景,他诞生初期主要解决两个业务的数据存储:历史订单和司机行程轨迹。大家都知道滴滴是个每天订单千万级的业务,那么历史订单很快就会突破百亿级,而每条订单都会对应一条司机行程轨迹,而且打车距离越长,单条行程轨迹数据越大,这是一个比历史订单数据量更大的业务。在Fusion诞生之前,滴滴的存储主要是用Redis和MySQL,很显然在这种规模下的数据量,用Redis和MySQL并不是最优解。因此诞生了Fusion。

因为Redis的协议实现很简单,且数据结构非常丰富,因此我们在磁盘上去实现了Redis的存储结构,基于这样的核心思想,我们实现了Fusion的集群架构,从上往下分别是接入层、集群管理层、持久层、高可用层。

接下来,总结了5个亮点,挑其中4个来说明Fusion的产品成熟度,如下:

亮点1:数据流动

第一个是数据流动能力。做为一个自研的存储系统,他必须融入整个公司的开发生态,具备与其他存储系统、中间件、离线计算、实时计算等平台打通的能力,才能推广开。因此,我们在这方面做了很多工作,其中挑hive到Fusion打通,以及Fusion与Fusion之间打通的例子来展开介绍。

为了解决离线hive到Fusion的数据流动,我们做了一站式DTS平台FastLoad,其架构设计如下:

首先,他诞生初期是为了解决标签平台和特征平台的业务问题。这两个业务的数据是通过离线计算产生,因此数据是存放在hive上,很显然hive的优势并不是OLTP。

因此他们希望有个存储系统能够满足两个需求:

1. 支撑每天数百亿次的高速查询;

2. 支持他们快速的从离线更新TB级别的数据到在线。

很显然Fusion很容易满足第一个需求。那么第二个需求如何解决呢?业务很容易想到的办法是:遍历读取hive的数据,然后构造成一条条Redis协议支持的KV数据,然后调用Redis客户端写到我们的VIP->proxy->Fusion。整个过程链路比较长,总结下有3个核心痛点:

  1. 浪费研发资源。凡是有从hive到Fusion数据打通的业务,都得维护一套相同逻辑的代码。
  2. 难以保证稳定性。离线平台意味着高吞吐、高并发,用它往在线数据库灌数据,显然得注意流控和错峰,因此稳定性难以保证。
  3. 生产效率低。业务使用Redis协议的方式灌库,很多batch和压缩能力都没法用上。

基于上述的业务需求和核心痛点,我们做了FastLoad一站式DTS平台。它主要给RD、产品经理等用户提供服务,因此提供了两种接入方式:web console和open API。用户通过这两种方式,把FastLoad任务上传到我们服务器,然后服务器会注册一个调度任务,该调度任务通过用户传入参数,判定数据源,然后从数据源捞取目标数据,再把目标数据分片通过may/reduce做排序,构建SST文件,然后通过TCP协议的方式下载到Fusion存储节点,绕过proxy,利用RocksDB的ingest功能,加载到Fusion当中,再通知用户,用户就可以通过Redis协议读到导入的数据了。

第二个流动能力是集群迁移。

它是指一个Fusion集群到另一个Fusion集群的数据在线热迁移,包括全量和增量,迁移过程是在线不停服的,对用户无感知的,这个功能可以用于业务一建开城、机房迁移等。它实现了两个异构集群间节点的点对点数据迁移。迁移的过程大概是:首先源节点保留增量日志,同时构建全量快照,然后遍历快照生成临时的SST,再以SST文件的方式发送到对端master,master收到后转发到slave,待全量同步完成,再打开增量日志的同步,其他细节如下。

亮点2:降级容灾

首先Fusion具有主库降级能力,当业务流量高峰时,导致主库响应慢,此时扩容已经来不及,因此我们在proxy上实现了读写分离能力,即牺牲一定的一致性,把一部分读流量路由到从库,达到保全主库的目的。

然后是集群容灾。即Fusion底层实现了两个集群间的数据自动同步。正常情况,用户通过VIP1访问集群1的数据,当集群1不可用时,我们提供两种切流方案:第一种是,需要用户切换访问链路到VIP2,这可能需要用户重启;第二种是假设VIP可用,我们可以通过一键切流平台,把VIP1指向的Real Server改到集群2。

接下来看看是如何实现集群间数据同步的。

我们实现了异构集群间节点的点对点数据传输,不依赖任何中间件,每个节点会感知对端集群的路由信息,且能适应两端集群的状态变化,比如切主、扩容等,都能保证数据由正确的节点发起,然后同步到正确的节点。整个数据的可靠性是由滑动窗口来保证的,即我们用写到Fusion的key的唯一seq做滑动窗口的元素,实现了一个send/ack机制,保证了顺序发送、顺序重传、可靠传送。

另外,该方案还支持数据的自动补偿。即当集群1挂掉后,用户把流量切到集群2,在集群1恢复之前的这部分增量数据,会在集群1恢复之后,自动同步过去。

最后,这个方案也支持双写。我们利用RocksDB的merge功能,做了一个基于NTP的去重方案。

亮点3:极致效率

这块是针对RocksDB的一些特性做些优化。第一个我们实现了key粒度且具有热点预测功能的cache,解决RocksDB随机读问题,命中率能达到原生的3倍;第二个是实现了compact的24小时调度,磁盘能节约25%,延迟和毛刺均有大幅度降低;第三个优化内存占用,比如slave的page-cache是无用的,需要GC,block-cache在低峰时期是无用的,也需要GC等。

亮点4:安全保障

第一个是升级时数据备份。常规升级时,我们都会备份二进制文件和配置文件,但是对于数据并没有备份。我们针对RocksDB的SST文件特性,即只会被创建和删除,而不会被修改,做了一个硬链接备份方案,该方案是写时拷贝,因此备份速度非常快且磁盘空间占用非常少。更关键的是,这个方案是实现在智能管控平台的升级流程里面,不需要改服务端代码,只需要修改管控程序就可以了,因此很方便做到100%线上覆盖,保证数据安全。

第二个是提供用户级别快照,利用了RocksDB的checkpoint,可以做到秒级快照。

第三个是数据多版本存放。即在FastLoad场景里面,我们会保留多版本数据,用于用户随时随地的切换他需要的数据,比如A/Btest,数据回滚等。

第二章:基于Fusion的NewSQL探索实践

接下来讨论NewSQL探索实践的细节。

首先,需要回答一个问题,我们为什么要做NewSQL演进?我想大家的出发点都是一样的,都是为了解决在大数据量存储下的MySQL的几个核心问题,即:灵活问题、扩展问题、成本问题。那么对应的NewSQL,我认为就该具备这样几个能力:轻松加字段、存储不限量、更高性价比。

那要实现这样的目标,面临哪些挑战呢?

  1. 如何在KV系统上感知用户schema?
  2. 如何在KV系统上吐出兼容MySQL的binlog?
  3. 如何实现二级索引的存储和查询?
  4. 如何实现事务和事务的交互?

接下来,给出我们的NewSQL架构图。首先我们把用户的DDL操作,收敛到控制台,不让用户直接给DB发起DDL操作,用户发起的DDL操作,改变的schema信息,会保存到配置中心,同时会推送给我们的proxy,所有proxy推送成功后,返回给用户成功信息。用户在通过MySQL客户端访问我们的proxy,我们在原来Redis协议的proxy上,增加了SQL-parser等工作,并将接受到的用户SQL请求,转成KV,再写到Fusion。Fusion服务端会生成MySQL格式的binlog,再吐出到MQ,我们的索引服务器会异步消费这部分信息,然后根据用户自定义的索引key,把他写到索引存储里即可。

schema管理

这块的核心思路,就是把DDL操作与数据流分离,通过配置中心来解耦。Proxy这边重点需要解析SQL,转换KV等。目前Fusion支持insert/replace/delete/select/update语句,它的定位是解决单表大表的存储问题,与MySQL做一定程度的互补,因此在兼容MySQL上做了较大的舍弃。

这里给了一个SQL到KV转换的例子,左边是一个student表,三行四列,右边是Redis的hash编码,大key有表名加主键组成,field是列名,value是行列值。

binlog兼容

这块是讲如何吐出binlog的,因为滴滴的通用binlog是需要原值的,而KV默认的log是只有当前值,因此我们在产生log的时候,先判断用户的插入类型,如果是update就先取原值,再一起写到log里,如果是insert则不需要。

二级索引(唯一和非唯一)

上述binlog吐出到MQ之后,这里的索引模块会来消费MQ,并异步构建索引(因为性能考虑和不具备分布式事务能力,因此没有做实时索引)。索引key的拼接方式如下两种,这里需要注意的是,看前面Fusion的架构图,我们知道Fusion集群的数据分片是通过hash实现的,因此分片之间是不连续的,无法做到跨节点scan,因此我们对同一个列索引,增加了一个分区健,即Redis协议的hash-tag,来保证同一个索引,必须存到同一个节点,方便我们做scan。

事务功能

事实上我们不支持分布式事务,还是通过redis的hash-tag,规避了分布式事务,即跟用户约定,让用户把希望做事务保证的行的主键,带上hash-tag,让这些行放到同一个节点。分布式事务就转成单机事务了。而单机事务,我们利用了RocksDB的事务引擎,因为RocksDB再给MyRocks提供引擎时,支持了完备的事务能力,因此我们直接加以利用。最后要解决的是事务交互问题,这里我们通过lua来解决。

事务交互

原生Redis对于事务交互处理支持的并不好,比如不支持回滚等,因此我们想要通过Redis来实现事务交互,就得增加一些接口,但这些接口显然不是标准SDK提供的,就很难推广。好在Redis客户端提供了lua的脚本支持,那么Fusion也实现lua解释器的功能,就可以让用户通过lua脚本传递任何接口过来,这个特性可以很好的解耦。同时用户可以在lua脚本里实现各种if/else等逻辑。那么当我们把事务交互通过lua提供后,用户跟Fusion交互时,就可以把相关逻辑放到Fusion来执行,整个过程是一阶段事务,不需要复杂的begin/commit/rollback等。我们的proxy也不需要维护事务状态,内部的锁处理也更简单,不需要关心长事务,更不用关心事务里多个key是否分布在不同节点。

那在MySQL协议这边如何实现事务呢?我们是建议用户把lua脚本写到MySQL的comment里,通过接下comment的lua,来执行事务。

总结

最后,对我们的NewSQL方案做个小结。他的优点很明显,直接在现有NoSQL架构上加以迭代即可快速上线,复用程度高,稳定性高;同时在不需要事务的场景下,确实很好的解决了扩展问题、灵活问题;另外,在前面提到的hive到Fusion的打通中,让用户使用更自然,因为原先的FastLoad是把SQL结构化的数据转成非SQL结构的KV的,现在有了NewSQL之后,用户就可以从SQL到SQL。

这个方案上线一年,当前存入数据超过了400TB,总QPS超过了200W;接入的业务超过了58个,平均每个业务存储8TB以上数据,可以看到这个数据量是MySQL不太容易解决的一个量级。

第三章:分布式数据库设计

前面只提到了NewSQL方案的优点,实际上,它还是有很多不足的地方,最核心的问题是,他是一个”伪分布式“方案,虽然数据做到了无限扩展,但是:

  1. 只实现了单机事务。
  2. 只实现了单机索引。因为集群按hash分片,无法跨机scan。
  3. 只有异步索引能力。因为没有分布式事务保证,我们只实现了异步索引。
  4. Join更是不支持的。
  5. 无弹性扩容能力scale out。

很显然,在现有的NoSQL架构上,已经无法简单的解决这些需求,需要彻底大改。因此,我们有了另起炉灶的项目——分布式数据库。它首要解决的几个问题是:

  1. 分布式事务。
  2. 数据和索引的真正无限扩展。
  3. 实时索引。
  4. 弹性扩容。
  5. 多副本强一致、高可用。
  6. SQL兼容等。

架构设计

架构设计如下,事实上,这也是分布式存储的经典架构,很多的系统都长在类似架构上面,它实现了range分区、强一致、弹性扩容、全局scan等能力。细节就不多做展开了。

当前状况

我们在这套架构上,实现了一个具备raft强一致、全局scan、自动分裂等能力的分布式KV系统,下一步是做分布式事务。即先实现一个功能强大的KV系统,然后在这基础上继续做SQL的支持。

总结三部曲

最后是对整个演进过程做一个总结。

首先我们研发了NoSQL系统Fusion,锻炼了我们的基础能力:分布式、持久化、高可用、数据流动等。

第二步是我们的NewSQL探索,我们做了一个快速解决业务问题的方案,这个方案有成功的地方,因为它解决了50多个业务的需求,也有失败的地方,我们无法在这套系统上走得更远,但这提升了我们的系统认知,这很重要。

第三步是未来演进,想要在海量数据OLTP这条路走的更远,必须彻底革命,因此最后的演进是抛弃了现有系统架构,从头设计我们的分布式数据库。这个项目也是分期的,一期我们先做一个功能强大的KV系统,然后二期在他的基础上增加SQL-parser,取代我们现有的NewSQL方案(功能比较简单,很容易落地),然后三期才是高度的SQL兼容。

整个演进过程遵循了两个原则:避免过度设计和大跃进。即在现有稳定的架构上花最小的代价,解决最短板问题。整个过程做到了产品的持续交付,既快速响应业务需求,又不断丰富自己的认知,最终朝着一个成本可控、稳定迭代的目标前进。

我还没有学会写个人说明!

哈登vs字母哥,看AI怎样预测今年NBA最有价值球员!

上一篇

程序员的入门门槛真的那么低吗?

下一篇

你也可能喜欢

滴滴 NewSQL 演进之 Fusion 实践

长按储存图像,分享给朋友

ITPUB 每周精要将以邮件的形式发放至您的邮箱


微信扫一扫

微信扫一扫