分享 | 滴滴分布式NoSQL数据库Fusion的演进之路

出品 | 滴滴技术redis

做者 | 余汶龙数据库


前言:

Fusion 是滴滴自研的分布式 NoSQL 数据库,彻底兼容 Redis 协议,支持超大规模数据持久化和高性能读写。在滴滴内部支撑了数百个业务,具备 PB 级别的数据存储量,是使用最普遍的主存储服务之一。在支持滴滴业务高速发展过程当中,积累了不少分布式存储领域的经验,孵化了离线到在线的高速数据导入方案、NewSQL 方案、跨机房同步等,一路解决了 Redis 容量限制、 离线数据在线打通、数据库扩展性差、异地多活容灾等问题。后端

本文来自滴滴的技术专家、Fusion 的负责人余汶龙在 2018 年北京 ArchSummit 全球架构师峰会上的演讲内容,重点介绍了 Fusion 的核心设计以及架构演进过程。服务器

内容框架

  • 诞生背景:滴滴业务发展简介网络

  • 演进过程:如何知足业务需求数据结构

    海量存储架构

    FastLoad负载均衡

    NewSQL框架

    跨机房多活运维

  • 总结 & 展望

诞生背景

  • 业务 & 架构演进过程

滴滴出行成立于 2012 年,刚开始创业阶段技术主要靠外包解决,没太多技术沉淀;发展到了 2014 年,乘客司机和单量都有不错的增加,咱们开始构建本身的系统架构,这个时候业务对于存储的需求很单纯,简单用用 MySQL 基本能解决咱们的问题。

到了 2015 年先后,咱们的业务线多了起来,专车快车等开始上线,这个时候咱们一方面作了中台系统的重构,另外一方面感觉到了不小的存储压力,即业务数据量和请求量剧增;到了 2016 年,合并优步先后,日订单量逼近 2000 万,进一步挑战咱们的存储系统,因而咱们按照不一样的业务,对存储进行拆分,由于不一样的业务对于存储的需求是不同的,不一样的业务对于吞吐、延迟、容量、数据请求大小等都有不一样的需求,分库分表也只是缓兵之计。

如何有效应对这些个性化需求呢?因而在这个时候,咱们就开始孵化滴滴本身的 NoSQL 数据库 Fusion 了,用它来丰富咱们滴滴的存储生态,为业务提供更多的存储选择。


  • Fusion 是什么?

前面咱们不断提到 Fusion 的关键字,那么是时候正式介绍下 Fusion。Fusion 是一个兼容 Redis 协议的分布式 NoSQL 数据库。定位介于 Redis 与 MySQL 之间的主存储数据库。怎么理解这个定位呢?也就是性能方面咱们向 Redis 看齐,即低延迟;持久化能力方面咱们向 MySQL 看齐,即 MySQL 具有的多副本、高可用、ACID 事务,咱们都是支持的,同时定位为服务打车订单这样的主流程在线业务。


它如何实现的呢?你们都知道 Redis 的数据是存放于内存中,虽然性能很好,可是容量极小,且每 GB 存储成本很高(大概是咱们 Fusion 的 10 倍以上)。因而咱们就基于 SSD 磁盘实现了一套分布式的存储系统,在 SSD 磁盘上实现了 Redis 的数据结构,对外经过 proxy 屏蔽内部细节,让用户像访问 Redis 同样访问 Fusion。当前咱们已经支持 String\Hash\Bitmap\Set\Sorted Set\List 这些主流的 Redis 数据结构。

演进过程

咱们 Fusion 的发展总共经历了 4 个阶段,分别解决了 4 类业务问题,咱们接下来重点看下具体过程。

  • 海量存储

首先来看如何解决海量存储的问题。


Redis 是一款很是优秀的内存数据库,但它也有这样一些已知问题存在:容量受限于内存、扩容迁移和大 key 过时、删除过程是阻塞的、宕机恢复慢等问题。咱们 Fusion 设计之初,就避免了这些问题。具体是如何实现的呢?咱们从需求分析出发。

 需求分析

Fusion 诞生初期,主要解决 2 个业务需求:

一是滴滴的历史订单,按照前面提到的每日千万级别订单量,很快就能达到几百亿的订单,这么庞大的数据量,存 MySQL 显然是不够灵活的,修改字段、修改索引都比较困难,存 Redis 就更加不可能,所以他们有新型存储的需求;

二是地图团队的司机行程轨迹,每产生一条打车订单就会产生一条司机行程轨迹,每一条行程轨迹由多个点组成,行程越长轨迹数据越大,这是一个比历史订单的数据量还要大的业务,存储的困难可想而知。


所以,咱们对上述两个业务的需求作了提炼和优先级排定:

  1. 刚需是海量存储。

  2. 具有基本的在线故障处理能力。

  3. 稳定性很重要!

  4. 性能要足够好,以司机行程轨迹为例,天天 300 亿级别写入,他们对性能的追求固然是越高越好。

  5. 接入要求简单,这里咱们选择了 Redis 协议。

  6. 打通其余存储系统。

知足了这些需求后,就诞生了存储系统 Fusion 的雏形。

  • 架构设计

· 软件结构

下图左边是数据流部分,从下往上看,即 Fusion 是构建在 SSD 磁盘上的存储服务,咱们引用优秀的存储引擎 RocksDB 来作磁盘 IO 操做,而后在磁盘之上,咱们增长一层 cache 来提高性能,而后封装一层网络框架并支持 Redis RPC,就实现了单机版本的 Fusion 存储节点,而后在单机的基础上加上咱们的集群路由管理,Fusion 的集群就搭建好了,固然对外提供服务的时候,还有一层负载均衡。

下图右边是控制流部分,即咱们在 SaltStack 平台基础上,构建了用户系统、运维系统、统计、监控、计费等系统,方便用户以及运维人员使用。


· 集群架构

集群架构上,咱们采用 hash 分片的方式来作数据 sharding。从上往下看,用户经过 Redis 协议的客户端(jedis、redigo、hiredis 等)就能够访问 Fusion,首先会通过 VIP 作负载均衡,而后转发到具体 proxy,再由 proxy 转发数据到后端 Fusion 的数据节点。

proxy 到后端数据节点的转发,是根据请求的 key 计算 hash 值,而后对 slot 分片数取余,获得一个固定的 slotid,每一个 slotid 会固定的映射到一个存储节点,以此解决数据路由问题。

此外,咱们还作了存储生态的打通。支持 Hadoop、MySQL、Redis 的数据同步到 Fusion,也支持 Fusion 数据同步到 MQ,供下游消费。


小结

接下来就对 Fusion 作个小结,拿 Redis 来作个简单对比。


FastLoad

咱们演进过程当中,解决的第二个问题是,离线数据到在线系统的快速打通。所以咱们作了一个叫 FastLoad 的系统。


  • 需求分析

首先,FastLoad 诞生初期主要支持两个业务:标签平台和特征平台。标签平台是指对每一个乘客和司机,都打上 N 个标签,而后后续的打车流程会依赖这部分标签,好比优惠券的发放;而后特征平台呢,会收集建立各种特征,对每一个对象用某个特征库作一次判断,便可肯定某种行为。接下来咱们对需求进行提取。

  1. 高性能。因为这部分数据是在离线计算平台 Hadoop 上加工完成的,业务很容易想到就近存放在 Hive 上,但 Hive 的查询性能实在不能知足在线查询的高吞吐、低延迟要求。所以对于新的存储系统,他们第一个要求就是性能!

  2. 定时更新。像特征数据,通常只须要小时级别甚至天级别的更新,因此业务须要有快捷的定时更新功能。

  3. 快速更新。特征数据还有一个特色,就是数据量特别大,以乘客特征为例,动辄上亿条数据,约 TB 级别数据量。这么大的数据量经过 SDK 写入确定是不行的。刚开始业务方也确实是这么玩的,直接经过 Hadoop 任务调用 Redis SDK,而后一条条的写入 Fusion,通常是天天凌晨开始写数据,等到早高峰 8 点时大量读取。可是这种方法实践下来,常常致使 Fusion 各种超时,在早高峰打车已经来临

    时还在写凌晨的数据,很是影响稳定性。所以第 3 个需求是必须快速更新。

  4. 稳定性。这个是毋容置疑的。

  5. 多表隔离。有些业务有不少类特征数据,他们有隔离存储的需求,也有分类更新、分类查找的需求,所以须要多表来支持逻辑到物理的隔离。


  • 架构设计

知足上述需求后,就诞生了咱们的 FastLoad 系统。接下来就来看下咱们的架构是如何设计的。咱们给用户提供两种接入方式:控制台和 OpenAPI。用户经过任一一种方式提交 FastLoad 任务时,都会在咱们的 FastLoad 服务器上,建立一个 DTS 任务,该任务会在 Hadoop 配置中心注册一个调度任务(周期性或一次性,由用户决定),而后 FastLoad 服务器根据用户上传的数据存储路径或 Hive 表(咱们支持的数据源有:HDFS 上的 JSON 文件和 Hive 结构的数据),按照用户提交的拼 key 方式,咱们启动 map/reduce 任务直接构造 Fusion 底层存储在文件系统上的文件 SST,并把它们构造好相互之间的排序,避免重复,构造好后通知 Fusion 存储节点,下载 SST 文件,而后 load 到 Fusion 数据库中。此后,用户就能够经过 Redis-Client 访问咱们帮它加载的数据了。


  • 小结

总结一下咱们的 FastLoad 一站式 DTS 平台,有以下优点:

  1. 减小 N 次网络交互。相比调用 Redis SDK 的方式写入,咱们减小很是多的网络交互,传输的是压缩格式文件,节省了网络带宽。

  2. 对用户请求 0 影响。咱们利用 map/reduce 的计算能力,作了 SST 的全局排序,让 SST 进入 Fusion 的时候,不经由 L0,直接到达最终 level,避免了 LSM 的 compact 影响,所以对用户能够说没有影响。

  3. 接入简单,用户 0 感知细节。用户既不须要关心 Hadoop 使用、任务调度,也不须要本身写 Redis SDK 的代码,只须要告诉咱们,在什么时间点须要什么样的数据便可!

  4. 提供了 OpenAPI,方便用户的自动化流程打通。

  5. 提供全量覆盖和增量导入两种方式。

NewSQL

在演进过程的第 3 个阶段,咱们主要是针对 MySQL 的。你们都知道 MySQL 的扩展性比较差,面对百亿级存储,有几个问题,一个是数据存不下,一个是扩展不灵活,好比修改字段、修改索引等。接着就来讨论下,咱们是如何解决这类问题的。


  • 需求分析

一样的,咱们先来分析下业务的需求是什么?简单理解下,咱们认为有 3 点刚需:

  1. 轻松改字段。即须要足够的扩展性。

  2. 存储不限量。即须要一个容量尽量大的存储。

  3. 省成本。既然须要存 MySQL 都存不下的数据,那么成本必定要考虑清楚。

至于事务、稳定性、高性能、二级索引,咱们认为都是基本需求。


  • 背景问题

· 如何实现 shema 到 key/value 的转换?

前面的介绍咱们知道,Fusion 是支持 Redis 协议的,那么 schema 转换成 key/value,就能够用 Redis 的 hash 结构来实现,下图咱们就以 student 表为例,转换了 2 行数据。


· 如何作主键查询呢?

下面的图片给出了一个例子,即查询 ID 为 1 的学生的所有信息或年龄。


· 如何实现二级索引呢?

咱们仍是以 student 表为例,分别构建以下 age\sex 索引,其编码规则以下可见。


· 如何作非主键查询和范围查询呢?

在上图构建好索引后,就很容易实现下面的两个例子,即查询年龄在某个范围的学生,和查询某种性别的全部学生。


· 背景问题

架构设计上分红接入层和数据存储层,在接入层(DISE)咱们提供控制台来管理用户的字段,用户能够在这里定义本身的 schema、字段、索引,并作相应的修改。而后用户经过咱们提供的类 SQL 的 SDK 接入,由咱们的 SchemaServer 作 schema 转换,接着插入数据到存储层。而后数据存储层吐出 binlog,由 IndexServer 异步消费 binlog 并构建索引。查询时候,用户的请求经由 SDK 到达 SchemaServer,SchemaServer 先查询索引服务器,拿到对应的主键信息,而后根据命中的主键查询详细信息,最后返回给用户。


  • 小结

NewSQL 解决的问题是针对 MySQL 的特殊场景的,咱们就拿 MySQL 来跟 Fusion 作个对比,能够看到 Fusion 只是在部分场景上解决了 MySQL 的容量限制以及扩展问题,但仍是有不少场景并不能支持的。


跨机房多活建设

最后一个演进咱们讲的是如何支持业务的跨机房容灾问题。


  • 背景介绍

滴滴多活的业务架构以下图,能够看到用户层接入层和业务层都是无状态的,所以如图中的白色虚线所描述的,他们的请求能够在两个机房间来回路由,而不影响业务请求的正确性。那么是如何作到这一点的呢?必然得有一个地方维护着状态的一致性,才能让业务自由切换。所以跨机房多活容灾最复杂的部分就在底层数据同步层,这里的数据同步涉及到不少中间件,咱们这里只关心 Fusion 的跨机房多活。


  • 架构设计

下图是 Fusion 的跨机房同步架构,不依赖任何外部中间件,也不依赖内部 proxy。当用户数据经过 A 机房写入时,落地到某个存储节点上,该存储节点会 cache 一份对端节点的路由表,并异步的将刚才写入的数据转发到对端集群。

咱们这里的转发采用了两个异步特性:1. 跟用户写入主流程彻底异步,即不影响用户正常请求;2. A 机房到 B 机房的数据同步,采用了异步、批量、应答的方式高效同步。既保证了用户请求主机房的吞吐和延迟,也大幅下降了备机房数据读取的延迟。


  • 小结

到此总结下咱们的多活方案:

  1. 异步数据复制。在追求性能的同时,放弃了一段时间的不一致。若是在数据未达成一致的时候,主机房宕机,备机房的数据将缺失,但这个缺失不会是永久,等到主机房恢复后,咱们会把这部分数据自动补齐到备机房,这个过程咱们会根据时间戳去重。

  2. 自适应感知集群状态变动,好比切主、扩容等。在运行过程当中,两个机房的集群不免会发生各种路由变化,咱们在设计时考虑到了这一点,针对路由变化,咱们会及时更新路由表,以把数据同步到正确的节点上。

  3. 数据可靠同步。咱们的数据同步是依赖滑动窗口的应答机制,所以实现了一种可靠的数据同步。

  4. 支持双写,解决秒级冲突。由第一点提到,在某些场景是存在双写的,若是双写的是不一样 key,天然不须要解决冲突,若是双写的是针对同一个 key,那么咱们会根据时间戳作冲突检测。

  5. 自动数据补偿。也就是在发生主机房宕机后,写入备机房的增量数据,能够自动的补偿到主机房;原先滞留在主机房的数据,在主机房恢复后,也能够补偿到备机房。便可以达到最终一致性。


总结与展望

  • 总结

在伴随滴滴业务发展的过程当中,Fusion 经历了 4 个发展阶段,咱们坚持”好东西是用出来“,所以在每一个阶段,都尽可能避免”过分设计“,只解决特定的业务问题。这给了咱们不少认真打磨产品的时间和精力,让咱们赢得了用户口碑。


  • 展望

经过前面的分享咱们知道,Fusion 虽然能作的事情不少,但不能作的事情更多,因此咱们的目标是持续发展持续演进,把解决业务问题当作己任。将来咱们将往分布式数据库方向前进,解决更多的业务问题。


  • 余文泷,滴滴出行技术专家,2016年加入滴滴,从零开始构建滴滴自研的分布式 NoSQL 数据库 Fusion。

相关文章
相关标签/搜索