当您将数据插入数据库并运行COMMIT时,您会期望它存在:从原子上讲,一致,隔离和持久,就像40年前Codd命令我们一样,而且很快。令人惊讶的是,它的复杂性令人注目,但是由于我不想在本文中羞辱MongoDB和Redis开发人员,因此在这里我不再赘述。
相反,我们正尝试从存储堆栈的角度了解数据库整天在做什么。
这是Brendan Gregg在各种迭代中展示的性能工具(没有EBPF)。我们对该图形中的各种蓝色,VFS和更低版本感兴趣。 blktrace提供了有关蓝色堆栈中发生的情况的最详细信息。这是一个I / O请求记录器。
不幸的是,blktrace工具占用大量时间。您可以在blkparse,iowatcher和seekwatcher之间进行选择,它们要么给您带来吸引力,并且很难读取ASCII转储,要么难以使用,并且无法启动。一位同事扔了Workload Analytics。这样您就可以实际看到发生了什么。
当您访问我们的Grafana并提出很好的要求时,您基本上可以将我们的数据库分为三个负载类别:“ 200 commit / s”,“ 2000 commit / s”和“ 20.000 commit / s”,因此“低写入负载” ,“繁忙的写负载”和“应该很快考虑他们正在做什么”的写负载。
我们以尝试将数据库的工作集保存在内存中的方式来构建数据库,尤其是面向客户的数据库。
任何事物的“工作集”都是您不久的将来需要的事物集。不幸的是,很难预测未来,因此我们定义为工作集的是“我们最近使用过的一组东西,因此希望在不久的将来再次使用”。工作量模式的变化。
在我们的数据库中,重要的是位于InnoDB缓冲池中的16 KB数据库页面。所以我们做的很简单:我们在内存上花钱,预热InnoDB缓冲池,然后从内存中提供数据。我们可以证明是可行的:InnoDB可以以memcache的速度从内存中进行点读取,这就是为什么我们不这样做首先使用大量的Memcache和Redis –它们无济于事。
我们还可以对数据库进行容量测试,我们会发现随着负载的增加,磁盘读取次数不会增加。数据是从内存而不是磁盘提供的。
因此,我们关心的是磁盘写入。由于没有共享的复制体系结构,因此在复制链的每个实例上它们都是相同的:每个链成员必须应用相同的更改集(从主副本到所有叶副本)。
在提交时,我们写出1 KB(或更多)的Redo Log。 O_DIRECT写入XFS时会发生这种情况,这几句话引起了很多思考。
因为它是O_DIRECT,所以写入已写入磁盘。不需要fsync()或fdatasync()或其他任何东西。
O_DIRECT写入还绕过了文件系统缓冲区高速缓存,这很好,因为这样可以减少内存压力,并降低分页数据库服务器的风险。通常,当这种情况发生时(内存压力迫使在mysqld上进行页面调度),您将死于可怕的问题,并且很难在延迟问题和SLO违反中调试死亡。
我们使用XFS。平均而言,XFS将ext4写入磁盘所需的时间是ext4的两倍,但是XFS写入数据的时间总是相同的-它是专门为降低抖动而设计的。另一方面,已知ext4每秒钟都有下降的尖峰和大量的提交等待时间,而当您以10.000个查询/秒的速度运行时,这确实非常糟糕。
当使用O_DIRECT打开文件时,XFS还能够在文件上具有多个并发写入器。与所有其他Linux文件系统不同,在这种情况下,它不会全局锁定文件的内存inode以保证原子写入。这样的功能对于数据库人员来说是非常宝贵的。
提交是用户必须等待的。完成后,数据库可以稍后(通常要晚得多)在后台执行实际数据写入。因此,我们真正关心的是实际的提交延迟。
真正的写入稍后在Checkpoint中发生。所发生的事情非常复杂,足以证明自己编写了自己的文章。但是,简而言之,我们通过一些非常大的线性写入将数据写入Doublewrite缓冲区,以防止页面被撕裂,然后我们将v()分散的I / O写入16K页面以进行适当的更新。
这与Postgres不同,这使我们避免了昂贵的真空运行。我们在这里做一个假设,那就是:回滚是很少的(在MySQL中是昂贵的)。
如果您想知道某个事物是否可以运行MySQL,以及运行的如何,您只需在O_DIRECT中随机写入16KB页面,然后看看会发生什么。与上述相比,这是一个很大的简化,但是令人惊讶的是,它可以作为预测MySQL速度和容量的模型。
为了查看实际的数据库对其磁盘有何作用,我去了200级的选定代表,并对其进行了blktrace记录。
事实证明,您无法blktrace一台磁盘数据库计算机,因为从blktrace写入磁盘会破坏数据库中的指标。还发现systemd将12GB ramdisk挂载为/ run / user /< uid>。可以保留10分钟的blktrace,没有问题。
红色低线是200 IOPS基准负载。事实证明,存在峰值,超过2000 IOPS。
我们要学习的第二件事:每秒可能有200个IO操作,但是甚至有峰值,甚至高达2000 IOPS。它们虽然不长,但经常发生。
那么写入磁盘需要多长时间?好吧,没有磁盘。有闪存,甚至没有磁盘接口,它直接位于PCI总线上。直接在PCI总线上的闪存驱动器称为NVME而不是SSD(否则为同一驱动器)。
闪存不是很快,但是NVME是:该驱动器不仅具有闪存,还具有(缓冲大电容器)非易失性存储器,用于在后台缓冲写入和重新排列内容。它还具有自己的ARM处理器和自己的操作系统。
有时我们需要稍等片刻,但是大多数时候它确实非常非常快。多快?我们需要更深入地研究:
30µs或0.03ms是通过PCI总线写入内存所花费的时间。
当我们与PCI总线另一侧的远程NVRAM进行通信时,这需要30µs或0.03ms。只要不使驱动器的NVRAM缓冲区不堪重负,我们在本地内存上的提交延迟就为30µs。
另一个数字,将在以后派上用场,是时候在同一个数据中心内跨我们的整个网络堆栈:大约有6个交换机和路由器,这将使整个网络中的任何往返行程再增加60µs,这是最好的案件。因此,远程闪存驱动器永远不会比100µs或0.1ms快。
当绘制“随着时间的推移写入的地址”时,您会看到有很多块被一次又一次地写入。这些是数据库使用的各种日志和写缓冲区。
该驱动器内部具有闪存转换层(FTL),可确保这些块重写实际上分布在您所拥有的闪存的所有位置上。这种磨损均衡技术可确保驱动器在闪光灯耗尽之前可以实际使用7-10年。
您在本地Raspi中使用的SD卡或USB记忆棒没有此功能,并且Tesla的娱乐模块中也没有板载闪存。这就是为什么它们会快速失败的原因,因为实际的固定闪存块已被重写了数千次。请使用具有适当FTL的适当驱动器,但不会发生这种情况。
在上面的图形中,您还可以看到检查点分散写入的发生,并且可以看到它们之间的间隔很大,成千上万的事务。通过检查点,数据库可以聚合内存页面(和日志,以保持持久性)中的更改。所有这些复杂性实际上可以节省您的磁盘写操作!
闪存没有磁盘驱动器那样的单个写头。他们没有被迫按顺序执行事务,但实际上,驱动器中的所有各种芯片都可以同时执行事务。深盘写入队列对于将所有这些通道馈送给NVME是必不可少的,而它们却是SSD和HDD的毒药。
诸如此类的低并发层次结构甚至没有达到单个NVME的真正写入潜力:如果您非常努力,单个驱动器每秒可以在单个线程中顺序执行每秒20.000磁盘写入操作,但是它可以完成800.000如果您以足够的并发速率进行写入,则每秒写入磁盘的次数。
这是另外两个需要牢记的重要数字。这就是“ IOPS配额”已成为过去的原因-任何合理的工作量都不可能真正耗尽单个NVME驱动器,而分布式存储通常有数百个。
让我们再次从上方查看延迟图,并检查延迟直方图中的完成时间分布。它告诉我们观察窗口中普遍存在缓慢写入:
因此,即使在时间延迟图上有这些限制,我们也可以看到90%的写入发生在30µs的窗口中,并且在> 100µs类中有值得注意的写入数量(它们确实存在,但它们如此罕见,它们不会显示在直方图中)。
我们可以看到,经过简化的16 KB Randwrite模型实际上并没有错,即使它过于简化也是如此。不过,更精确地建模不会对预测性能产生重大影响,因此,仅几行即可获取数据。
这就是我们从底面进行的200班之旅–这就是您的存储空间如何查看提交的内容。
我选择此特定数据库作为2000 Commit / s类的成员,也是因为它是我们拥有的最古老和最狂野的数据库复制层次结构。一次,这是我们的性能瓶颈,运行速度超过20.000次/秒,峰值达到32.000,并且当时的硬件根本不稳定。
的确如此,但请注意,即使是像这样的大怪兽,也可以完全将工作集保存在内存中。您也可以成为成功的数据库性能顾问:说“买更多的内存!”然后根据需要添加“缺少索引”(如果您在SAP或Oracle工作,则添加“这将非常昂贵”)。
我们可以看到固定延迟的水平延迟带。它们公开了我所不了解的Flash存储的内部结构。叽!
我们的确看到深绿色固定的延迟层频带,分别为30µs,120µs和220µs。较高的层随着时间的推移显示出重复的模式。这些是FTL的恶作剧,对于外部用户来说是可见的延迟,但它们远低于任何关键级别。我对Flash内部知识了解不足,无法解释这些事情,这使我感到困扰。很多。
我们的30µs,120µs和220µs频带作为直方图中的凸起。就担忧而言,任何低于500µs的时间都可能无关紧要。
所选示例是一个数据库队列,该数据库记录来自一个数据集的更改,以便在另一个数据集中处理和创建实例化视图。这是Fowlerspeak中CQRS的一个示例。
在采样过程中,它们以2500 commit / s的速度运行,峰值达到7500。硬件将对它们进行高达20.000的处理,有时队列也将执行。
事实证明,写入队列数据库使使NVME的队列保持最大的忙碌状态。
好吧,如果您一直坚持到这一点,您可能不会对写入延迟的延迟感到惊讶,并且可能还会看到它们反映了队列深度的变化,而无需我特别指出。
我们确实看到了显微镜下的窗帘,这是真的,但是请看一下所有这些下面的快速写入的深绿色带。
如您所见,在所有帘幕下方的快速写入的深绿色带中,该特定驱动器的FTL一直很忙。物有所值-当您为企业级闪存而非家用设备付费时,不会因内部重组而造成总延迟。
到目前为止,大多数写入仍在NVRAM驱动器端。 0.25ms以上,我们基本上是干净的。
平稳的客户体验,即使遇到像这样的队列数据库这样的滥用客户,也要启用它,我们可以接听您的意见。
直方图很有趣。例如,我们可以计算每个块地址的磁盘写操作:
在作为队列的数据库中,如果一切正常,则表会发生很大变化,但永远不会增长。组成小型队列表的实际数据页面很少,但是插入和删除却很多。我们必须在日志中将它们持久保存到磁盘上以成为ACID。但是几乎不需要检查点,即使那样,也几乎没有整页要写出来。
如果您走到了这一步,欢迎光临柜台并获得书呆子存储徽章。