本文整理自 QCon 北京站演讲《分布式 Data Warebase - 探索数据系统物理极限》,完整视频如下:https://www.infoq.cn/video/FN7zAKVCK6TmzQc4NoZQ
本文从一个民宿应用出发阐述现有数据系统的弊端,总结出业务的三个核心需求和四个典型场景。然后探索在这四个典型场景下满足最苛刻业务需求的数据系统分别是什么。最后解释什么是分布式 Data Warebase 以及它意味着什么。
一、当前数据系统的弊端
假设现在有一个民宿应用,房东可以将自己的房源上架民宿平台,潜在住客可以通过该应用查找心仪的民宿,并且在应用平台上完成预定。住客也可以通过平台办理入住和离店手续,以及在平台上输入评论,帮助其他用户更精准地筛选适合自己的民宿。
为了实现这一系统,起初只需要开发一个应用服务,并将所有数据存储在 MySQL 或 PostgreSQL 这样的关系型数据库中,就能实现简单查询。由于数据量足够小,应用的需求也足够简单,一个数据库就能满足应用的所有需求。然而,随着客户数量的快速增长,应用的数据量和查询请求已超出单机 MySQL 或 PostgreSQL 的处理能力,遭遇性能瓶颈。为解决性能问题,引入了 MongoDB,MongoDB 的优点是可以通过增加机器这种水平扩展方式来提升性能,以满足业务日益增长的性能需求。
随着用户数的进一步增长,用户不再满足于通过全名去查找民宿,而是希望能够通过关键词搜索符合需求的民宿。无论是关系型数据库还是 MongoDB ,用于关键词搜索时均无法满足用户对搜索的性能需求。此时需要引入一个搜索引擎,比如 Elastic Search。为了能让搜索引擎提供服务,需要先把数据导入搜索引擎中,此时就需要数据同步链路。数据同步一般有两种方式:一种是全量数据同步,即周期性地全量将数据写入搜索引擎。另外一个则是增量数据的同步,适用于有实时性需求的场景。增量链路通常会使用 Kafka/Flink 这类产品。当数据都同步到搜索引擎之后,系统就能通过搜索引擎高性能的搜索能力给用户提供关键词搜索。比如用户可以在民宿应用中输入“无烟 停车位”,系统就能够帮他找到无烟的,并且带停车位的民宿。
过去两年大语言模型取得巨大突破,机器终于开始理解自然语言,这给业务带来一种全新的可能。除了使用关键词搜索,用户也可以使用自然语言去寻找自己喜欢的民宿。为了满足这种语义搜索的需求,系统又引入了向量数据库。比如用户可以搜索“对宠物友好的简约风格的民宿”。系统同样需要通过全量和增量的同步链路把数据先同步到向量数据库之中,再结合大语言模型的能力完成语义搜索。
最后是汇总分析需求,用户可能希望知道过去三天某个城市评价最高的民宿有哪些,为了提供这种分析的能力,系统又引入像 Clickhouse、Hive、Snowflake 这类数仓产品。同样需要开发新的同步链路,将数据同步到数仓之中。除此之外,还可以将应用和服务的性能信息写入数仓以便发现和定位性能问题。有了数仓后就可以通过 BI 工具去满足用户的复杂的分析需求了。
这个民宿应用从一个简单的关系型数据库出发,随着业务的增长,数据量越来越大,需求越来越复杂,系统逐渐发展出一个较为复杂的数据架构。
简单总结一下这个民宿应用对数据存储的需求和使用场景。从数据存储需求上,这些数据可以分为以下几类:
从数据使用场景来说,我们刚才讲到了四种场景:
为了应对这些复杂的业务需求,系统逐渐发展出一套复杂的数据架构,这种架构在许多业务中都非常典型,然而,它也存在明显的弊端。接下来,我们将从三个视角分析这些弊端。
二、业务的核心需求
马斯克最近在 X 平台发表分享:“评价一个产品正确的方式,不是跟竞争对手比(太容易),而应当跟物理极限比。”。如果把追求物理极限当做目标,数据系统的物理极限究竟是什么?我们应该从哪些维度评估数据系统的物理极限?
技术服务于业务,让我们先从业务视角出发总结业务的核心需求,我认为业务的核心需求有三个:性能、正确性和实时性。
性能:随着业务规模的不断扩张,对性能的需求也会越来越高。我们希望无论业务对性能的需求有多高,都能够在合理的资源条件下满足它。随着业务功能越来越多,也将在更多场景中有性能的需求,我们希望无论业务以什么样的方式使用数据,性能都能够得到满足。因为不满足性能需求会导致系统立即出现问题,所以性能需求是过去二十年驱动大数据领域蓬勃发展的重要动力。我们民宿应用之所以采用这么复杂的数据架构,正是为了解决性能问题。
正确性:我们希望系统中存储的数据永远是正确的,并且所有场景查询的结果都是正确的。只有确保数据的准确性,我们依赖数据所做的一切决策才有可靠的基础。因为数据错误可能比较隐蔽,这个需求往往容易被忽视,但是对一个挑战物理极限的数据系统来说这必须是一个核心而基本的需求。
实时性:不同业务对实时性的需求各不相同,有的业务可能满足于小时级的实时性,有的则需要分钟级甚至秒级的实时性。作为一个挑战物理极限的数据系统,它必须能够满足所有业务对实时性的需求,即数据的延迟能够做到任意低。
接下来我们探讨如何为刚才所讲到的四个场景分别构建一个数据系统,在性能、正确性和实时性这三个维度上达到极致,挑战物理极限。
三、探索各场景数据系统的物理极限
简单查询
第一个场景是简单查询,我们从性能、正确性和实时性三个维度来评估。用户希望查看民宿的详情页,需要系统获取民宿的相关信息。初始阶段,系统采用的是关系型数据库,由于关系型数据库存储的是原始数据,因此自然满足了数据的实时性需求。同时,由于关系型数据库支持事务,事务保证了数据的强一致性,因此也满足了数据正确性的需求。
关系型数据库所遇到的唯一挑战是性能问题,它的性能受单机性能的制约,当业务性能需求超出单机性能上限时,单机关系型数据库就难以满足性能需求。一种自然的解决思路是将数据分片存储到多台机器上,通过水平扩展来满足性能需求。然而,在解决性能问题的同时,如何保证数据的正确性将成为挑战。例如,系统中有两张表,一张是住户表,记录了住户的基本信息,另一张是订单表,记录了所有的订单。当这两张表通过分片方式分配到多台机器上时,可能出现住户信息和他的订单信息被分配到不同机器的情况。当需要执行某些操作,如删除某住户时,我们需要删除该住户的基本信息及其所有订单,就需要同时修改多台机器上的数据,而保证这些写操作是原子的且读取时也是原子的,则需要分布式事务。然而,不幸的是实现分布式事务在技术上充满挑战。
为了绕开分布式事务实现水平扩展,NoSQL 数据库应运而生。在 NoSQL 数据库中,文档型数据库是最成功的一类。接下来我们就以文档型数据库为例来分析 NoSQL 数据库如何解决水平扩展的问题。水平扩展的主要挑战是需要实现跨机分布式事务。如果我们能保证把需要同时修改的多个实体分配到同一台机器上,我们就可以避免跨机分布式事务的需求。
文档模型正是基于这样的想法,把需要同时修改的关联实体组织成一个文档,一个文档能够保证存储在一台机器上不会被拆分。在这个例子中,左边是用关系模型表达的数据,右边是同样的数据在文档模型里的表示。在引入文档模型后,我们刚才所做的操作,比如删除一个住户和它的所有订单信息就变得非常简单,只需要删除这个住户对应的文档。
通过这种方式,文档型数据库巧妙地避开了跨机分布式事务的需求,从而实现了水平扩展的能力,这样就能通过水平扩展来满足业务日益增长的性能需求。文档模型能够很好地描述实体之间一对一和一对多的关系,用户可以把相关的实体放在同一个文档之中。但是如果两个实体之间是多对多的关系,文档模型的局限性就显现了。针对多对多关系,存在两种常见方案:
综上所述,要在文档型数据库中完整地表达所有实体之间的关系,分布式事务是一个绕不过去的坎。只有真正实现分布式事务,才能确保数据永远正确,永远一致。
这个时候我们面临着两种选择:
那么应该如何选择呢?为了回答这个问题,我们来比较一下这两种选择的最终结果。
文档型数据库除了水平扩展能力之外,另一个很大的优势是对半结构化数据的支持。文档的结构无需事先声明,用户能够很方便地修改文档结构,而不需要像关系型数据库那样需要找 DBA 才能修改表结构。这种特性特别适合于数据的敏捷开发,以文档型数据库为代表的 NoSQL 曾经一度被视为水平扩展和敏捷开发的代名词。
然而,从另外一个角度看,关系型数据库也有着诸多优势,其中最重要的一点是 SQL 查询语言,SQL 查询大大简化了业务的数据开发。并且 SQL 有着强大的生态,各种工具都能很好地支持 SQL。综合起来,我们认为关系型数据库拥有更多文档型数据库所不具备的能力。
因此我认为以关系型数据库为基础是更好的选择。挑战在于分布式事务的实现,但值得庆幸的是,Google 的 Spanner 已经成功实现了分布式事务,并发表了一篇详细的论文来解释其实现。Spanner 在 Google 内部取得了巨大成功,这表明分布式事务不仅在理论上可行,而且在工程实现上也是可行的。有了分布式事务后,我们就可以通过数据分片的方式来水平扩展满足性能的需求了。
以关系型数据库为基础并不意味着放弃文档型数据库在半结构化数据上的能力。关系模型的类型系统可以灵活扩展,只要在关系型数据库引入 JSON 数据类型,就能够方便地表达半结构化数据。
关系型数据库加持了分布式事务和半结构化数据能力之后,它就涵盖了 NoSQL 的所有优势。我们也在简单查询的场景构建了一个同时保证性能、正确性和实时性的极致系统。
关键词搜索
接下来是关键词搜索的场景,让我们通过一个例子解释为什么搜索的场景需要实时性。
在民宿应用的架构中,我们通过数据同步将数据库和 MongoDB 中的数据同步到搜索引擎,然后通过搜索引擎进行关键词搜索。假设有一个民宿管理员搜索“无烟 停车位”希望能够搜到自己的民宿,但搜索结果中却没有他的民宿。经过分析后发现,原来他忘了在民宿信息中添加停车位的信息,于是他前往管理台添加了停车位的信息。然后他立刻再次搜索,却仍然没找到自己的民宿,这让管理员感到困惑。实际上,问题出在他通过管理台修改了数据库,但数据库的修改同步到搜索引擎存在一定的延迟。正是因为这个同步延迟,管理员没有搜到自己的民宿。所以我们可以看到在搜索场景下,实时性也可能是非常重要的需求。
从三个维度看,搜索引擎比较好地解决了性能问题,但现有的搜索引擎不能满足极致的正确性和实时性需求。
为了解决正确性和实时性的问题,保证搜索引擎返回的结果永远是最新的,实际上只有一个办法,那就是不通过数据同步而是直接在原始数据上进行搜索。原始数据存储在关系型数据库中,而关系型数据库自然满足了正确性和实时性的要求,剩下的唯一问题就是性能。在这个场景下,性能问题和可扩展性并不是同一个问题,因为即使数据量不是很大,完全可以在单机上处理,使用关系型数据库进行搜索仍然会导致性能较慢。这里的性能问题是数据的新使用场景带来的单机的性能问题。
我们的想法是以分布式数据库为基础,来解决搜索的性能问题。搜索引擎具有良好的搜索性能,其中一个核心技术是倒排索引。倒排索引将文章分成一个一个关键词,找出每个关键词出现在哪些文档中,把这些文档 ID 排序形成一个列表,这个列表就是这个关键词的倒排链。当用户进行搜索时,系统首先找到每个关键词的倒排链,然后对这些倒排链进行并集或者交集的操作,就可以找到满足查询条件的文档了。
除了类型外,关系型数据库另一个扩展机制是索引类型。我们可以在关系型数据库中引入倒排索引来实现高性能搜索。而且这种倒排索引的应用不仅局限在关键词搜索上,我们同样可以用倒排索引来索引表中的结构化数据。这些倒排索引就可以用来加速这些结构化数据的联合搜索和过滤。因此,我们再次以关系型数据库为基础,通过吸收搜索引擎的倒排索引技术,打造了一个同时满足性能、正确性和实时性的极致方案。
语义检索
过去两年,生成式 AI 取得了突飞猛进的进展,基于它的大语言模型对很多问题都有出人意料的高质量回答,但由于这些模型在训练时只能接触到公共领域的数据,因此在涉及私有领域数据的提问时,它们的能力并不能得到充分发挥。解决这个问题有两种主要方法:
简单介绍下 RAG 的工作流程:当用户向系统提出问题时,系统会将问题提交给召回系统。召回系统可以在知识库中进行搜索,这种搜索可以是基于关键词的。例如,对于提到的“无烟 停车位”,知识库会返回与这些关键词相关的文档。然后,将系统提示、用户问题和这些召回的文档作为上下文输入给大语言模型,在充分理解问题并阅读召回的文档后,大语言模型生成答案并返回给用户。通过这种方式,我们可以把大语言模型的能力在私有领域数据上发挥出来。
需要指出的是,检索的方式有多种多样,前文提到的基于关键词的检索只是其中一种可能。随着大语言模型对自然语言的理解能力增强,另一种检索模式即语义检索也逐渐兴起。
语义检索能够让用户使用自然语言去寻找自己感兴趣的民宿。其基本工作原理是:利用嵌入模型为每个文档生成一个高维向量,也就是嵌入向量,该向量在一定程度上概括了文档的信息。不同的文档被转化为高维空间中的不同向量,向量之间的欧氏距离或内积表示了文档之间的相似程度。当用户提出一个自然语言问题时,系统会使用相同的嵌入模型为问题本身生成一个向量。然后,在高维空间中找到与该向量相近的一些向量,这些向量所对应的文档就是与问题相关的文档。将这些文档和提示作为上下文输入给大语言模型,大语言模型就有了足够的信息生成答案。
如果使用一个专门的向量数据库,就会依赖同步任务去同步数据,就可能有数据延迟。更好的做法是在关系型数据库中引入向量类型,这样能够很好地表达高维向量。当系统有上亿的文档的时候,在高维空间中它们对应着上亿个向量,找出和某个向量最近的一些向量计算量非常大。这时我们就需要用到关系型数据库的索引的扩展能力。我们会为这些向量创建一种新的索引,也就是向量索引,最常见的向量索引有 IVFFlat 或者 HNSW。在创建了向量索引后,寻找一个向量相近的向量就变得非常高效。把向量检索和大语言模型的能力结合起来就能够进行高效的语义搜索。我们并不需要一个专门的向量数据库,以关系型数据库为基础能够打造出一个同时保证性能、正确性和实时性的语义检索系统。
汇总分析
我们仍然先解释一下实时性在汇总分析的场景中的重要性。前文架构里的数仓中不仅存储了民宿和客户信息,还存储了应用和服务运行的性能信息,以帮助发现和定位性能问题。运维在监控上发现性能问题后,需要快速理解应用为何慢,是全体用户都慢还是部分用户慢。如果是部分用户慢,运维会检查在不同应用版本、不同手机型号、网络类型等情况下性能是否存在差异。经过一系列即席查询,运维最终定位到可能是某些特定情况下的配置问题。修改配置后,运维立即再次检查受影响用户的性能是否已恢复。数据的实时性直接影响了运维解决问题的迭代速度,因此实时性越高越好。
而数据的实时性取决于它如何写入数仓,这里有两种常见的做法:
可以看到这两类数仓都不能同时满足正确性和实时性的需求,为了能够同时实现这两点,我们必须支持数据的实时写入,并在写入时保证强一致的语义。全文提到的分布式数据库能够很好地实现这两点,剩下的唯一的挑战就是性能。这里的性能同样不是扩展性的问题,而是数据新的使用场景下的单机性能问题。我们可以借鉴数仓高性能的分析能力,数仓的性能主要得益于以下三个技术:
通过引入列式存储、向量化执行和物化视图等核心技术,系统可以高性能地支持分析场景。我们再次得到了一个同时做到性能、正确性和实时性的极致汇总分析系统。
四、分布式 Data Warebase
博采众长
麦克斯韦是历史上最伟大的物理学家之一,他将前人关于电和磁的四个方程放在一起,构成了麦克斯韦方程组。这四个方程合并后就产生了电磁波,这是任何单个方程都不具备的物理现象。
在刚才讨论的四个场景,我们都决定以关系型数据库为基础,吸收了其他系统在性能上的优化技术,从而在性能、正确性和实时性方面都达到了极致。这些性能优化技术不仅互相不冲突,而且相辅相成。我们完全可以像麦克斯韦一样,将这些性能优化技术同时吸收到一个系统中。这样我们就拥有了一个能够同时存储和处理结构化数据、半结构化数据和非结构化数据的系统,而且在简单查询、关键词查询、语义查询和汇总分析场景下都能够满足业务对性能、正确性和实时性的极致需求。
除了支持这四个场景外,这个系统还能够支持一些新的场景,例如同时使用关键词和自然语言进行查询,这是以前任何一个单独产品都无法做到的。
如果麦克斯韦仅仅因为博采众长就成为世界上最伟大的物理学家之一,也许你会觉得他的成功来得太容易。但事实上,没有人能够如此轻易地取得成功。麦克斯韦在写下方程组后意识到电和磁的不对称:变化的磁场可以产生电场,但变化的电场却不能产生磁场,这与他的美学观念不符。因此,他做了一个“小”修改,添加了一项位移电流。这个修改的意义非常深远,它解决了麦克斯韦方程组与电荷守恒定律之间的矛盾。同时,也正是由于这个修改才使得电磁波得以产生,于是就有了光。
极简体验
我们不禁会问:什么才是数据系统中的位移电流?我认为是极简体验。刚才我们所讨论的一切只是在满足业务的三个基本需求上做到了极致。当业务的基本需求得到满足后,我们需要同样在开发和运维的需求(也就是体验)上追求极致。体验将会成为区分各个产品的重要标准。
为了给开发和运维提供良好的体验,我们的系统需要做到以下几点:
通过这一系列的技术发展,终于诞生了一种全新的数据产品,即分布式 Data Warebase。Data Warebase 这个词是由 Data Warehouse (数据仓库)和 Database (数据库)两个词融合而来。这也意寓它包含了数仓和数据库的所有能力。
所有数据:它能够存储所有数据,包括结构化数据,半结构化数据和非结构化数据。
所有场景:它能够支持所有场景,包括简单查询、关键词查询、语义搜索和汇总分析,以及它们的各种组合。
挑战极限:它在业务的基本需求即性能、正确性和实时性上挑战极限。分布式 Data Warebase 在数据库场景是个更好的数据库,因为它通过分布式解决了数据库的水平扩展问题。分布式 Data Warebase 在数仓场景是个更好的数仓,因为它同时解决了数仓的正确性和实时性问题。
极简体验:同时它为开发和运维提供了极简体验,对外提供统一的 API,统一的数据存储,兼容已有生态,提供不同工作负载间的隔离,通过自适应根据场景做针对性的优化。
分布式 Data Warebase 是从业务场景出发追求极致的一个必然推论:分布式 Data Warebase 不是一个发明,而是一个发现。
在有了分布式 Data Warebase 之后,我们就开启了数据开发的新范式。比如,对于民宿应用的数据架构,通过使用分布式 Data Warebase,我们可以看到数据架构得到了大幅简化。并且更为重要的是,它能够满足业务在性能、正确性和实时性方面的极致需求。
最后给大家分享一个小故事:一个小女孩第一次看到一本纸质杂志,试图通过双指去放大和缩小内容,几次尝试无效之后,她终于意识到原来这本杂志是一个坏了的 iPad!
也许有一天我们也会意识到:现有的数据库,或者数仓,都是一个能力不完整的分布式 Data Warebase!