作者 | 火尉子

责编 | Carol

封图 | CSDN 下载自视觉中国

在各行各业数字化转型深入的当下,数据呈爆炸式增长。面对海量数据的存储需求,分布式存储显然在架构上有着天然优势,但在这波数据洪流之中也面临着性能上的全新挑战。

由于分布式存储的工作原理是将各个存储节点使用网络互联的方式构建为集群,向外部提供更加可靠的高性能服务,因此可以说分布式存储本质上是一种网络存储,其性能在很大程度上受网络的影响。

在高性能的分布式存储中,使用传统的TCP网络进行各存储节点之间的网络互联很容易形成网络瓶颈,而在分布式存储最容易让用户诟病的IO延时方面,网络部分的开销是不可忽视的一部分。

随着网络带宽的高速增长,在软件上的消耗已成为网络性能的瓶颈,而通过绕过CPU实现高性能传输的RDMA网络成为分布式存储不错的选择。

RDMA(Remote Direct Memory Access),可以简单理解为网卡完全绕过CPU实现两个服务器之间的内存数据交换。其作为一种硬件实现的网络传输技术,可以大幅提升网络传输效率,帮助网络IO密集的业务(比如分布式存储、分布式数据库等)获得更低的时延以及更高的吞吐。

最初的RDMA是实现在IB(Infiniband)上,由于其新的硬件技术栈成本比较高,主要用于HPC(高性能计算)等少数场景。而新的技术发展下,能够实现在以太网上使用RDMA。 

当前RDMA在以太网上的传输协议是RoCEv2,RoCEv2是基于无连接协议的UDP协议,相比面向连接的TCP协议,UDP协议更加快速、占用CPU资源更少,但其不像TCP协议那样有滑动窗口、确认应答等机制来实现可靠传输,一旦出现丢包,依靠上层应用检查到了再做重传,会大大降低RDMA的传输效率。

所以要想发挥出RDMA真正的性能,突破数据中心大规模分布式系统的网络性能瓶颈,势必要为RDMA搭建一套不丢包的无损网络环境,而实现不丢包的关键就是解决网络拥塞。在解决拥塞问题的关键在支持ECN等特性的交换机,而这个技术在一般的交换机都普遍支持。

既然RDMA是一个硬件技术方案,而且在分布式存储中又能快速地解决网络高延时等相关问题,是不是直接更换上这个新的硬件技术就能万事大吉?实际可能并没有那么简单,那么我们就来简单聊聊RDMA技术实践过程中踩过的坑。

RDMA想说爱你却不容易

  • RDMA的使用需要应用程序的代码配合(RDMA编程)。

RDMA操作的语义更加贴近硬件实现的语义形态,与传统TCP/IP的Socket编程有很大的差别,不能直接将现有程序简单地直接套用RDMA接口,而使用原生RDMA编程库比Socket编程要复杂很多。

并且在编程方式上RDMA编程是异步的,而原始的Socket编程是同步的。这样将导致对大多数开发者来说,无论是改造原有应用程序适配RDMA,还是写一个全新的RDMA原生应用程序,都不容易。

那么如果完成了RDMA对接,是否就能使我们的程序获得高性能呢?我们再看看在通信模块中对接完RDMA的对比性能测试。

  • RDMA在RPC模块中使用

分布式存储中通常使用RPC框架进行节点之间的通信,RPC性能对整体的存储性能有重要的影响。笔者团队实现了一个基于C++的RPC框架,可以灵活支持多种数据传输协议并支持协程快速处理,适用于有高性能需求的场景。该RPC框架同时支持传统TCP和RDMA两种传输方式,RDMA方式下注册了统一管理的RDMA内存,并采用双边操作进行数据传输,支持事件和轮询的请求检测机制。笔者团队对两种传输方式的性能进行了对比测试,测试结果参见下图:

图1 RPC TCP与RDMA性能对比测试

(测试说明:使用同一张10Gb网卡,采用不同的模式进行对比测试)

通过测试我们发现RDMA的方式具有明显的优势,尤其在低深度的场景下能获得更好的性能。我们继续将RPC对接实际业务进行了测试,在这里我们对接了一个基于RAFT一致性协议的多副本数据存储业务,测试结果参见下图:

图2 对接实际业务后的性能对比测试

从上面的测试结果看到,在包含有实际业务的测试中,RDMA的表现并没有原始的RDMA测试表现那么优异,我们分析了其主要原因是线程切换、数据复制等操作以及IO路径上其它模块引入了额外的开销。

那么怎样才能发挥出RDMA的优异性能?在实践过程中我们也针对分布式存储系统Ceph分别使用TCP和RDMA进行了对比测试,测试结果也不是十分满意,我们发现在Ceph中对于小IO的场景使用不同的传输模式都没有明显的提升,甚至还有略微的下降,对于大IO的高吞吐情况,还会出现性能的明显下降,测试结果见下图:

图3 Ceph 原生RDMA 1M大块吞吐测试结果

在测试结果的基础上我们分析了Ceph中关于RDMA部分的实现,主要发现存在下面几个问题:

  1. Ceph中使用双边的编程方式,传输的过程并没有绕开CPU,同时Ceph内部使用bufferlist的数据结构,需要多次进行内存拷贝,没有充分发挥RDMA中零拷贝的特性。

  2. 由于RDMA的异步编程模型,需要进行Polling来检测数据操作的完成,使用单独的线程轮询不但没有降低CPU,反而有更多的CPU开销,而如果使用事件机制则会增加延迟降低性能。

RDMA在上述的各个场景中,都没能充分发挥其优越性能,那么需要怎样的编程方式才能发挥其价值呢?笔者团队又探索了SPDK(Storage performance development kit)的NVMe-oF中的RMDA使用方式。

  • RDMA在NVMe-oF中的使用

NVMe Over Fabrics(NVMe-oF)是使用RDMA或光纤通道(FC)架构等Fabric技术取代PCIe传输,从而将把本地高速访问的优势暴露给远端应用的一种传输技术。在SPDK中实现了相关的逻辑,下图是分别通过SPDK的NVMe-oF的target访问与本地访问SSD的性能对比测试。

图4 NVMe-oF 与原始SSD的性能对比测试

通过上面的数据我们发现,通过RDMA的网络传输的性能和原始的性能非常接近,在单深度的情况下读延时只增加了11us,写延时只增加了 18 us。通过多深度能够跑出与本地SSD相当的性能。上面的测试也进一步论证了正确地使用RDMA的相关编程技术是能够充分发挥出硬件优越性能的。

我们分析了SPDK中关于RDMA相关的实现,认为以下因素是RDMA编程实现的关键:

  1. 充分利用RDMA的内存注册机制,整个IO路径过程中使用内存池中的内存,实现全流程零拷贝。

  2. 对于控制消息(小IO)使用send/recv的双边编程方式,对于数据消息(大IO)使用write/read的单边编程方式。

  3. RDMA的poll与NVMe盘的读写处于同一个线程,整个过程没有线程切换,是全用户态的IO处理。

EDS中的RDMA应用

基于以上对于分布式存储中RDMA使用的分析,笔者团队认为简单地生搬硬套发挥不了RDMA与新型存储设备的性能优势,从整体框架上进行RDMA适配,将IO路径上各模块联动优化才是分布式存储追求极致性能之路。为此我们设计验证了面向RDMA的高性能分布式存储框架和实现方案,并在深信服企业级分布式存储EDS中落地应用。

EDS针对RDMA网络和NVMe SSD设计了低延迟高并发的存储架构,将存储节点前后端网络使用RDMA RoCE接入,实现低延迟极速网络传输,并对IO栈上各模块进行了相应的优化设计。深信服EDS存储架构采用了run to complete和无锁化编程模型,并优化了各模块之间的数据处理,实现系统内全IO栈的内存零拷贝,使整体性能得到了极大的提升。

如图5所示,前端协议网关(Protocol Gateway,PGW)接收来自用户的多种协议类型请求(包括NFS,SAMBA,S3,Swift,iSCSI,NVMe-OF等),将这些请求封装后通过SRPC通信模块发送到存储服务端进行处理。存储服务端解析请求后由存储引擎进行相应的处理并访问存储设备完成数据的写入和读取。

前端PGW请求发送和回调、存储服务端请求处理采用run to complete的方式在同一线程完成,极大地提高单个请求的处理速度,并采用无锁化编程的方式启用多个线程分组处理请求,达到请求的高并发。此外设计了统一的数据缓存管理机制,整个请求处理过程从数据经RDMA接收后,经过各模块处理,再到通过RDMA发送出去,全程不需要对数据进行内存拷贝,进一步减少系统开销并大幅降低了延迟。

图5 EDS存储软件架构示意图

 SRPC通信模块设计

(1)通信架构

深信服EDS存储设计了面向存储系统消息传输的专用网络通信模块SRPC,将上层多种类型请求消息封装,使用RDMA进行快速数据传输,可以灵活扩展支持上层NVMe-OF、iSCSI等多种类型协议,并对消息封装进行了抽象和简化,减少RPC数据包大小和处理时间。

SRPC的架构如图6所示,一个服务端server进程绑定一个port监听连接,server可以设置多个线程作为多个poll group并行处理请求,从请求接收到处理和回调Reply的整个过程都在同一线程内完成;客户端client创建channel连接server,可以指定要连接的server线程,client可以设置多线程,并在创建channel时分配其所在的线程。SRPC同时也支持使用TCP协议兼容未配置专用RDMA网卡的场景。

图6 SRPC通信架构

(2)数据传输方式

在使用RDMA操作数据传输时,通常有使用双边操作传输和使用单双边操作结合传输两种方式,SRPC选择了更为灵活的单双边操作结合的方式。

如图7所示,使用双边操作(SEND/RECV)传输数据与传统Socket网络传输类似,发送端使用RDMA SEND发送数据,接收端使用RDMA RECV接收数据。但是在发送端发起RDMA SEND操作之前,接收端需要准备好接收数据的内存区并发起RDMA RECV操作,否则就会发送失败。因此双方需要约定一次传输最大的数据大小,一般在在创建RDMA连接时协商,接收端以该大小准备接收内存区,发送端以该大小对大请求进行切分。使用双边RDMA SEND/RECV的限制在于当请求小于约定大小时存在接收端内存浪费的现象,而请求大于约定大小时需要发送端切分多次传输并在接收端重组,并且通常需要应用Buffer到RDMA协议Buffer之间的内存拷贝,增加了开销和延迟。

图7 RDMA双边传输方式

如图8所示,在单双边结合的传输方式下,双边SEND/RECV多用于控制类消息传输,而实际数据则是通过单边READ/WRITE来完成。在实际场景中每次需要传输的数据大小不是固定的,所以发起单边操作前需要先协商好数据长度和相应的内存区信息。其方式为先使用双边SEND/RECV操作把待传输数据的内存地址、大小、rkey等控制信息进行传输,然后根据传输类型选择单边READ或WRITE操作完成实际数据的传输,最后使用双边SEND/RECV操作发送Reply结果

数据发送到对端在实际场景中每次需要传输的数据大小不是固定的,所以发起单边操作前需要先协商好数据长度和相应的内存区信息。单双边结合传输的方式具有灵活适应各种大小请求的特点,尤其在传输大请求时具有明显优势,只需要一次单边操作即可将数据全部传输,同时单双边结合的方式也实现了应用Buffer到RDMA协议Buffer之间的数据零拷贝,进一步降低了开销和延迟。

图8 RDMA单双边结合传输方式

SRPC设置了两种模式进行请求的检测和处理,分别是性能模式和经济模式。性能模式下SRPC不间断轮询RDMA CQ队列获取请求,可以更快地响应请求并进行处理。而经济模式下SRPC采用RDMA事件触发与低频率轮询相结合的方式来进行请求检测处理,可以降低对CPU等资源的消耗。

SRPC根据负载采用智能算法自动在两种模式之间切换,当请求密集时采用性能模式快速处理请求,降低延迟,而当负载较低时则转入经济模式,降低CPU等资源使用,降低能耗。

总结

RDMA的编程模式与传统TCP/IP相比有很大的不同,不管是内存使用机制,还是数据操作逻辑都发生了很大的变化。使用RDMA可以减少在内核协议栈处理和内存拷贝等开销,从而大幅降低数据在网络上传输的延迟,但实际数据访问需要经过软件栈多个模块的处理,这其中可能存在数据拷贝和同步等开销,当存储设备的访问延迟和网络传输延迟都达到10us级别时,这些软件栈上的开销对整体性能的影响变得愈发明显。

从TCP/IP网络通信切换到RDMA通信不仅仅是数据收发网络接口的简单替换,而是需要进行整体的软件架构的优化设计,使IO路径上各模块契合RDMA的特点整体联动,才能充分发挥RDMA的优势,并与NVMe SSD等高性能低延迟设备结合,取得存储性能的突破。

【作者介绍】

火尉子,华中科技大学博士,研究分布式存储、大数据存储中的数据分布、性能和可靠性,在国际会议和期刊上发表多篇相关论文,目前主要从事分布式存储系统性能提升、网络通信优化的研发工作,专注于存储性能优化、RDMA等领域,2018年加入深信服科技。

推荐阅读
  • 138 张图带你 MySQL 入门!

  • 如何在 Kubernetes 上配置 Jenkins?

  • 这 10 行比较字符串相等的代码给我整懵了,不信你也来看看

  • 200 个工具分析机器学习十年:前途未卜、工程师是核心!

  • 天下苦苹果久矣:面对苹果税,开发者揭竿而起!

  • 独家揭秘!抖音爆款漫画变身特效的背后技术

  • 2013年买了100万美元比特币却希望“比特币归零”,这位亿万富翁公开“比特币鲸鱼”身份

真香,朕在看了!

浅谈分布式存储中的网络通信相关推荐

  1. java 中的单元测试_浅谈Java 中的单元测试

    单元测试编写 Junit 单元测试框架 对于Java语言而言,其单元测试框架,有Junit和TestNG这两种, 下面是一个典型的JUnit测试类的结构 package com.example.dem ...

  2. mybatis与php,浅谈mybatis中的#和$的区别

    浅谈mybatis中的#和$的区别 发布于 2016-07-30 11:14:47 | 236 次阅读 | 评论: 0 | 来源: 网友投递 MyBatis 基于Java的持久层框架MyBatis 本 ...

  3. 浅谈CSS3中display属性的Flex布局,关于登陆页面属性框的设置

    声明:本文转发自三里屯柯南的浅谈CSS3中display属性的Flex布局http://www.cnblogs.com/xuyuntao/articles/6391728.html 基本概念 采用Fl ...

  4. python sys模块作用_浅谈Python中的模块

    模块 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在Python中,一个.py文件就称之为一个模块(Mod ...

  5. python生成器和迭代器作用_浅谈Python中的生成器和迭代器

    迭代器 迭代器协议 对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么返回一个异常来终止本次迭代.(只能往前走,不能往后退!) 迭代器对象 遵循了(实现了)迭代器协议的对象.(对象内 ...

  6. oracle hash join outer,CSS_浅谈Oracle中的三种Join方法,基本概念 Nested loop join: Outer - phpStudy...

    浅谈Oracle中的三种Join方法 基本概念 Nested loop join: Outer table中的每一行与inner table中的相应记录join,类似一个嵌套的循环. Sort mer ...

  7. 浅谈caffe中train_val.prototxt和deploy.prototxt文件的区别

    浅谈caffe中train_val.prototxt和deploy.prototxt文件的区别 标签: caffe深度学习CaffeNet 2016-11-02 16:10 1203人阅读 评论(1) ...

  8. python中 是什么类型_浅谈python中的变量默认是什么类型

    浅谈python中的变量默认是什么类型 1.type(变量名),输出的结果就是变量的类型: 例如 >>> type(6) 2.在Python里面变量在声明时,不需要指定变量的类型,变 ...

  9. 浅谈Java中的Set、List、Map的区别

    就学习经验,浅谈Java中的Set,List,Map的区别,对JAVA的集合的理解是想对于数组: 数组是大小固定的,并且同一个数组只能存放类型一样的数据(基本类型/引用类型),JAVA集合可以存储和操 ...

最新文章

  1. 超越GhostNet!吊打MobileNetV3!MicroNet通过极低FLOPs实现图像识别(文末获取论文)
  2. 记录Nginx搭建网关服务
  3. C++最简单的方式实现split分割函数
  4. 【实施工程师】vim命令
  5. bzoj 1914: [Usaco2010 OPen]Triangle Counting 数三角形——极角排序
  6. java填空题_Java语言基础知识填空题
  7. 2020mysql安装教程_2020MySQL安装图文教程
  8. 23. 合并K个排序链表
  9. java实现遍历文件夹下的文件及文件夹
  10. docker 2376端口 CA 认证,并不能阻止服务器成为肉鸡
  11. AD19实时高亮显示网络,当鼠标悬停在网络上时能自动高亮
  12. shiro-cas------本地配置cas为HTTPS登录
  13. 【软件工程导论】可行性研究
  14. 网页版bpc电波对时_bpc电波对时app下载
  15. 服务如何获取当前登录的windows用户名
  16. c语言程序设计教程答案王晓云,【单选题】华人图灵奖获得者是( ) A. 吴恩达 B. 王小云 C. 姚期智 D. 杨振宁...
  17. Excel表格多个工作表数据汇总求和
  18. PDF文件加密解密-文件设置密码
  19. 牛牛的猜球游戏(前缀和+逆交换)
  20. reactos操作系统实现(119)

热门文章

  1. linux查找文件名赋值给变量,Linux平台从文件中查找字符赋值于变量
  2. java 查看垃圾收集器_JVM系列:查看JVM使用的什么垃圾收集器
  3. 国内11所“袖珍”大学!最小的甚至只有一栋楼……
  4. 清华王兴再抛神论:为什么教育决定着中国餐饮业质量?
  5. 何传启:第六次科技革命的三大“猜想
  6. 入门 | 一文看懂卷积神经网络
  7. 睡眠声音识别中的准确率问题(三)--采集的音频测试结果及分析
  8. mysql ( )连接_MySQL中concat函数(连接字符串)
  9. TFIDF java实现
  10. JanusGraph: 可视化 Gephi 插件安装