RDMA 技术概述
文章目录
RDMA 作为一种高速网络传输技术,在快速发展的数据库领域尤其是分布式数据库中,扮演着越来越重要的角色。传统单体数据库一般只涉及数据库客户端与数据库服务端的通信,而由于存算分离的设计,在分布式数据库中常常有多个数据库组件之间的协同工作,一个典型的场景是SQL 引擎层在为SQL 语句生成好执行计划后,需要将执行计划通过网络请求发送到数据存储层,存储层再将查询的结果作为响应返回给SQL 引擎层;随着分布式数据库普及与发展,整个数据库集群中的网络交互明显增加,对于集群内部网络通信性能的需求也“水涨船高”,RDMA 这种更加高速的网络传输技术正在被越来越多的研究与应用,本文将从RDMA 的背景、关键概念、以及技术优势三个方面对RDMA 的基本知识进行简要阐述。
RDMA,全称为 ”Remote Direct Memory Access“,是一种网络传输技术。早期的RDMA 技术使用InfiniBand 协议,对于网络设施的要求很高,因此只在特定领域进行有限应用,比如此前在数据库领域中,传统商业数据库 DB2 的pureScale技术和Oracle 的一体机方案中都有使用基于InfiniBand 的RDMA 技术;随着基于以太网的RDMA 协议RoCE的成熟,以及支持RoCE 的高速网卡等硬件在数据中心的普及,RDMA 正在高性能计算、深度学习框架、分布式存储等对网络传输要求较高的场景中快速发展,例如Aurora、OceanBase、PolarDB 等众多新兴分布式数据库都已经对此有较多的研究与实践。
RDMA 背景
从RDMA 的名称就让人联想到是不是与DMA 技术有些渊源,实际上RDMA 确实是在DMA 基础之上实现的。DMA 可能大家接触的多一些,在很多Zero-Copy 的场景(如Kafka )中都有应用;在开始介绍RDMA 之前,让我们先对DMA 做一个简单回顾。
DMA 概述
DMA 全称 “Direct Memory Access”,翻译为直接存储器访问,这里的存储器是指SRAM 等内部存储器。
我们知道CPU 是系统运行的核心,担负着计算、控制程序转移以及数据转移等任务;相比于其他复杂事务,数据的复制与存储较为简单,但却占用了很多CPU 资源;如果能将这部分工作交由专门硬件控制,让CPU 解放出来去处理更多复杂事务,便可以更好的利用CPU。
DMA 是就为了解决在外设和存储器或存储器和存储器之前进行数据传输时,需要占用CPU 资源的问题。
DMA 的实现涉及到DMA 控制器、DMA通道、仲裁器等硬件的支持,在此我们不再过多深究,只需要知道通过DMA 我们可以在外设(如磁盘、网卡)与存储器或内部存储器之间进行数据传输,而不需要CPU 及CPU 寄存器的参与。
无DMA 参与的I/O 示意图如下:
有DMA 参与的I/O 示意图如下:
基于DMA 的传统网络传输
在使用了DMA 技术之后,我们实际编写应用程序进行tcp/http 通信时,在发送端与接收端的链接建立完成之后,一次发送和接收数据的过程可以概述如下:
- 发送数据
- 应用程序准备好要发送的数据,调用write/sendto 系统调用,传入要操作的fd、待发送数据的内存地址以及数据长度;
- 系统调用write 导致一次用户空间到内核空间的上下文切换,用户空间中的数据被复制到内核空间中的socket 缓冲区SKB 中;在SKB 中将完成各协议栈报文头的组装。
- 系统调用write 执行结束,返回时会又一次导致内核空间到用户空间的上下文切换,被复制到SKB 中的待发送数据会在DMA 引擎的控制下被传递到网卡驱动中,然后被通过物理链路传输出去。
- 接收数据
- 经由物理链路传输来的数据包,首先会到达接收端网卡的接收队列。
- DMA 将数据复制到网卡驱动分配并初始化的内核空间缓冲区中
- 网卡发出硬件中断,给CPU物理引脚施加电压变化通知CPU 有数据到达
- CPU 简单处理硬中断后发出软中断,特定内核线程发现软中断后,会调用网卡驱动对已经处于内核空间缓冲区的数据进行协议解析等处理 ,并将处理后的数据放入到socket 的接收缓冲区
- 应用程序(通过阻塞等待或轮询)从内核得知有新数据到达的消息后,调用 read/recvfrom 系统调用,从socket 缓冲区中将数据从内核空间读取到用户空间
可以看到上述数据的发送和接收步骤中,将数据在网卡与内核空间的移动就是由DMA 完成的;另外我们还可以看到,虽然数据的移动已经有DMA 来负责,但CPU 在数据发送和接收过程中仍然起到了非常重要的作用,这部分工作便是将待发送的数据根据传输协议进行封装,以及对接收到的网络数据包进行解析。
我们思考这样一个问题:报文的封装与解析一定要CPU 来做吗,是否可以将CPU 从这些固定且繁重的工作中解放出来呢?
这便引出了我们的主题:RDMA
RDMA 与 基于RDMA 的网络传输
RDMA(Remote Direct Memory Access)翻译为远程直接地址访问,《RDMA Aware Networks Programming User Manual》中对其的解释为 “Accessing memory in a remote side without involvement of the remote CPU”,也即“在远端服务器CPU 不参与的情况下完成对其数据的访问”。
这里我们需要正确理解对远端数据的直接访问,在所谓的直接访问时,数据依然要通过网络设备,并按照约定好的RDMA 相关网络协议进行传输,但与传统基于TCP/IP 协议的网络传输又有很大区别。
在DMA 概述中我们介绍过,经由传统TCP/IP 网络传输的数据包需要在CPU 的参与下完成协议的封装和解析,而基于RDMA 网卡的数据传输,除了网卡与内存之间的数据移动无需CPU 参与,对于传输协议的封装和解析也交由硬件网卡实现。
由于无需CPU 对数据进行协议处理,因此应用程序还可以省去将待发送数据复制到内核的步骤,通过RDMA 进行数据发送的简化步骤如下:
- RDMA 发送数据
- 应用程序通知网卡硬件发送数据,并告知硬件待发送数据的内存地址,此时的待发送数据存在于用户空间
- RDMA 网卡通过DMA 从用户空间中获得待发送数据,根据RDMA 协议组装成报文发送给对端
- RDMA 接收数据
- 应用程序在能够接收数据前,需要首先通知网卡硬件准备接收数据,并告知硬件接收到的数据存放的内存地址
- RDMA 网卡在收到物理链路传输来的报文后,对其进行协议解析并通过DMA 直接将数据写入用户空间的内存
- RDMA 网卡通知应用程序,应用程序获得通知后便可读取已经位于用户空间的数据
(更加详细的RDMA 原理可以参考这篇文章)
在上述RDMA 的通信流程中,可以看到两个特点,这也是RDMA 技术两个显著优势:
- Zero Copy
由于RDMA 网卡支持DMA 操作,因此DMA 所具有的优势在RDMA 中仍然保留。数据在用户空间和网卡之间的移动均由DMA 处理,避免了用户空间到内核空间的多次copy
- Kernel Bypass
得利于RDMA 网卡硬件对数据的协议卸载和封装,数据与网络报文之间的转换不再需要CPU 全程参与,实际与TCP/IP 对比可发现网络传输过程中 sys cpu time 的显著下降
另外我们注意到在接收数据的第一步操作中,接收方在接收数据前需要显示的做一些准备工作,这也是RDMA 传输的另外一个不同于socket 的特点,在后续RDMA 操作中我们将看到准备工作的具体内容。
RDMA 协议类型
RDMA 本身是一种网络传输技术,而在具体实践应用时,存在多种不同的协议,包括Infiniband(简称IB)、RDMA over Converged Ethernet(简称RoCE)和internet Wide Area RDMA Protocol(简称iWarp),这三种协议提供给开发者的编程接口是相同的,区别主要是底层实现所依赖的网络基础设施与网络各层的协议有所不同,以下为三种协议的对比示意图(图片来源)
- Infiniband 协议
2000年由IBTA(InfiniBand Trade Association,独立于各个厂商的制定IB协议的组织)提出,其规定了一套完整的链路层到传输层(此处并非指传统OSI七层模型的传输层,而是位于其之上)规范,但是其无法兼容现有以太网,除了需要支持IB的网卡之外,企业如果想部署的话还要配套的IB交换设备,实现成本较高
- RoCE 协议
RoCE 是基于以太网链路层的RDMA 协议,存在RoCEv1和RoCEv2 两个版本。其中v1 版本在网络层仍然使用IB协议(因此仍然必须配合IB 交换机设备才可使用),而v2 版本使用UDP/IP 做为网络层的协议(也即报文在OSI七层的传输层仍然被封装为UDP 数据包,在网络层仍然被封装为IP数据包),数据包在传统以太网交换机上可以被路由到正确目标,这大大降低了企业数据中心基础设施的部署成本,只要在现有以太网中配合上支持RoCE 的网卡即可;也由于这些特性,RoCEv2 在实际场景中应用较多。
- iWarp 协议
iWarp 是IETF (负责开发、支持开源跨平台IB协议的非盈利组织,与上述IBTA 是配合关系)提出的基于TCP 的可靠协议。由于使用TCP 可靠连接,相比RoCEv2 在性能上有一定差距。
RDMA 关键概念
上一章中我们对RDMA 的背景和基本概念进行了阐述,本章将对RDMA 中涉及到的一些通用关键概念进行介绍,这里所说的通用是因为无论哪种RDMA 协议,都对上层应用暴露了相同的API,因此其对应的核心概念也是一致的。
在传统网络编程中,Socket 是其核心概念,网络的连接建立与数据传输都可以通过对socket 执行系统调用来实现;而在RDMA 编程中,QueuePair 是类似socket 角色的概念,RDMA 通信的基本单元是QP
QueuePair 直译为“队列对”,这里的队列是指RDMA 中的Work Queue(简写为WQ);Queue Pair 就是指一对共两个WQ 的组合。那么Work Queue 又是什么意思呢?
Work Queue
work queue 字面意思为“工作队列”,在上面章节中我们提到,RDMA 网络传输时,应用程序会通知硬件网卡要执行发送或者接收数据的动作,并将准备存放数据的内存地址告诉硬件网卡。硬件正是通过WorkQueue 接收应用程序给的操作通知的,因此我们可以将WQ 理解为“存放任务的队列”,而队列中的每个元素就代表了应用程序试图执行的一个“任务说明”,也被称作WQE(work queue element,工作队列元素)。
此处使用队列是利用其先入先出的特点,从而保证了应用程序提交给硬件的任务被按入队顺序执行。应用不断向队列尾部添加元素,而硬件不断从队首取出任务并执行,示意图如下:
Queue Pair
现在我们已经知道 queue pair 中的”queue“ 是work queue,那为什么需要两个WQ 组成一个“pair”呢?这是因为RDMA 将数据发送和接收任务使用了独立的WQ,分别称为SQ(send queue) 和RQ(recv queue);SQ和RQ总是成对出现的,不会脱离QP存在,在RDMA 编程中我们一般是直接操作QP,而非SQ和RQ。
应用程序可以创建多个QP ,而每个QP 只能对应某一个应用程序。
Completion Queue
Completion Queue 意为完成队列,是与WQ 对应的概念;放入WQ 中的WQE 在被硬件处理完成后,会在CQ 中产生一个CQE(completion queue element),CQE 可以视作“任务说明”WQE 对应的“任务完成报告”,一个QP 会对应存在一个send CQ 和 recv CQ,分别存放send work request 和recv work request 的执行完成报告。
Work Request 与 Work Completion
在了解了 QP、WQ和CQ 之后,我们还需要认识一下WR 和 WC;实际上WR 和WC 只是 WQE 和CQE 概念在用户态API 中的称谓,因为WQE 和CQE 都是网卡驱动中的概念,而应用程序无法直接操作网卡驱动;RDMA 暴露到用户态的API 是以WR 和WC 呈现给应用程序的,应用程序对WR 和WC 的操作实质上就是对 WQE 和CQE 的操作。如下图所示(图片来源 InfiniBand Architecture Specification Volume 1 ,Page93 )
在介绍完以上几个重要概念之后,我们便可以将RDMA 通信逻辑使用这些概念进行更具体的描述,如下图所示:
- RDMA Recv接收数据
R1. 接收端应用程序向QP的WQ中添加一个接收数据的WR,QP的SQ中会对应新增一个WQE;
R2. 接收端硬件从QP 的 RQ中拿到接收数据的WQE,准备接收数据。
R3. 接收端收到数据,进行校验后回复ACK报文给发送端。
R4. 接收端硬件将数据放到WQE中指定的位置,然后生成“任务报告”CQE,放置到CQ中。
R5. 接收端应用从CQ 中取得任务完成信息WC。
- RDMA Send发送数据
S1. 发送端应用程序向WQ 中添加一个发送数据的WR,WQ 中会对应新增一个 WQE;
S2. 发送端硬件从QP 的SQ中拿到发送数据的WQE,从内存中拿到待发送数据,组装数据包。
S3. 发送端网卡将数据包通过物理链路发送给接收端网卡。
S4. 发送端网卡收到ACK后,生成CQE,放置到CQ中。
S5. 发送端应用程序从CQ 中取得任务完成信息WC。
RDMA 操作类型
RDMA 技术中存在两组操作类型,RDMA Send/Recv 和RDMA Read/Write;在第一章介绍基于RDMA 的网络传输以及第二章介绍RDMA 关键概念时,我们使用的是RDMA 的Send/Recv 操作类型,这是一种加入了zero-copy 和 kernel-bypass 的传统网络通信的升级版,并未发挥出RDMA 的最大优势,本章我们对RDMA 这两组操作类型进行对比介绍,下图前两部分也可以直观的看到write/read 与send/recv 在对端CPU 介入上的区别。
双端操作:RDMA Send/Recv
Send/Recv 的详细流程如上一章RDMA 关键概念最后所述,由于每次Send/Recv 需要发送方和接收方两端都参与,即每次发送,接收端必须首先提交一个recv work request,发送方提交的send work request 才会正确执行并得到对应WC,因此把Send/Recv 称作双端操作。
单端操作:RDMA Read/Write
与Send/Recv 双端操作不同,Read/Write 是单端操作。单端的意思是准备阶段之后,每次数据的发送无需双端都参与,而只有执行RDMA Read或者RDMA Write 的一端操作即可,对端在此过程中无感知。
因为RDMA Write 与 RDMA Read 是两种不同的操作,下面我们对两个操作进行单独说明。
RDMA Write
RDMA Write 是本端主动写入远端内存的操作。在准备阶段之后,数据传输阶段过程中远端CPU 对于内存被写入是无感知的。
具体来说,整个过程的两个阶段如下:
- RDMA Write 准备阶段
在准备阶段,本端通过其他方式(比如RDMA 的Send/Recv)获得对端的内存地址以及访问对端内存所需的 rkey,rkey 是为了控制远端内存的访问权限;
- RDMA Write 数据传输阶段
本端准备好待发送数据,提交一个包含待发送数据地址,对端内存地址以及对端内存地址访问rkey 的work request,提交的WR 仍然是加入到QP 的Send Queue 中。
远端接收到RDMA Write 的报文后,会将数据放入目标内存地址,并向发送端响应ACK;
执行一次RDMA Write 的示例代码如下,可以清楚的观察传入的目标地址与rkey 等信息。
|
|
需要注意的是,操作过程中使用到的内存地址实际上是一个虚拟地址,实际的物理地址需要由RDMA网卡对虚拟地址进行转换得到。
RDMA Read
RDMA Read 是本端主动读取远端内存的行为。同RDMA WRITE一样,准备阶段之后的数据传输过程中CPU不需要参与,也不感知数据在内存中被读取的过程。
- RDMA Read 准备阶段
与RDMA Write 准备阶段一样,通过数据传输获得对端的内存地址以及rkey
- RDMA Read 数据传输
本端提交WR 之后,远端会根据WR 要读取的内存地址,将待读取的数据组装成响应报文;
请求端收到响应报文后,就可以成功获取到目标数据。
再次强调这里的响应端组装报文等操作是没有CPU 参与的,因此被读取数据的一端CPU 对于数据被读取是无感知的。
RDMA 优势总结
- 零拷贝(Zero-copy):
零拷贝网络技术使NIC 可以直接与应用内存相互传输数据,从而消除了在应用内存与内核内存之间复制数据的需要.内核内存旁路使应用无需执行内核内存调用就可向NIC 发送命令.在不需要任何内核内存参与的条件下, RDMA 请求从用户空间发送到本地NIC,并通过网络发送给远程NIC ,这就减少了在处理网络传输流时内核内存空间与用户空间之间环境切换的次数。
- 内核旁路(Kernel bypass):
数据传输直接在用户层来进行传输,无须进行进入内核态,也不用进行系统内存。
- 不需要CPU干预(No CPU involvement):
一旦应用已经系统上注册了RDMA对应的内存。之后,再进行数据传输就不需要CPU进行干预。也就是不需要再想TCP/IP一样进行拥塞避免等操作。
- 消息基于事务(Message based transactions):
不像TCP/IP协议一样基于字节流进行传输,RDMA的协议是通过一个消息来进行传输,处理消息和存储消息的位置自然是各种消息队列。
- 支持分散/聚合条目(Scatter/gather entries support):
RDMA原生态支持分散/聚合。也就是说,读取多个内存缓冲区然后作为一个流发出去或者接收一个流然后写入到多个内存缓冲区里去。
结语
本文简要介绍了 RDMA 技术的背景、关键概念和操作以及RDMA 基本带来的一些优势,对初次接触 RDMA 网络编程所要掌握的基本知识进行简单整理;鉴于RDMA 的技术细节很多,一篇文章无法将所有细节囊括其中,希望可以让读者对RDMA 有一个初步的认识。欢迎感兴趣的同学一起交流讨论。
参考资料
文章作者 Raygift
上次更新 2022-03-18