【翻译】如何构建你的分布式数据库

#distributed system #database

我参与的第一个分布式数据库叫做 CSPIT。Leader是个很有远见的架构师,还有Amazon中我认为最聪明的一群工程师来一起合作。

可惜的是,CSPIT一直都没有发布。在技术方面由于两个原因,我们陷入了可扩展性的问题(scalability),这篇文章中我想要分享一些我们在实战中耗费了许多时间才学到的东西。当然这些东西并不足以用来构建分布式数据库,但是希望它能够在你开发分布式数据库的时候能够节约你大量的时间。

第一个学到的知识就是从理论上理解哪一种计算容易被扩展(scale)。退一步讲,你可以用下面两种方法中的一种解决所有的分布式系统的问题:

  1. 将数据转化为计算(Bringing the data to the computation)
  2. 将计算转化为数据(Bringing the computation to the data)

举个例子,假设我们希望将销售的数据存到分布式存储中,你希望获得全部的收益。可以用这两种方法中一种回答这个简单的问题,我将用sql来描述他,但是这个思想可以应用到任何查询语言。

first diagram

第一幅图的优势就是简单,对于所有的计算机你都可以将数据拉到一个结点,然后在这些数据上运行最初的查询。缺点也很明显,就是不能很好的扩展。原因是在数据传输过程会导致许多的网络的I/O,而网络是最难扩展的部分;还有一点是,所有的数据在单个结点上计算机,你无法并行任何操作。

second diagram

第二幅图解决了两个可扩展的问题。我们同时的并行计算,并且也将计算结果通过网络传输。最后还要对最终的结果进行求和,但是这个已经非常简单了。

下面来看一个复杂一些的例子,我们要求订单的平均值。这个问题中我们能否在每个结点求平均值,然后在所有结果中求平均呢?

当然不行,我们需要每个结点计算机他们的sum(order)和count(order_value),然后在协作结点上计算sum(sum()/sum(count()))。这是一个简单的转化,但是我们为啥要现在做这个呢?

换句话说,为什么对查询结果求和的并行操作比对求平均的并行操作要简单呢?因为有些计算本身就是符合交换律的,有些却并没有(a+b = b+a; a/b != b/a)[1]

当你将你的计算机操作推给你的数据的时候,实际上做的就是将你的计算机转化为符合交换律的形式。这些观察到的显现,如何帮助我们g构建分布式系统呢?

如果你构建一个数据库,你可能有一个查询语言,将每个查询操作解析为一种逻辑上形式。而交换律对于这些查询语言来说都会有很大的好处。下面我用SQL语言来作为例子,因为 SQL有着广泛的应用,而且在Citus,我们有着将它scaling的经验。

SQL使用关系代数来解释查询。当你发送一个SQL命令的时候,Query将会被解析然后转化为一个逻辑的查询树。举个例子,如果你的Query包含了一个WHERE语句,那么他将会成为这个查询数上的过滤结点(filter node)。如果query有聚合函数,那么将会被解析为扩展操作结点(Extended Operator node)。

对SQL到关系代数的映射以及关系代数操作符不是我们的重点,所以我们不会深入讨论这些,而是在分布式系统中使用这些关系代数操作符。先看一下下面的SQL语句:

SELECT sum(price) FROM orders, nation
WHERE orders.nation = nation.name AND
      orders.date >= '2012-01-01' AND
      nation.region = 'Asia';

这个简单的查询join了sales表和nation表,然后查询对应的记录。然后在所有的结果里面计算销售的总额。现在让我们看看这个query的分布式的罗辑查询的计划。

plan1

乍一看这棵树就像标准的关系代数节点树[2],如果你仔细看,你会发现这个树的叶子节点附近有两个Collect节点。这个新增加的操作符类型用来收集他下方的数据到一台机器。

联系到博客的开头,这些Collect结点,将两个表的数据拉到一个地方。然后query树的剩余部分将会在这些数据上执行join、filtering、和计算。

但是我们已经知道了,它并不能很好的扩展。现在让我们将所有的关于网络IO scaling、交换律操作、关系代数的知识集合起来。Collect操作符能够和Filter操作符双向交互吗。

实际上这里有一种简单的方案可以优化这个罗辑,将Collect结点往上移,下放计算结点。直到符合交换律和分布式的属性[3]。下图展示了我们应用这个优化后罗辑查询方案:

plan2

这个优化后的方案有许多计算被下放到了query树中,仅仅需要采集一小部分数据。所以允许了可扩展性,更重要的是这个罗辑方案形式化了如何将关系代数在分布式系统中扩展以及为什么这样。

在我过去开发分布式数据库的过程中,发现最关键的一点就是:在分布式系统的世界中,交换律就是“国王”。对query建模的时候尊敬国王,那么就可以拥有可扩展性。

当然,构建一个分布式的数据库,这些是远远不够的,如果你只是对数据库的应用感兴趣,那么可以尝试使用一下CitusDB,v4.0将在下个月发布。如果你想构建一个分布式数据库,希望这篇博文对你有用。第二个希望分享的东西会在下一篇博文分享。

[1] 这篇博文中+操作符和sum()聚合函数可以互换,以简化问题。实际上这两个操作是相关的,而且在分布式关系代数中有不同的意义。

[2] 第二个不同是在这个罗辑方案中,分布式操作符能够操作一个或者多个数据片段。

[3] Join节点是一个二分操作符(Binary operator),所以,我们在优化查询方案涉及到Join的时候,利用他的分布式属性。

原文地址: https://www.citusdata.com/blog/2015/02/24/how-to-build-distributed-db/

comments powered by Disqus