在单台服务器上将SQLite扩展到4M QPS(EC2与裸机)

2020-05-05 19:38:22

Expensify在很多方面都有不同寻常的技术堆栈。例如,我们不在内部使用DNS-只是配置管理的/etc/hosts文件-它工作得很好。类似地,我们只有限地使用AWS-而不是托管我们自己的Web和数据库层硬件-它工作得很好。但最令人惊讶的是,我们没有使用MySQL或Postgres-而是使用SQLite-它工作得非常好。

诚然,它本身并不是SQLite。我们已经将它包装在一个名为Bedock的自定义分布式事务层中,该层是开源的,可以在这里获得:www.Bedckdb.com。它不仅是我们核心数据库背后的主力,而且还为我们的任务关键型作业队列提供动力,并提供一个本地托管的复制缓存层。功能越重要,它对Bedrock的依赖就越大。

因此,考虑到2017年的爆炸性增长,升级驱动基岩的服务器一直是当务之急--同时升级基岩(在不可思议的SQLite团队和SQLite本身的帮助下),以利用所有这些硬件。为此,我们即将推出全新的一代自托管硬件,其基本规格如下:

这是一个庞然大物的服务器,几乎是在现货Linux内核上运行的最大的服务器(没有进入“超级计算机”领域),我们正在得到一大批这样的服务器。但仅有硬件是不够的。需要明确的是,以上规格对于大多数数据库来说是毫无意义的,因为几乎没有任何可伸缩的东西可以很好地处理这种硬件-而且几乎没有人尝试。

传统的智慧是,你应该总是“向外”而不是“向上”,这意味着拥有大量的小服务器比相对较少的大型服务器更好。大多数公司会将他们的数据“分割”到单独的服务器中,比如在每台服务器上放置1000名客户-然后在客户登录时只需将每个客户路由到正确的服务器即可。这种设计对许多应用程序都非常有效,这意味着你永远不需要学习如何管理大型数据库-只需要很多小型数据库。

Exexpsify有点不同,因为没有分片-也没有明确的“断层线”来做这件事。“每一个用户都可以与其他每个用户分担费用,无论他们在哪里工作。”这是一个关键功能,使我们能够为管理数百甚至数千客户的大型会计师事务所提供如此好的支持:当一家公司与一家公司竞争时,我们希望让该公司访问该公司的数据,而不需要将公司迁移到会计师事务所已经在使用的同一服务器上。(这是一个关键功能,使我们能够为管理数百甚至数千客户的大型会计师事务所提供如此好的支持:当一家公司与一家公司竞争时,我们希望让该公司访问该公司的数据,而不需要将该公司迁移到该会计师事务所已经在使用的同一服务器上。

换句话说,作为一名会计师,你的Expensify“收件箱”向你展示了你需要同时在所有客户身上做的所有事情,而不需要从一个客户“切换”到另一个客户,看看是否需要为每个客户做什么。当然,即使你进行了分片,也有其他方法可以做到这一点。但是,由于没有分片,这以及其他一系列类似的多客户处理变得超级容易。

显然,SQLite从来不是为做这样的事情而设计的。但这并不能改变这样一个事实,即它做得非常好,尽管SQLite多年来应我们的要求做了一些修改(我们是SQLite联盟非常热情的赞助商,他们给了我们很大的帮助)。这些修改是开源的,任何人都可以使用(只要问他们,我相信他们会把你联系起来),而且基本上不在本文的讨论范围内,但包括(如果我都记得的话):

禁用POSIX建议锁。这会阻止您在数据库运行时从外部进程(例如,从命令行工具)访问同一数据库,但这对于我们的使用是可以接受的。

禁用malloc()全局锁(我不记得为什么会有这样的锁,但对于我们的情况显然不需要)。

换句话说,SQLite的核心设计非常准确。我认为人们没有意识到它是一个多么不可思议的工具。相反,优化系统性能的大部分努力归结为优化BIOS和内核功率设置,以消除无关的内存访问(例如,禁用预取),并防止CPU自行节流以节省电力。

人们普遍认为EC2比托管你自己的硬件更快、更便宜、更容易。也许我是老派,但我从来都不完全认同这个观点。你在EC2服务器上可能得到的最好价格是预付3年合约的一年,但你在第一天支付的价格仍然等于硬件成本。因为想想看:亚马逊不会在你身上冒险,所以他们不会为你购买硬件,除非他们预付了钱,否则他们不会为你购买硬件,因为亚马逊不会在你身上冒险,所以他们不会为你购买硬件,除非他们预付了钱,否则他们不会为你购买硬件,因为亚马逊不会在你身上冒险,所以他们不会为你购买硬件,除非他们预先付款。他们相信他们会得到很多倍的回报。因此,对于同样的硬件,你拿出了同样的现金,除了不是得到5年的稳定使用-你需要再支付同样的钱5次(因为与手机不同,服务器硬件在3年后不会变成南瓜)。EEC2的溢价令人震惊-可能是实际硬件成本的3-10倍-如果你这么看重便利性,这是可以接受的。但鲜为人知的是,它也会带来巨大的性能损失。

在撰写本文时,您可以买到的最大的ec2实例是具有128vCPU和4TB内存的3x1e.32xLarge。它的价格为每小时26.688美元,即每台服务器每年233K美元。)(如果您承诺使用3年,并预付1年,则可以“仅”花35万美元购买3年。)以下是该服务器与您可以托管的服务器的比较结果,预付成本和持续成本都很低:“只需”35万美元。(如果您承诺购买3年,并预付1年的费用,则可以“仅”花35万美元购买3年的服务器。)下面是该服务器与您可以托管的服务器的比较结果,前者的预付成本和持续成本都只有很小的一部分:

纵轴显示每秒查询的总数,横轴显示获取该数据点所使用的线程数。对于每个线程计数配置(例如,一个线程为100s,两个线程为100s,等等),测试运行100s,并使用最快的单个秒作为结果。(这一点很重要,因为CPU缓存“预热”可能需要一段时间,这会过滤掉人为缓慢的样本。)完整的性能测试可以在GitHub上找到。(这很重要,因为CPU缓存需要一段时间才能“预热”,这会过滤掉人为缓慢的样本。)-完整的性能测试可以在GitHub上找到。(这一点很重要,因为CPU缓存需要一段时间才能“预热”,这会过滤掉人为缓慢的样本。)。

查询本身只是对随机整数的10B行(447 GB)两列数据库中随机放置的10行的范围求和,其中一行已编制索引,另一行未编制索引。*这意味着测试已完全缓存在RAM中,而数据库在测试之前已预缓存。*在此测试期间不执行写入,只执行读取操作。*每个线程都支持NUMA,这意味着其所有本地内存访问都在本地内存节点内完成,数据库本身在NUMA节点之间拆分(尽管与预期相反,NUMA感知不会。

橙色线条显示EC2机器的总聚合性能,上限约为每秒150万次查询。蓝线显示在“裸机”机器上进行的相同测试,每秒查询次数超过4M次,并且在测试期间不断攀升。(出于测试目的,我只关心192个线程,所以我不担心线程数会更高。)红线显示的是同样的测试,只不过是30B的行表(1.3TB)--比裸机的物理RAM还大。

首先,哇。这是一吨的马力。一台服务器每秒进行400万次查询是不容小觑的,即使认识到这是一个很难测试的人工查询。)(如果你测试“SELECT 1”,它每秒会获得大约1.6亿次查询,但这更是人为的。)(如果你测试“SELECT 1”,它每秒会得到大约1.6亿次查询,但这更是人为的。)。

其次,SQLite的伸缩性令人惊叹,几乎开箱即用。这项测试在添加物理CPU时实现了近乎完美的线性可伸缩性,超线程在释放额外容量方面的效果比我预期的要好得多。

但第三,wtf与EC2有问题?我真的认为1个vCPU大致等于1个物理CPU,但MAN错了。这一点在这里真正突出显示:

这是相同的基本图表,但这一次显示了每个线程的平均性能,以及该性能是如何随着线程数量的变化而变化的。蓝线显示的几乎与我预期的一样:当新的物理核心被激活时,每个线程的性能保持稳定(非常明显),但当我们达到超线程区域时,性能开始下降。事实上,这比我预期的线性程度要高得多,因为测试的设计方式确保每个CPU 87.5%的时间都在访问远程RAM。这意味着这几乎是最糟糕的情况。但这似乎丝毫没有降低测试的性能,看起来英特尔可以做对一些事情,而Purley的新内存架构相当稳固!

此外,红线显示,即使25%的读取进入磁盘,对总体性能的影响也可以忽略不计。我将此归因于NVMe固态硬盘的疯狂快速访问-同样,远远好于预期。

但橙色的线条显示,EC2机器上的每个新线程都会对其他线程造成很大的惩罚。首先,这个图表非常明显地显示,128个“虚拟CPU”实际上意味着64个物理CPU+超线程。*这相当令人失望和误导,因为我真的认为每个vCPU都意味着同等的性能。

然而,更重要的是橙线本身的曲线。当用单行数据库替换100亿行数据库后重新运行测试时,更容易看到这一点,以便从等式中删除内存访问:

(请原谅参差不齐的数据,这是以每个样本较短的周期运行的,因此噪音较大,但它让人明白了这一点。)

蓝线显示了添加新线程时裸机上每个线程的QPS,正如您所预期的那样,它获得了高达192个物理核心的完美线性可伸缩性,然后随着超线程开始发挥作用,QPS线性下降。

红线是EC2的盒子,性能甚至在达到64个物理核心之前就会下降,但在达到64个物理核心之前就会急剧下降。更糟糕的是,这条线的曲线表明它不是内存访问减慢了速度-这是电源节流。*一旦超过64个核心并开始超线程,每个CPU消耗的功率就会上升。但很明显,EC2已经将其配置为某种“平衡”的性能配置文件,这意味着当总功耗变得太高时,CPU的时钟速度会降低-从而限制了CPU的运行速度。但很明显,EC2在达到64个物理核心之前就会降低性能,这意味着当总功耗过高时,CPU的时钟速度会降低,从而限制了。

这会导致每个线程的性能-计数曲线,在128个线程处趋于平缓的线性下降斜率,因为这是所有128个CPU都在使用的点。“一旦所有CPU都被使用了,线程就会竞争一个插槽-添加更多的线程不会消耗更多的电力(但它是线性的,因为每个新线程都会占用现有线程的时间)。

事实证明,托管您自己的硬件的一个主要隐藏优势是,它意味着您可以配置BIOS以满足您自己的需求,在我们的特定情况下,这些更改就是一切。

购买功能强大的服务器价格低得惊人(只要您托管的是您自己的硬件)。

并不是所有的EC2虚拟CPU都是相同的,而且它们都不像您自己托管的实际CPU那样强大。

所有这些测试都是在Meltdown和Spectre之前完成的,所以我不确定这项测试的性能会受到什么影响。“我们现在正在进行这些测试,如果有人感兴趣,我会写下来分享结果。”早期迹象显示,性能大约会下降2.5%,但还有更多的工作要做。

此外,虽然这项测试的重点是读取性能,但我应该提一下SQLite也有出色的写入性能。*默认情况下,SQLite使用数据库级锁定(最小并发),并且有一个“开箱即用”的选项来使WAL模式获得出色的读取并发性--如此测试所示。但是不太为人所知的是,SQLite有一个分支具有页面锁定,这可以实现出色的并发写入性能。如果您联系SQLite人员,我相信他们会告诉您更多!