⚡ 现在提供具有无限 IOPS 的极速 NVMe 驱动器。了解 PlanetScale Metal

Blog|Engineering

目录

想了解更多关于 Metal、Vitess、水平分片或企业版选项的信息吗? 与解决方案团队联系 获取 RSS feed

IO 设备与延迟

By Benjamin Dicken | March 13, 2025

CPULocal NVMe (~20μs)EBS io2 BX (~400μs)EBS gp3 (~1ms)pd-balanced (~1ms)

非易失性存储是现代计算机系统的基石。 每一个现代照片、电子邮件、银行余额、医疗记录和其他关键数据都保存在数字存储设备上,通常会多次复制以提高耐用性。

非易失性存储,或者通俗地称为“磁盘”,即使连接的计算机断电,也可以存储二进制数据。 计算机有其他形式的易失性存储,例如 CPU 寄存器、CPU 缓存和随机存取存储器,所有这些都更快,但需要持续供电才能运行。

在这里,我们将介绍计算历史上非易失性存储设备的历史、功能和性能,所有这些都使用有趣且交互式的可视化元素。 本博客是为了庆祝我们最新的产品发布而撰写的:PlanetScale Metal。 Metal 使用本地连接的 NVMe 驱动器来运行你的云数据库,而不是大多数云数据库提供商使用的速度较慢且不太一致的网络连接存储。 这带来了极快的查询、低延迟和无限 IOPS。 查看文档以了解更多信息。

磁带存储

早在 1950 年代,计算机就使用磁带驱动器进行非易失性数字存储。 多年来,磁带存储系统已经生产出多种外形尺寸,从占据整个房间的磁带存储系统到可以放入口袋的小型驱动器,例如标志性的 Sony Walkman。 磁带阅读器是一个包含专门设计用于读取磁带盒的硬件的盒子。 插入磁带盒,然后将其展开,这会导致磁带在 IO Head 上移动,IO Head 可以读取和写入数据。

p0p1p2p3p4p5p6p7p8p9p10p11p12p13p14p15p16p17p18p19p20p21p22p23p24p25p26p27p28p29p30p31p32p33p34p35p36p37p38p39p40p41p42p43p44p45p46p47p48p49Take-up reelI/O headTapeCartridge

虽然磁带在 70 多年前就开始用于存储数字信息,但它至今仍用于某些应用。 标准 LTO 磁带盒有数百米的 0.5 英寸宽的磁带。 磁带具有沿其长度延伸的若干磁道,每个磁道进一步划分为许多小单元。 单个磁带盒包含数万亿个单元。

每个单元的磁极化都可以设置为向上或向下,对应于二进制 0 或 1。 从技术上讲,两个单元之间转换产生的磁场才是产生 1 或 0 的原因。 磁带上一长串比特形成一个数据页。 在磁带阅读器的可视化中,我们通过将磁带显示为简单的数据页序列来简化这一点,而不是显示单个比特。

当需要读取磁带时,它会装入阅读器中,有时是手动装入,有时是机器人装入。 然后,阅读器使用其电机旋转磁带盒,并使用阅读头读取磁带经过下方的二进制值。

在下面的交互式可视化中尝试一下(速度已大大降低)。 如果你希望磁带更快或更慢,可以控制磁带的速度。 你还可以发出读取请求和写入请求,然后监视这些请求所花费的时间。 你还可以在左上角看到待处理 IO 操作的队列弹出。 尝试发出一些请求以了解磁带存储的工作原理:

速度选择器

如果你花足够的时间,你会注意到:

  1. 如果你读取/写入“靠近”读取头的单元,则速度很快。
  2. 如果你读取/写入“远离”读取头的单元,则速度很慢。

即使使用现代磁带系统,读取磁带上很远的数据也可能需要 10 秒的时间,因为它可能需要旋转磁带数百米才能到达所需的数据。 让我们比较两个更具体的交互式示例,以进一步说明这一点。

假设我们需要读取总共 4 页并写入另外 4 页的数据。 在第一种情况下,我们需要读取的所有 4 页都在一个整洁的序列中,并且要写入的 4 页紧随读取之后。 你可以在左上角的白色容器中看到排队的 IO 操作。 继续并单击“Time IO”按钮以查看此操作,并观察完成所需的时间。

启动并计时 IO 队列 Time IO ... p0p1p2p3p4R page 1R page 2R page 3R page 4W value 10W value 20W value 30W value 40

正如你所看到的,这大约需要 3-4 秒。 在实际系统中,IO 头可以更快地运行,并且电机可以更快地驱动线轴,因此速度会更快。

现在考虑另一种情况,我们需要读取和写入相同数量的页面。 但是,这些读取和写入分布在整个磁带中。 继续并再次单击“Time IO”按钮。

启动并计时 IO 队列 Time IO ... p1p2p3p5p6p7p9p10p11p13p14p15p17p18p19p21p22p23p25p26p27p29p30p31p33p34p35p37p38p39p41p42p43p45p46p47p49R page 10W value 10R page 41W value 20R page 21W value 30R page 43W value 40

对于相同的读取和写入总数,这花费了大约 7 倍的时间! 想象一下,如果此系统用于加载你的社交媒体提要或你的电子邮件收件箱。 可能需要 10 秒甚至整整一分钟才能显示。 这是完全不能接受的。

虽然随机读取和写入的延迟很差,但磁带系统在以长序列读取或写入数据时运行良好。 事实上,在现代技术世界中,磁带存储仍然有许多这样的用例。 磁带特别适合需要大量存储空间的情况,这些存储空间不需要经常读取,但需要安全地存储。 这是因为磁带的每 GB 成本更低,并且保质期比其竞争对手更长:固态硬盘和硬盘驱动器。 例如,CERN 有一个磁带存储数据仓库,管理着超过 400 PB 的数据。 AWS 还提供磁带存档作为一项服务。

磁带不适合的是高流量事务数据库。 对于这些和许多其他高性能任务,需要其他存储介质。

硬盘驱动器

存储技术中的下一个重大突破是 硬盘驱动器

我们不是将二进制数据存储在磁带上,而是将它们存储在一个称为 盘片 的小型圆形金属磁盘上。 此磁盘放置在带有特殊 读/写头外壳 内,并以非常快的速度旋转(例如,7200 RPM 很常见)。 像磁带一样,此磁盘也分为磁道。 但是,磁道是圆形的,单个磁盘通常有超过 100,000 个磁道。 每个磁道包含成千上万个页面,每个页面包含 4k(左右)的数据。

d2d3d5d6d7d10d11d12d13d14d16d17d18d20d21d22d23c1c2c4c5c6c7c8c9c10c11c12c13c15c16c17b1b2b3b5b6b8b9b10b11b12a1a2a3a4a7EnclosureReaderPlatterTracks

HDD 需要读者和盘片的机械旋转运动才能将数据移动到正确的位置以进行读取。 HDD 优于磁带的一个优点是,比特的整个表面积 100% 可用。 移动针头 + 旋转磁盘到正确的位置以进行读取或写入仍然需要时间,但它不需要像磁带那样被“暴露”。 结合有两个不同的东西可以旋转的事实,意味着可以以更低的延迟读取和写入数据。 典型的随机读取可以在 1-3 毫秒内执行。

下面是一个交互式硬盘驱动器。 如果你希望盘片更快或更慢,可以控制盘片的速度。 你可以请求硬盘驱动器读取一个页面并写入到附近可用的页面。 如果你在上一个请求完成之前请求读取或写入,将建立一个队列,并且磁盘将按照收到的顺序处理请求。 与以前一样,你还可以在白色 IO 队列框中看到待处理 IO 操作的队列。

速度选择器

与磁带一样,盘片旋转的速度也降低了几个数量级,以便更容易看到发生了什么。 在实际磁盘中,还会有更多的磁道和扇区,足以在某些情况下存储多个 TB 的数据。

让我们再次考虑一些特定情况,以了解读取和写入的顺序如何影响延迟。

假设我们需要写入总共 3 页的数据,然后在之后读取 3 页。 三个写入将发生在附近可用的页面上,读取将来自磁道 1、4 和 3。 继续并单击“Time IO”按钮。 你会看到请求到达队列,读取和写入得到满足,然后在最后看到总时间。

速度选择器

由于大多数这些操作的顺序性质,所有任务都能够快速完成。

现在考虑相同的 6 个读取和写入集,但它们的交错顺序不同。 继续并再次单击“Time IO”按钮。

速度选择器

如果你有耐心等到最后,你应该注意到相同的读取和写入总数花费了更长的时间。 花费了大量时间等待盘片旋转到读取头下方的正确位置。

长期以来,磁盘直接在磁盘上支持命令排队(80 年代使用 SCSI,2000 年代使用 SATA)。 因此,OS 可以发出并行运行且可能乱序运行的多个命令,类似于 SSD。 如果磁盘可以建立一个操作队列,磁盘控制器可以调度读取和写入以针对磁盘的几何形状进行优化,磁盘的性能也会提高。

这是一个可视化效果,可以帮助我们看到随机磁带读取与随机磁盘读取的延迟差异。 随机磁带读取通常需要几秒钟(我在这里放了 1 秒以慷慨),而磁盘头寻道需要接近 2 毫秒(千分之一秒)

比实时慢 250x slower CPUtape (1s)local HDD (2ms)

即使 HDD 比磁带有所改进,但在某些情况下,它们仍然“慢”,尤其是在随机读取和写入方面。 下一个重大突破,也是目前事务数据库最常见的存储格式,是 SSD。

固态硬盘

固态存储,或“闪存”存储,于 1980 年代发明。 即使在磁带和硬盘驱动器主导商业和消费存储领域时,它也已经存在。 由于技术限制和成本,直到 2000 年代,它才成为消费存储的主流。

SSD 优于磁带和磁盘的优势在于,它们不依赖任何机械组件来读取数据。 所有数据都使用一种称为 NAND 闪存的特殊类型的非易失性晶体管以电子方式读取、写入和擦除。 这意味着每个 1 或 0 都可以读取或写入,而无需移动任何物理组件,而是 100% 通过电气信号。

SSD 被组织成一个或多个目标,每个目标包含多个,每个块包含一些页面。 SSD 在页面级别读取和写入数据,这意味着它们一次只能读取或写入完整页面。 在下面的 SSD 中,你可以看到通过控制器和目标之间的线路(也称为“迹线”)发生的读取和写入。

PRNEZIControllerLinesTargetBlocks

消除机械组件减少了发出请求和驱动器可以满足请求之间的时间延迟。 不再需要等待任何东西旋转。

我们在视觉效果中显示了小例子,以便更容易理解,但单个 SSD 能够存储多个 TB 的数据。 例如,假设每个页面保存 4096 位数据 (4k)。 现在,假设每个块存储 16k 页,每个目标存储 16k 块,并且我们的设备有 8 个目标。 这等于 4k * 16k * 16k * 8 = 8,796,093,022,208 位,或 8 TB。 我们可以通过添加更多目标或在每个块中打包更多页面来增加此驱动器的容量。

这是一个可视化效果,可以帮助我们看到 HDD 与 SSD 上的随机读取延迟差异。 SSD 上的随机读取因型号而异,但可以快至 16μs(μs = 微秒,即百万分之一秒)。

比实时慢 25000x slower CPUHDD (2ms)SSD (16μs)

人们很容易认为,随着机械部件的移除,SSD 上数据的组织不再重要。 由于我们不必等待东西旋转,我们可以以完美的速度访问任何位置的任何数据,对吗?

不完全是。

还有其他因素会影响 SSD 上 IO 操作的性能。 我们不会在此处介绍所有因素,但我们将讨论的两个因素是并行性垃圾回收

SSD 并行性

通常,每个目标都有一条从控制单元到目标的专用线路。 此线路是处理读取和写入的线路,并且每条线路一次只能通信一个页面。 页面可以在这些线路上进行非常快速的通信,但这仍然需要一小段时间。 数据的组织以及读取和写入的顺序对这些线路的效率有显着影响。

在下面的交互式 SSD 中,我们有 4 个目标和一组 8 个写入操作排队。 你可以单击“Time IO”按钮,以查看当我们可以在并行使用线路以写入这些页面时会发生什么。

速度选择器

在这种情况下,我们写入了分布在 4 个目标中的 8 个页面。 因为它们是分散的,所以我们能够利用并行性在两个时间片中一次写入 4 个。

将其与另一个序列进行比较,在该序列中,SSD 将所有 8 个页面写入同一目标。 SSD 只能对写入使用单个数据线。 再次,点击“Time IO”按钮查看计时。

速度选择器

请注意,仅使用了一条线路,并且需要按顺序写入。 所有其他线路都处于休眠状态。

这表明我们读取和写入数据的顺序对性能很重要。 许多软件工程师不必每天都考虑这一点,但是那些设计像 MySQL 这样的软件的人需要仔细注意数据存储在什么结构中以及数据在磁盘上的布局方式。

SSD 垃圾回收

可以从 SSD 读取或写入的最小“块”数据是页面的大小。 即使你只需要其中的一部分数据,也必须在请求中指定该单元。

可以从页面读取数据任意次数。 但是,写入有点不同。 在将页面写入后,在旧数据被明确擦除之前,无法使用新数据覆盖该页面。 棘手的部分是,无法擦除单个页面。 当你需要擦除数据时,必须擦除整个块,之后可以重复使用其中的所有页面。

每个 SSD 都需要有一个内部算法来管理哪些页面是空的,哪些页面正在使用中,以及哪些页面是脏的页面是已写入但不再需要数据的页面,并且已准备好被擦除。 有时还需要重新组织数据,以便允许新的写入流量。 管理此的算法称为垃圾回收器

让我们通过查看另一个可视化效果来了解这会产生什么影响。 在下面的 SSD 中,所有四个目标都存储着数据。 一些数据是脏的,用红色文本表示。 我们想将 5 页数据写入此 SSD。 如果我们计时此写入序列,SSD 可以愉快地将其写入空闲页面,而无需额外的垃圾回收。 第一个目标中有足够的未使用页面。

速度选择器

现在假设我们有一个已经有不同数据的驱动器,但我们想将相同的 5 页数据写入其中。 在此驱动器中,我们只有 2 页未使用,但有许多脏页。 为了写入 5 页数据,SSD 需要花费一些时间进行垃圾回收,以便为新数据腾出空间。 在尝试计时另一个写入序列时,将进行一些垃圾回收,以便为数据腾出空间,从而减慢写入速度。

速度选择器

在这种情况下,驱动器必须将左上方目标中的两个非脏页面移动到新位置。 通过这样做,它能够使左上方目标中的所有页面都变脏,从而可以安全地擦除该数据。 这为写入 5 个新数据页面腾出了空间。 这些额外的步骤显着降低了写入性能。

这表明驱动器上数据的组织方式会影响性能。 当 SSD 有大量的读取、写入和删除时,由于垃圾回收,我们最终可能会得到性能下降的 SSD。 尽管你可能没有意识到,但繁忙的 SSD 会定期执行垃圾回收任务,这会减慢其他操作的速度。

这些只是影响 SSD 上数据排列方式的众多原因中的两个。

云存储

从磁带到磁盘再到固态的转变使持久 IO 性能在过去几十年中显着加速。 但是,还有另一种现象导致 IO 性能发生了额外的转变:迁移到云。

尽管在此之前有公司提供云计算服务,但当 Amazon AWS 在 2006 年推出时,大规模迁移到云获得了显着的吸引力。 自那时以来,成千上万的公司已将其应用程序服务器和数据库系统迁移到他们的云以及来自 Google、Microsoft 和其他公司的类似服务。

尽管这种趋势有很多优点,但也有几个缺点。 其中之一是服务器往往具有较少的持久性。 用户在巨型数据中心内的任意硬件上租用(虚拟化)服务器。 这些服务器可能随时因各种原因而关闭 - 硬件故障、硬件更换、网络断开等。 在租赁云基础设施上构建平台时,计算机系统需要能够在任何时刻容忍更频繁的故障。 加上许多工程师希望动态扩展存储卷的愿望,导致了一种新的子现象:存储计算的分离。

存储与计算分离

传统上,大多数服务器、台式机、笔记本电脑、手机和其他计算设备都直接连接有非易失性存储。 这些通过 SATA 电缆、PCIe 接口连接,甚至直接内置到与 RAM、CPU 和其他组件相同的 SOC 中。 这对于速度来说非常好,但会带来以下挑战:

  1. 如果服务器关闭,数据也会随之关闭。
  2. 存储是固定大小的。

对于应用程序服务器,1 和 2 通常不是什么大问题,因为它们在设计上非常适合临时环境。 如果一个服务器关闭,只需启动一个新的服务器。 它们通常也不需要太多存储空间,因为它们所做的大部分事情都是在内存中发生的。

数据库是另一回事。 如果服务器关闭,我们不希望丢失数据,并且数据大小会迅速增长,这意味着我们可能会达到存储限制。 部分由于这个原因,许多云提供商允许你启动具有单独配置存储系统的计算实例,该存储系统通过网络连接。 换句话说,使用网络连接存储作为默认值。

当你在 EC2 中创建新服务器时,默认情况通常是连接 EBS 网络存储卷。 许多数据库服务,包括 Amazon RDS、Amazon Aurora、Google Cloud SQL 和 PlanetScale,都依赖于这些类型的存储系统,这些系统具有通过网络将计算与存储分离。 这提供了一个很好的优势,因为存储卷可以随着数据的增长和缩小而动态调整大小。 这也意味着,如果服务器关闭,数据仍然安全,并且可以重新连接到不同的服务器。 然而,这种简单性是有代价的。

本地存储 vs 网络存储

考虑以下简单配置。 在其中,我们有一个带有 CPU、RAM 和直接连接 NVMe SSD 的服务器。 NVMe SSD 是一种固态磁盘,它使用非易失性内存主机控制器接口规范来实现极快的 IO 速度和出色的带宽。 在这种设置中,从 CPU 到内存 (RAM) 的往返大约需要 100 纳秒(纳秒是十亿分之一秒)。 从 CPU 到本地连接的 NVMe SSD 的往返大约需要 50,000 纳秒(50 微秒)。

比实时慢 5000000x slower CPUmemory (100ns)local SSD (50μs)

这清楚地表明,最好将尽可能多的数据保存在内存中,以获得更快的 IO 时间。 但是,我们仍然需要磁盘,因为 (A) 内存更昂贵,并且 (B) 我们需要将数据存储在某个永久位置。 尽管本地连接的 NVMe SSD 在这里看起来很慢,但它对于现代存储来说已经非常快了。

让我们将其与网络连接存储卷(例如 EBS)的速度进行比较。 读取和写入需要在数据中心内进行短的网络往返。 往返时间显着变差,大约需要 250,000 纳秒(250 微秒或 0.25 毫秒)。

比实时慢 25000x slower CPUlocal SSD (50μs)network SSD (250μs)

使用相同的尖端 SSD 现在需要一个数量级的时间才能满足单个读取和写入请求。 当我们有大量的顺序 IO 时,可以减少这种负面影响,但不能消除。 每次我们需要访问存储系统时,我们都引入了显着的延迟恶化。

云中网络连接存储的另一个问题是以限制 IOPS 的形式出现。 许多使用此模型的云提供商,包括 AWS 和 Google Cloud,都限制了你可以通过线路发送的 IO 操作数量。 默认情况下,Amazon 上的 GP3 EBS 实例允许你每秒发送 3000 个 IOPS,并允许构建额外的池以允许偶尔的突发。 下图显示了它是如何工作的。 请注意,突发余额大小比实际尺寸小,以便更容易看到。

每秒的 I/O 请求数 3000 serverbalance0EBSrequested IOPS3000actual IOPS3000burst

相反,如果你的存储直接连接到你的计算实例,则不会对 IO 操作设置人为限制。 你可以根据硬件允许的速度进行读取和写入。

对于多年来我们在 IO 性能方面取得的许多进步,这似乎是一个错误的步骤。 这种分离购买了一些不错的便利,但性能的代价是什么?

我们如何克服问题 1(数据持久性)和 2(驱动器可扩展性),同时保持良好的 IOPS 性能?

问题 1 可以通过复制来克服。 我们可以将数据复制到多台计算机上,而不是依赖于单台服务器来存储所有数据。 一种常见的方法是让一台服务器充当主服务器,它将接收所有写入请求。 然后,2 个或更多其他服务器会将所有数据复制到它们。 通过将数据存储在三个位置,丢失数据的可能性变得非常小。

副本数 2 replicareplicaprimaryprimary

让我们看一下具体的数字。 作为一个编造的值,假设在一个给定的月份中,服务器发生故障的概率为 1%。 对于单台服务器,这意味着我们每个月有 1% 的可能性丢失数据。 这对于任何严肃的商业目的来说都是不可接受的。 但是,对于三台服务器,这降至 1% × 1% × 1% = 0.0001% 的概率(百万分之一)。 在 PlanetScale,保护实际上比这还要强大,因为我们会自动检测和更换集群中发生故障的节点。 我们会频繁且可靠地备份数据库中的数据,以提供额外的保护。

问题 2. 可以解决,但在使用直接连接 SSD 时需要更多的人工干预。 我们需要确保我们监控并在磁盘容量接近限制时收到警报,然后使用工具轻松增加容量(如果需要)。 借助这样的功能,我们可以实现数据持久性、可扩展性和极快的性能。 这正是 PlanetScale 使用 Metal 构建的。

解决方案:Metal

Planetscale 刚刚宣布了 Metal,这是解决此问题的行业领先的解决方案。

使用 Metal,你可以获得一个完整的 Vitess+MySQL 集群设置,每个 MySQL 实例都使用直接连接的 NVMe SSD 驱动器运行。 每个 Metal 集群默认都带有一个主服务器和两个副本,以实现极其耐用的数据。 当你达到存储限制时,只需点击几下按钮,我们就可以让你使用更大的驱动器调整服务器的大小。 在幕后,我们会处理启动新节点并将数据从旧实例迁移到新实例,而不会造成任何停机时间。

也许最重要的是,对于 Metal 数据库,IOPS 没有人为上限。 你可以以最小的延迟执行 IO 操作,并根据需要对其进行尽可能多的处理,而不会受到限制或为喜爱的云提供商上的昂贵 IOPS 类付费。

如果你想要极致的性能和可扩展性,今天就试试 Metal