首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >2分钟论文阅读-如何榨干NVMe Storage性能

2分钟论文阅读-如何榨干NVMe Storage性能

作者头像
早起的鸟儿有虫吃
发布2026-01-05 19:32:44
发布2026-01-05 19:32:44
1080
举报

一、你为什么阅读这篇文章

做事就得多练

但在公司里

百人开发的项目

五年、十年不调整

提重构、说优化,没人同意

到最后连自己都不敢信

领导只会说:

“你说不清楚,就是没搞懂”

每天测试、排障

看似忙碌,实则没收获

哪怕解决一个bug

也能从全栈角度想

多看看别人的解法,读读论文

菜就需要多练

但在公司里

百人开发的项目

五年、十年不调整

提重构、说优化,没人同意

最后自己也不相信了,这样公司ceo,总监,领导 就说,你说都不清楚,搞都不搞明白

最后 被安排会不停人肉测试, 解决测试发现问题,这个好像保证产品质量,让客户满意,最好方式了吗?

不是,你都根本开始,

哪怕是解决bug可以全栈角度考虑,

今天推荐方式 看别人怎么解决,看别人写论文

论文是 免费公开的,人人都可以看。

不需要CEO的战略判断能力,哪怕理解错了也不会影响公司上下上千人的饭碗跟你无关。

不需要总监负责市场和客户的能力,哪怕看不懂,造成百万项目损失,跟你无关

更不需要领导的设计能力,哪怕一时理解不了,不会周围人全部反对。

看一下总不会吃亏,不会上当,总比每天熬夜刷短剧强

开始

《What Modern NVMe Storage Can Do, And How To Exploit It: High-Performance I/O for High-Performance Storage Engines》

论文作者与署名信息

作者:Viktor LeisTobias ZieglerThomas NeumannAlfons Kemper

收录与年份:VLDB 2023

主题聚焦:在现代 NVMe SSD 上,如何通过 I/O 路径与存储引擎协同设计,充分释放设备的高并发、低时延与高带宽能力2。

作者背景与研究方向

Viktor Leis

研究方向:数据库系统存储引擎NVMe/PCIe 性能优化、查询执行与并发控制。

代表性工作:与团队长期深耕 NVMe 场景,参与 LeanStore 系列工作(如 vldb24: LeanStore: A High-Performance Storage Engine for NVMe SSDs),聚焦在 NVMe 上的高效 I/O 与系统可扩展性2。

Tobias Ziegler

研究方向:NVMe 存储系统文件系统/块层交互高性能 I/O 路径设计

代表性工作:围绕 直接附着 NVMe 阵列I/O 栈优化 的系统性研究与工程实践,持续推动 NVMe 在数据库负载中的极限利用2。

二、帮你解决什么问题

能用钱解决事情,都不是事情,系统慢 好解决 ,

在过去十年中,闪存固态硬盘(flash SSDs)已取代磁盘,成为操作型数据库系统的默认持久存储介质

NVMe的阵列接近直接访问内存型

This means that arrays of NVMe

SSDs are approaching DRAM bandwidth

购买最好设备 NVMe SSD盘,这样 系统性能提高吗?

补充一下:

NVMe的全称是 Non-Volatile Memory Express, 即非易失性存储器快速通道。 它是一种用于连接计算机系统与闪存存储设备(如固态硬盘)之间的通信协议和接口标准

这里指 NVMe盘

结果呢 压力上来了 性能没有提高,cpu利用率还暴增?这不是瞎说,请看

图1
图1

图1

怎么可能

MySQL,RocksDB 等大名鼎鼎软件 开启1000-2000个线程 为什么如何这样发挥原来10分之一能力

系统/指标

最高性能 (IOPS)

达到峰值所需线程数

与硬件极限的差距

硬件极限

12.5 M

不适用 (参考线)

0 (基准)

LeanStore (基准版)

~3.6 M

约 1500 线程

3.5倍

RocksDB

~2.8 M

约 1000 线程

4.5倍

WiredTiger

~1.8 M

约 500 线程

约7倍

PostgreSQL

~1.3 M

约 1500 线程

约9.6倍

MySQL

~0.8 M

约 1000 线程

约15.6倍

实验场景:一个100 GB的数据库,仅使用10 GB的缓冲池(即数据量是内存容量的10倍)。

硬件配置:使用8块企业级NVMe SSD

测试负载随机查找(random lookups)

对比系统:LeanStore(基线版本)、RocksDB、WiredTiger、PostgreSQL、MySQL。

X轴线程数(从0到2000)。

Y轴吞吐量(每秒查找次数,单位:M/s)。

关键发现与解读

1

硬件性能极限

图顶部有一条虚线标注 system I/O capability: 12.5M(千万) IOPS

这代表由8块SSD组成的阵列,在4 KB随机读取下的理论最大I/O能力

2

巨大的性能鸿沟(3.5x gap)

图中明确标注了 3.5x gap

它证明了:

1

现代NVMe SSD阵列拥有巨大的性能潜力(12.5M IOPS)。

2

现有存储引擎存在显著的性能鸿沟(最高仅能利用~29%的硬件能力)。

3

传统架构(基于OS线程的同步I/O)是瓶颈所在,无法有效管理海量并发I/O请求与有限CPU核心之间的矛盾。

简单回答是: 是的,I/O本身(数据在SSD和内存之间传输)不占用CPU。

管理这些I/O请求的过程— —包括生成、提交、跟踪、完成通知和错误处理——会消耗大量的CPU时间。

硬件性能与软件架构不匹配

硬件潜力:由8块企业级NVMe SSD组成的阵列,在4 KB随机读取下可提供超过1200万IOPS的极高吞吐量。

软件现实:如图1所示,即使是为SSD优化的现代存储引擎(如LeanStore基线版),在内存外(out-of-memory)场景下也仅能达到约360万次查找/秒,存在约3.5倍的性能差距

这张图引出了论文后续章节要回答的核心研究问题:

如何通过重新设计存储引擎的I/O架构来弥合这一性能鸿沟?

当系统能够调度数百万IOPS时,CPU处理能力成为瓶颈。(IO原来不占用cpu的,现在不行了)

传统存储引擎架构(基于OS线程的同步I/O、专用后台线程、低效I/O模型)无法协调现代NVMe SSD硬件所要求的极高请求级并行性、有限CPU核心数和高SSD I/O深度之间的矛盾

这导致了严重的线程过度订阅、高上下文切换开销、I/O放大以及CPU瓶颈,使得软件无法充分利用硬件的巨大性能潜力

于是 就问:

问题编号

研究问题

论文给出的核心解答与发现

Q1

NVMe阵列能否达到硬件标称的性能?

可以,甚至能超越。 实验证实,8块NVMe SSD组成的阵列能够实现1250万次/秒的随机读取IOPS,超过了单个硬盘标称性能的简单叠加。

Q2

应该使用哪种I/O API?是否需要内核旁路(如SPDK)?

1. 所有异步接口(libaio, io_uring, SPDK)都能实现高吞吐。2. 内核旁路(SPDK)在CPU效率上具有绝对优势,但io_uring在轮询模式下也能接近其性能。对于追求极致效率的场景,SPDK是最佳选择。

Q3

存储引擎应使用多大的页大小,才能在获得高性能的同时最小化I/O放大?

4 KB是最佳权衡点。 这是NVMe SSD随机读取性能的“甜点”,能同时优化IOPS、带宽和延迟。小于4KB会因硬件限制导致性能下降,大于4KB则会造成严重的I/O放大。

Q4

如何管理实现高SSD吞吐所需的高并发度?

必须采用用户态协作式多任务(轻量级线程)。 传统“一个查询一个内核线程”的模型会导致数千线程的过度订阅和巨大开销。论文通过用户态任务调度,使少量工作线程能高效管理海量并发的I/O请求。

Q5

如何让存储引擎足够快,以管理每秒数千万的IOPS?

内存外(Out-of-Memory)的代码路径必须进行深度优化和并行化。 这包括:采用分区锁消除热点、优化淘汰算法(如引入乐观父指针)、移除内存分配、以及微调热代码路径,确保CPU不会成为瓶颈。

Q6

I/O应由专用的I/O线程执行,还是由每个工作线程执行?

应由工作线程直接执行(All-to-All模型)。 论文否定了专用I/O线程或SSD绑定的模型,采用对称设计:每个工作线程拥有通往所有SSD的独立I/O通道,无需线程间通信。这简化了设计,并实现了最佳的可扩展性。

1

Q1: Can arrays(阵列) of NVMe SSDs achieve (取得)the performance promised in the hardware specifications?

研究 NVMe SSD 阵列是否能达到硬件规格所承诺的性能。

实验中验证了 8 块 Samsung PM1733 SSD 能达到 12.5 M (千万)IOPS,略高于理论值 12 M IOPS 。

一块高性能的 SATA SSD 的随机读取IOPS约为 10万 (0.1M) 级别。 一块消费级 NVMe SSD 的随机读取IOPS可达 50万至100万 (0.5-1M) 级别。 论文中通过8块企业级NVMe SSD组成的阵列,实现了 1250万 (12.5M) IOPS。这比单块高端消费级SSD快了10倍以上。 这已经接近了测试所用 PCIe 4.0 x8 总线带宽的理论上限,充分“榨干”了硬件潜力

2

Q2: Which I/O API (pread/pwrite, libaio, io_uring) should be used? Is it necessary to rely on kernel-bypassing (内核旁路)(SPDK Storage Performance Development Kit”)?

比较不同 Linux 异步 I/O 接口的性能与适用性,并探讨是否需要采用 SPDK 这种内核旁路方案。

结论:io_uring(开启轮询)可达到全带宽,SPDK CPU 效率更高但在实际部署上有局限

3

Q3: Which page size should storage engines use to get good performance while minimizing I/O amplification?

确定存储引擎的最佳页大小,以在性能与 I/O 放大之间取得平衡。

实验表明 4 KB 页在随机读 IOPS、吞吐、延迟方面是最佳折衷 。

4

Q4: How to manage the parallelism required for high SSD throughput?

如何管理实现高 SSD 吞吐量所需的并行度。

提出使用协作式多任务(cooperative multitasking)与轻量级用户态线程,避免线程过度订阅 。

5

Q5: How to make a storage engine fast enough to be able to manage tens of millions of IOPS?

如何让存储引擎足够快以支撑千万级 IOPS 的管理。

通过分区锁、无锁数据结构、微优化关键路径等手段提升扩展性 。

6

Q6: Should I/O be performed by dedicated I/O threads or by each worker thread?

I/O 应由专用 I/O 线程执行,还是由每个工作线程直接处理。

结论:采用 all-to-all 模型,由工作线程直接处理 I/O,可避免消息传递与同步开销,提高鲁棒性

三、如何解决的(把cpu瓶颈 变成I0瓶颈)

3.1 IO 接口选择

异步接口都满足要求,代价不一样
异步接口都满足要求,代价不一样

异步接口都满足要求,代价不一样

I/O接口比较

SPDK:性能最佳,CPU开销最低,仅需3个线程即可达到峰值带宽。

io_uring(轮询模式):接近SPDK性能,是内核空间的强大替代方案。

使用内核旁路(SPDK)的主要优势是CPU效率更高,但在有足够CPU核心的情况下,内核接口也能达到全带宽

首先了解NVMe设备特点

极高的并行性

一个NVMe设备可以支持多个(如64K个)Queue Pair。 每个CPU核心可以拥有自己专属的Queue Pair, 从而彻底避免了多核争用同一队列的锁开销,实现了真正的并行I/O。

NVMe Queue Pair是硬件层面的高性能生产-消费者模型

简单来说,一个NVMe Queue Pair(队列对)就是主机(CPU)与NVMe固态硬盘(SSD)之间进行通信的一对“指令通道”

队列

名称

方向

核心功能

类比

SQ

提交队列 (Submission Queue)

主机 → SSD

主机将需要执行的I/O命令(如读、写)描述符放入此队列,通知SSD“有活要干”。

像一家餐厅的点菜单。顾客(应用)把订单(I/O请求)写好放在单子上,交给服务员(驱动)。

CQ

完成队列 (Completion Queue)

SSD → 主机

SSD处理完SQ中的命令后,会将完成状态(成功或失败)写入此队列,通知主机“活干完了”。

像餐厅的出菜铃。厨房(SSD)做好菜后,按一下铃(写入CQ),通知服务员来取。

NVMe的革命:大规模并行的无锁模型

接口的本质:

无论用哪个库,最终目标都一样:把I/O请求塞进NVMe的提交队列,然后等SSD处理完,再从完成队列里取结果。

区别在于“塞”和“取”的方式。

Blocking POSIX interface.

libaio: traditional asynchronous interface

io_uring: modern asynchronous interface.

1. 阻塞式POSIX接口(read/write)

怎么用: 最传统的方式。线程发一个请求,然后就被内核“挂起”(阻塞),直到SSD干完活才被唤醒。

架构师视角:简单,但性能差。

一个线程一次只能处理一个I/O,要榨干SSD的性能,

你得启动成千上万个线程,上下文切换开销巨大,完全不现实。这是给慢速磁盘时代设计的模式。

2. 传统异步接口(libaio)

怎么用: 进步了。

可以一次性提交一批请求(io_submit),然后立刻返回,不阻塞。

程序需要自己轮询(get_events)来检查哪些请求完成了。

架构师视角:减少了系统调用次数,一个线程能管理多个并发I/O。

但接口比较原始,而且在高并发下,轮询和事件获取的效率可能成为瓶颈。

3. 现代异步接口(io_uring)

怎么用: Linux内核的“新宠”。

它在用户态和内核态之间建立了共享的环形队列

用户程序把请求丢到提交队列,发个信号(io_uring_enter)通知内核去处理。

内核处理完,把结果放到完成队列。

高级模式(SQPOLL): 内核可以开个专门的线程来轮询提交队列,这样连发信号这个系统调用都省了,进一步降低延迟。

高级模式(IOPOLL): 可以关掉硬件中断,让应用自己轮询完成队列。在超高IOPS场景下,这能减少中断开销,提升性能。

架构师视角:目前Linux上最先进、最灵活的通用异步I/O方案。 它试图在易用性和极致性能之间找到平衡,支持多种优化模式。

4. 用户态I/O(SPDK)

怎么用: “掀桌子”的方案。

完全绕过操作系统内核(文件系统、块设备层、页缓存)。

直接在用户态分配和管理NVMe的队列对。提交请求就是往内存里的环形缓冲区写条记录,然后通知一下SSD

3.2 线程模型选择

三幅图

图7

图8

图8
图8

图8

原文: Figure 8: Design overview. The system is handling many incoming requests in parallel. Worker threads are running these requests cooperatively as tasks, while also taking care of page eviction, I/O submission, and polling

图10:I/O模型比较(I/O后端连接模型)

描述对象工作线程如何与SSD硬件连接的具体I/O模型

核心内容:比较了三种模型:

1

专用I/O线程模型:工作线程需通过消息传递将I/O请求交给专门的I/O线程处理。

2

SSD分配模型:每个SSD被分配给特定线程,跨SSD访问需要线程间通信。

3

全对全模型(作者采用)每个工作线程都能直接访问所有SSD,拥有自己到每个SSD的I/O通道(队列对),无需任何线程间消息传递或同步

名词解释:

疑问:什么是工作线程,什么IO后端

工作线程是系统执行的核心单元,采用用户空间协作式多任务模型。

1

职责集成:每个工作线程身兼多职,不仅执行用户查询任务,还负责处理页面淘汰、I/O提交和轮询完成事件

2

协作式执行流程

当用户任务遇到页错误时,会提交异步I/O请求,并将任务状态设置为“等待I/O”,然后主动让出(yield) CPU控制权给调度器。

调度器随后会处理I/O提交、页面淘汰和轮询I/O完成事件。

当I/O完成时,I/O后端会回调工作线程,工作线程将对应的任务状态设置为“I/O完成”,并将其重新放入就绪队列,等待调度器恢复执行。

I/O后端的工作原理

I/O后端是一个软件抽象层,负责管理与底层NVMe SSD硬件的所有通信。

1

统一抽象:它封装了不同异步I/O库(libaioio_uringSPDK)的实现细节,为上层(工作线程)提供统一的接口。

2

I/O通道模型(全对全模型)

每个工作线程拥有一个独立的I/O通道,该通道管理着到所有SSD的直接连接(如NVMe队列对)。

这种全对全模型意味着无需任何线程间消息传递或同步。工作线程可以直接向任何SSD提交I/O请求。

疑问:.工作线程是否共享I/O后端?

不共享,但每个工作线程拥有自己独立的I/O通道。

设计模型:系统采用全对全模型

具体实现:每个工作线程都拥有一个独立的I/O通道,该通道是I/O后端抽象的一部分,负责管理该线程到所有SSD的直接连接(如NVMe队列对)。

关键优势:这种设计无需线程间消息传递或同步

每个工作线程都可以直接、独立地向任何SSD提交I/O请求,从而实现了高效、无锁的并行I/O访问。

疑问 . I/O后端是协程吗?

不是。I/O后端不是协程,而是一个管理硬件通信的软件抽象层。

I/O后端的本质

它是一个库或模块,封装了与底层NVMe SSD硬件通信的细节。

它支持多种异步I/O库(如libaioio_uringSPDK),

并为上层(工作线程)提供统一的、高性能的I/O接口。

3.3 页面大小选择

为什么选择page size 4K 最佳

较大页面的主要缺点在于内存外工作负载上的I/O放大。 However, the big downside of larger pages is I/O amplification on out-of-memory workloads.

例如,使用16 KB页面,读取或写入100字节的记录会导致160倍的I/O放大。

With 16 KB pages, for example, reading or writing 100 Byte records results in an I/O amplification of 160x.

总结

What Modern NVMe Storage Can Do, And How To Exploit It: High-Performance I/O for High-Performance Storage Engines

Q1: Arrays of NVMe SSDs can achieve the performance promised in hardware specifications. In fact, we achieved slightly higher throughput at 12.5 M IOPS with our 8×SSD setup. Q1:NVMe SSD阵列能够达到硬件规格承诺的性能。事实上,在我们的8块SSD配置中,我们实现了略高的吞吐量,达到1250万IOPS。

Q2: Good performance can be achieved with all asynchronous I/O interfaces. Kernel-bypassing is not essential to achieve full bandwidth even with small pages. However, it is more efficient in CPU usage. Q2:所有异步I/O接口都能实现良好的性能。 即使使用小页面,内核旁路也不是实现全带宽所必需的。然而,它在CPU使用方面更高效。

Q3: The best trade-off between random IOPS, throughput, latency, and I/O amplification is achieved with 4 KB pages. Q3:4 KB页面在随机IOPS、吞吐量、延迟和I/O放大之间实现了最佳权衡。

Q4: To manage the high parallelism required for large NVMe SSD arrays, the database system must employ a low overhead mechanism to quickly jump between user queries. To solve that we employed cooperative multitasking using lightweight user-space threads. Q4:为了管理大型NVMe SSD阵列所需的高并行性,数据库系统必须采用一种低开销机制,以便在用户查询之间快速切换。为此,我们采用了使用轻量级用户空间线程的协作式多任务处理。

Q5: Managing workloads with tens of million IOPS makes out-of-memory code paths hot and performance critical. This requires scalable I/O management through partitioning relevant data structures to prevent contention hotspots. The replacement algorithm has to be optimized to evict tens of millions of pages per second. Q5:管理每秒数千万IOPS的工作负载使得内存外代码路径变得热门且对性能至关重要。 这需要通过分区相关数据结构来实现可扩展的I/O管理,以防止争用热点。 替换算法必须优化到每秒能淘汰数千万个页面。

Q6: I/O should be performed directly by worker threads. In our design worker threads in fact perform all duties, like, in-memory work, eviction, and I/O. This symmetric design has conceptual advantages and allows for a more robust system. Q6:I/O应该由工作线程直接执行。在我们的设计中, 工作线程实际上执行所有职责,例如内存内工作、页面淘汰和I/O。 这种对称设计具有概念上的优势,并允许构建更健壮的系统。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-01-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端开发成长指南 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二、帮你解决什么问题
    • 硬件性能与软件架构不匹配
  • 三、如何解决的(把cpu瓶颈 变成I0瓶颈)
    • 3.1 IO 接口选择
      • 接口的本质:
    • 3.2 线程模型选择
      • 三幅图
      • 名词解释:
      • 3.3 页面大小选择
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档