数据库毁了所有好主意 - squarism


本文假设是一个三层网络堆栈。它有很多 Web 和应用程序服务器,但只有一个数据库框。你可以用云来代替它,但原理是一样的。我敢打赌你的基础设施看起来非常相似。对于本文的其余部分,假设我说的数据库是指传统的 RDMS。
为什么数据库总是一个?
我们可以总结在整个堆栈中扩展每一层,如下所示:

  • 客户端:不是我们能左右干预的问题,会有数以百万计的客户端
  • web 层:easy-peasy,它是一个守护进程加上一个配置文件
  • 应用层:这是我们的代码/东西;它在负载均衡器池中,即使它有状态我们也有扩展它的技巧
  • 数据库层:不要碰它!只有一个!

 除了数据库之外,每一层都很容易理解横向扩展。这里发生了什么?我将讨论一些好主意以及它们为什么会死在数据库层上。
  
让我们负载平衡?
负载均衡器池非常适合没有状态的层。当您需要有某种状态时,您可以使用诸如粘性会话session之类的技巧。但生命周期很短。保存这些状态在数据库不是好的想法,因为连接很长而且整个状态都只是本地的,只与某个客户端有关,如果客户端成千百万会消耗很多数据库空间。您不能将数据库卷放在共享驱动器上并期望它工作(很好)。所以问题至少是有关状态的。
 
让我们DOCKERIZE?
Docker 非常适用于没有状态的层。您可以对数据库进行 dockerize,但无法获得其他层的可扩展性和一致性优势。Docker 非常适合生产和部署,但您通常不会在没有很多花哨的正常运行时间工具的情况下部署数据库。
关于 dockerizing 其他层的话题和争论很少。Dockerizing 数据库层可以争论。
 
让我们多主双活?
水平扩展不适用于数据库层。您不能轻易拥有读/写(活动)对。有替代的守护进程和方法 (NewSQL),但这里我指的是常见的关系 SQL 数据库。
 
让我们做不可变或配置管理?
NixOS 呢?或者其他一些热门和时尚的新想法?当我听说 NixOS 时,我的第一个担忧和问题是关于数据库层。我问过这个关于 NixOS 的问题,显然可以这样做。但是,我并不完全理解这一点,但我想这是我观点的一部分。数据库层又是一个特例。
 
让我们模拟测试我们的数据库?
在单元测试期间,只能模拟测试应用服务器。那么为什么人们不能测试数据库?
你可以在 Python 中找到 fakeRedis 适配器,在 Ruby 中找到假缓存,在 C# 中找到内存数据库。但这一切都被警告所包围。创建测试数据库更容易,因为数据库会破坏所有好的想法。
关系数据库中有如此多的状态和来回协议,将其视为客户端/服务器消息传递太简单了。所有的状态和数据都存在于数据库中。即使是触发器和内部结构也太复杂而无法解释。创建测试数据库更容易,因为数据库命名空间/集合非常容易创建。数据库还具有在事务中回滚的优势,这对于单元测试非常多余。
  
让我们使用云?
租用大箱子通常在经济上没有意义。上面的数据库缩放和池化问题不会改变。即使是 SaaS 也有同样的问题。在这种情况下,奇异一个数据库盒子只是移动到云中而已。
 
一个可怕的故事
很久以前,我在一个需要大量专业软件、硬件、管理和配置的 Oracle 集群上工作。几乎整个想法都是关于可用性和性能的。CEO 无法忍受系统的一半因为只读而浪费金钱。他想在两个节点上读写:多主双活。这是很久以前的事了,但 CAP定理 是无法被改变的。我主要是通过这些创伤学到了很多关于裂脑的知识。
当时,您不能只下载一个可以进行水平扩展的关系数据库。您必须购买所有这些供应商选项和东西。这是超级昂贵的。我忘记了价格,db 许可证可能是 4 万美元,集群插件可能是 2 万美元。然后您需要专门的磁盘和卷软件。硬件也非常昂贵,因为当时是 Sun公司的。
在集群安装期间,它会告诉您将交叉电缆插入专用 NIC。就像,你有 eth1 只是免费坐在那里,或者你必须为它购买 NIC。我想我们买了一个网卡。除非您执行此交叉操作,否则安装将无法进行。此外,您需要在 SAN 上设置仲裁磁盘以充当决胜局(稍后会详细介绍)。这条交叉电缆上的所有流量都是 SSH。它所做的只是通过 SSH 进行关系数据库协议。您无需进行数据分片或拆分,所以要么全有要么全无。始终完整的 ACID 协议。这就是为什么由于网络负载而需要专用 NIC 的原因。
所以你终于击败了 CAP 理论。你有你的双活Active-Active数据库,你根本不需要改变你的应用程序。
现在到了权衡,魔鬼的细节:ACID 意味着我们必须 100% 及时同步每次查询。这意味着所有节点上同步数据,这就是扩展节点如此糟糕的原因。您在第二个节点上获得了大约 50%同步数据,然后在第三个节点上获得了 +25%。它在第4个之后酒停止扩展。请记住,每个节点都非常昂贵。此外,您的神经系统就如同这条交叉电缆(实际上是一对)。如果我拿剪刀剪它会怎样?
好吧,db01 认为它已经完成。db02 认为它已经结束。但是 db02 认为 db01 不见了。db01 认为 db02 不见了。那么现在怎么办?如果写入同时进入 db01 和 db02,会发生什么情况?
db01:  foo=bar
db02:  foo=ohno

foo 应该是什么? 裂脑!
这就是您配置仲裁磁盘的原因。当集群失去仲裁时,就会出现仲裁磁盘的竞争。它在磁盘扇区的开始处写入一个幻数(甚至不像在磁盘 iirc 的正常部分),无论谁到达第二个,都会引起恐慌。现在你已经从脑裂中幸存下来了。但是出于任意原因,您甚至需要疯狂的共享磁盘技术才能做到这一点。
那是一段疯狂的时光,我应该在稍后的某个时候分享这一点,因为这是在制作一部恐怖片。这个故事中的很多技术都非常古老。但有些现在还没有改变。当我学习 Mongo 时,我从这种恐怖中获得了高度的背景知识,我不必经常问“是的,但为什么”。
回到过去,我们的 CEO 无法忍受让一半的硬件无所事事。他想让它参与进来。这不是一个“愚蠢的想法”。这是个好主意!很多人对数据库有很好的想法。
不过对我来说,数据库毁了所有的好主意。