垂直分片很糟糕

banq


垂直分片会显著增加查询复杂度,并可能影响性能。本文将介绍团队最初考虑拆分数据库的典型原因。您将了解意外的复杂性、它如何影响架构,以及如何避免陷入困境。这提醒您在以可能适得其反的方式切分数据之前务必谨慎。如果您曾梦想通过“仅拆分表”来实现扩展,那么这篇文章不容错过。

垂直分片(有时也称为功能分片)会将表从主数据库中取出并放置在其他位置。大多数情况下,会将表放置在另一个 Postgres 数据库中。这可以减轻主数据库的负载,并为您的应用提供一些增长空间。

这也会把你的应用弄得支离破碎,让你的后端工程师感到难过。难过的工程师会毁掉你的产品路线图,毁掉你开发产品的希望。

虽然数据库是分片的,但应用程序却没有
表(应用程序)从未真正被分割。即使在理想情况下,同一家公司开发的两个应用程序也需要相互通信。当然,这可以通过 HTTP 实现,但这并不能改变一个应用程序实际上依赖于另一个应用程序的事实。因此,虽然数据库是分片的,但应用程序却没有。

向 Kubernetes 集群添加更多容器会让您的 SLA 提升更多个 9。添加更多数据库则会产生相反的效果。这有点违反直觉,但原因如下:容器是独立的,而数据库不是。因此,如果您的应用需要两个数据库来处理请求,那么两个数据库都必须正常工作。

垂直分片
正常运行时间公式
计算正常运行时间的方法是取 100% 的概率,然后减去系统中每个组件的故障概率。如果您的数据库在 RDS 中,它保证数据库在 99.95% 的时间内正常运行。

因此,如果您只有一个数据库,那么您应用的 SLA(理想情况下)为:
100% - (100% - 99.95%) = 99.95%

从 100 中减去 0.05 的方法有点奇怪,但原因如下。计算两个独立系统的正常运行时间时,两个系统同时发生故障的概率会相乘。因此,如果您有两个完全独立的数据库,并且您的应用可以使用其中一个(可以理解为副本),那么您的 SLA 就是这两个系统同时发生故障的概率:
100% - ((100% - 99.95%) × (100 - 99.95%)) = 99.9975%

这就是为什么向数据库添加副本(或向 Kube 集群添加容器)可以为您提供两个 9 的正常运行时间。

现在,有了垂直分片数据库,需要两个(或更多)服务器才能处理请求。因此,任何一个服务器发生故障都可能导致整个应用程序下线。这会对我们的公式进行如下修改:
100% - ((100% - 99.95%) + (100 - 99.95%)) = 99.90%

仅仅添加一个垂直分片,你的正常运行时间就减少了 0.05%。虽然这个数字看起来很小,但你的客户想要的是两个 9,这足以让你的应用每年都超出 SLA 的保障范围。

垂直分片很容易上瘾。一旦开始,就停不下来。你会不断地删除表,因为以前用过,而且并不难。这样一来,你的正常运行时间就会减少,事故也会增多,而这仅仅是麻烦的开始。

您的代码已损坏
嗯,不完全是。但它确实会让你讨厌使用它。我可以告诉你原因,但我更愿意展示给你看。你一开始是这么开始的:

user_with_products = User.where(id: user_id).joins(:orders, :products)

您现在的位置是:

Users database
user = User.find(id: user_id)

Orders database
orders = Order.where(user_id: user_id)

product_ids = []
for order in orders
  product_ids << order.product_id
end

Products database
products = Product.where(id: product_ids)

products_by_order_id = {}
for product in products
  if products_by_order_id.include? product.order_id
    products_by_order_id[product.order_id] << product
  else
    products_by_order_id[product.order_id] = [product]
  end
end

如果你仔细阅读,你可能已经发现了几个错误。我第一次没有发现……第二次也没有。但这就是我们必须处理的情况,我们无能为力。这些模型位于不同的数据库中,我们不能让数据库做它最擅长的事情:连接记录并进行搜索。

现在我们的应用层已经承担了责任,但我们就是无法正确处理那些代码。我不怪你,集合论很难,数据库几十年来一直在优化它。现在我们必须每次都这样做,针对每个端点,永远如此。也许几年后我们会有所改进。

你可能会想:“那又怎么样?”我的应用上线了,基本可以正常工作了,数据库也不再崩溃了。这感觉太棒了。这就是为什么垂直分片被一次又一次地采用。它确实名副其实。

但真正让人头疼(或者即将让人头疼)的是产品路线图。像从数据库获取数据这样简单的事情现在都变得很困难。它们很烦人,而且过了一段时间,你就不想再做了。于是你开始走捷径,比如在数据库之间复制数据,只是为了恢复连接。你不再真正关心数据库扩展,你只想完成你的工作。

你开始编写“服务”,将这些应用层连接封装在 HTTP(或 gRPC)API 后面,并在应用程序中的多个位置调用它们。你只是将数据库故障的概率添加到应用程序代码中,并将延迟增加了两倍。

你只能这么做了。你的功能已经落后计划好几个月了,你的项目经理还以为你去哪儿了,而不是在工作呢。可笑的是,你真希望自己在那儿呢。

我无意指责你。文中的“你”并非指你,而是我。所有这些我都做过。作为一名应用工程师和数据库工程师。最糟糕的是?我做了好几次。如果你今天问我,我还会不会再垂直分片……说实话,我也没法肯定地告诉你。

哪里出了问题
Postgres 生态系统没有 OLTP 分片解决方案。我们不像 MySQL 的 Vitess、DynamoDB 或 Cassandra 那样“酷”。我们更务实一些,热爱 Postgres,认为它无所不能。事实也的确如此,除了分片部分。


这就是 PgDog 的目标。它或许不会让我们变得很酷,但当我们的业务取得成功时,它不会让我们的错误率翻倍,而是会让我们的生产力提高 10 倍。

PgDog 是一个用于 Postgres 分片的开源项目。我们正在寻找早期设计合作伙伴和贡献者。请给我们点个星GitHub