Lustre分布式锁管理器技术(三)

在文件系统中,要支持对文件的并发读写,解决文件读写一致性问题,必须要有一种机制来对文件的并发操作操作进行控制。当前文件系统一般都使用缓冲技术来加快数据访问的速度。其原理为将最近访问的文件保持在缓存中,下次访问相同的文件数据时则不需要再访问低速的磁盘,直接从缓冲中得到,从而获得较高的I/O速度。在基于客户端服务器模型的分布式文件系统中,缓存技术的引进使得一致性问题更加突出,它需要为系统中所有的文件系统客户端提供一个一致性的视图,必须解决多客户端并发访问控制和缓存一致性的问题。CIFS实现了一种oplock的文件锁机制,锁的粒度以文件为单位,它支持对文件单写者多读者的并发访问。很多分布式文件系统使用的锁协议都与CIFS类似,它们只提供对文件的粗粒度的并发访问,任何时候只能允许一个客户端向文件写数据,即使其他客户端写入同一个文件的不同数据部分。在它们的实现中,服务器一般授予单写者互斥锁来维护客户端缓冲的一致性,此时客户端可以读写并缓冲脏数据。当服务器撤销授权的互斥锁时,客户端会刷新并废除缓冲;当有多个客户端同时访问某个文件时,如果都为读访问,则服务器可以授予客户端共享锁,各个客户可以缓冲读取的数据,但不能脏写数据;如果存在写者,则服务器需要撤销所有被访问文件上的锁,清除客户端缓冲,文件I/O以Direct I/O的模式进行,所有的数据都要写透到服务器。Lustre通过范围锁实现了基于页面粒度的并发访问控制和基于锁回调的客户端数据写回缓冲,为HPC MPI-IO应用的多并发读、写者操作提供了高并发度和优秀的并行I/O性能[1]。Storage Tank文件系统[2,3]引入了半抢占式锁机制,即使共享文件已被关闭,隶属于该共享文件的某些锁依然可以缓存在本节点,以便优化后续的打开操作。此外,Storage Tank还引入了租约(lease)机制,用于处理网络分区和节点失效等故障。服务器为每个客户端节点维护一个租约,客户端可以通过与服务器交互更新租约的时效期,如果租约在其时效之内不能被更新,服务器会假定网络或者客户端节点发生故障,在一段指定的时间之后与该租约关联的客户端的锁将被清除;NFS第4版本[4]开始自身包含了对文件锁的支持,变为有状态的协议,它也同样引入了租约机制用于文件锁的有效性管理,并讨论了在网络分区和服务器重启情况下锁的恢复算法;Lustre同样通过客户端缓冲的锁支持文件关闭后的数据缓冲,并通过周期性的ping消息和客户端驱逐机制实现了类似的无效锁废除功能。IBM GPFS[5]实现了一种乐观的基于令牌的字节范围锁来同步文件I/O,但是系统中仍然存在专门的全局锁管理节点。Lustre同样采用了乐观的范围锁机制,同时对高度数据共享的应用的锁服务进行了优化,而且它的锁服务器分布于各个存储服务器节点,避免了集中锁管理容易造成的性能瓶颈问题。下面将介绍Lustre范围锁的语义、Lustre如何使用它来实现各种文件I/O服务以及为了增强锁服务的性能和扩展性我们所做的一些优化。

范围锁

在Lustre文件系统,范围锁主要用来维护细粒度的文件数据并发访问,锁的粒度以页面为单位。它主要用于数据对象资源上。与普通锁不同的是,范围锁增加了一个表示要锁定文件的范围的域,由于同一数据对象资源的不同锁的锁定范围之间是有关联的,因此,范围锁语义也发生了一些变化。为了便于描述,我们将范围锁定义为如下二元组:Li =mi, [ai, bi], (ai < bi),其中mi表示锁模式;[ai, bi]表示Li 锁定的文件范围。范围锁的兼用性判定公式如下:Compat(Li,Lj) = Compat(mi, mj)([ai, bi][aj, bj] ==).那么,锁请求Li与资源上已存在的锁Lj是否冲突,可以由以下公式判定:Conflict(Li,Lj) = !Compat(mi, mj)([ai, bi][aj, bj] !=).范围锁的包容性可以通过如下公式判定:Inclusion(Li,Lj) = Inclusion(mi, mj)([ai, bi][aj, bj]).相对于元数据,对文件数据的访问相对要简单,只有读和写两种方式。Lustre的范围锁也只用了两种锁模式:PW和PR。而且范围锁是可匹配的。由于PW模式的锁可以包容PR模式的锁,那么根据范围锁可匹配的特征,当客户端已经获得了数据对象上某个范围上的写访问权限时,那么它也同时获得该数据对象对应范围的读访问权限。Lustre结合范围锁,实现了客户端数据写回缓冲。图1来展示范围锁的使用示例:客户端A对服务器(OST)上的数据对象object1进行写入操作时,首先会根据要读写的范围向OST发出锁请求的L1=PW, [a1,b1]。当客户端获得返回的授权锁后,会将它缓冲在本地锁命名空间,并将锁的写者计数加1。然后客户端A的进程就可以对文件数据对象的范围的[a1, b1]进行写入操作,而且还可以利用VFS层的缓存机制将写入的脏数据缓冲在客户端页高速缓存中而不立即刷新到服务器。客户端的本地命名空间缓冲的锁 可以保护锁范围内的页高速缓存的一致性。当客户端完成写操作后,会执行锁释放操作,此时客户端一般仅仅只是将锁的写者计数减1,即使客户端进程已经关闭该文件或者锁保护的脏数据缓存已经被刷新到服务器,客户端一般也不立即主动撤销锁,而是仍将该锁缓存在客户端,这样就可以被客户端后续的重复读写所使用。当客户端B也要对object1进行写操作且服务器发现要获得的新范围锁L2=PW, [a2, b2]与L1有冲突即[ai, bi][aj, bj] !=时,服务器通过 的blocking callback通知客户端A撤销锁。客户端A在撤销本地锁命名空间中的缓冲的锁L1时,会通过它的blocking callback将脏的数据缓存批量的刷新到服务器并清除它所保护的客户端缓存,然后将锁释放返还给服务器。当服务器删除L1 后,会对锁L2进行授权并返回给客户端B。之后,客户端B就可以对锁L2所保护的范围进行读写操作。

图1 范围锁获取和撤销过程

范围锁可以锁定数据对象中不存在的区域。例如,当执行截断操作(truncate)时,如果截断后的文件大小为s,那么客户端在执行该操作的过程中,会首先获得要截断文件的范围锁PW, [s, OBJECT_EOF]。这里OBJECT_EOF是64位文件大小的最大值,它的值为0xffffffffffffffffULL。当获得该锁后,其它客户端持有的与该锁冲突的锁都被撤销,对应的缓存都被刷新和清除。此后,客户端发送截断请求到OST,将数据对象大小截断为s,从而保证了数据的一致性。

基于锁回调的文件大小获取

Lustre文件系统支持多读者多写者语义,也就是说多个客户端可以同时对一个文件进行读写操作。对于同一文件并发写的典型应用有HPC 检查点操作(checkpointing):将各个客户端的检查点数据写入到一个由Lustre文件系统共享的文件中。当多个客户端都打开同一个文件对它进行并发写时,文件的大小可能时刻在改变,如何动态及时的获得文件大小信息是个问题(例如,某个客户端通过stat()系统调用企图获得文件大小信息时)。由于Lustre采用了元数据和数据I/O路径分离的体系结构,此时,MDS上的元数据对象保存文件大小信息是不合适的。我们是从OST上获得数据对象大小的信息的。由于客户端可以将脏文件数据缓冲在本地页高速缓存中,而某些客户端扩展写的脏数据可能还没有刷新到OST上,因而OST上数据对象的大小信息也可能是不正确的。如果为了让OST上数据对象大小信息为当前正确的信息而刷新所有客户端缓存,显然代价很大,而且会大大增加操作延迟。Lustre采用了一个微妙的方法,通过范围锁的glimpse callback获得文件大小的即时信息。

图2 通过锁回调获得文件大小

我们以图2来介绍Lustre如何利用锁的glimpse callback来获取数据对象大小信息的算法。假设OST上object的当前大小为s,而锁命名空间上该对象资源的已授权锁队列上有n个为PW模式的范围锁:Li=PW, [ai, bi], (1in),其中当n>1时,aiai+1, [ai, bi][ai+1, bi+1]=,Ci表示持有锁 的客户端。那么,获得数据对象大小的算法为:当n = 0时,表示没有客户端写该数据对象,则返回的数据对象大小为s。当n > 0时,表示有多个客户端写该数据对象。此时,如果bns,则客户端不可能扩展该数据对象大小,返回客户端的数据对象大小为s。当n > 0且bns时,客户端可能会对会增大文件大小,只是扩展写的数据还缓冲在客户端。此时OST会调用Ln的glimpse callback,该回调的处理过程类似与blocking callback,其意图是从Cn获取它已知的数据对象的大小。当客户端Cn收到OST的回调消息后,它会调用本地对应的锁拷贝的glimpse callback获得当前客户端已知的数据对象大小s’,并返回给服务器。在上述过程中,不撤销任何锁。返回给客户端的数据对象的大小为s和s’中的最大值。在Lustre文件系统中,数据对象的大小信息是通过意图锁来实现的。当客户端企图获得数据对象大小信息时,它会向OST发送该数据对象上的锁请求:PR, [0, OBJECT_EOF],并指明锁的意图为获取数据对象的大小信息。当OST接受到锁请求时,执行锁的意图,按照上面的算法获取数据对象的大小;之后不返回锁给客户端,只是将对象大小信息返回给客户端。在Lustre文件系统中,文件数据被条带化到一个或多个数据对象中。当文件只被条带化到一个数据对象时,文件的大小即为返回的数据对象的大小;如果被条带化到多个数据对象时,客户端根据文件数据对象布局属性中的条带模式、条带大小和条带的数据对象数目等信息,对返回的各个数据对象大小进行几何计算既可得到文件大小信息。在一个大规模的Lustre集群中,当在一个很大的目录下执行“ls –s”时,我们发现系统的响应延迟有时甚至达到几分钟,这是用户难以容忍的。通过调查,我们发现“ls –s”命令对当前目录下的每个文件都执行stat()系统调用。在该系统调用中,Lustre会首先从MDS获得文件的连接数、访问时间等信息,然后通过意图锁来获得文件大小。当文件被条带到多个OST上的数据对象时,客户端要到每个条带的OST上获得数据对象的大小信息,从而导致访问延迟大大增加。为了解决这个问题,我们对Lustre文件大小获取的算法做了如下的优化。其基本原理是:当没有客户端对文件进行写访问时,MDS可以将文件大小信息可以保存在元数据对象中,并置上SOM(Size On MDS)标志。当元数据对象置上SOM标志时,文件大小可以直接从MDS上获取。当客户端以写模式打开文件时,MDS会清除元数据对象的SOM标志,此后文件大小的信息只能通过正常的意图锁的方式来获取;当最后一个写客户端关闭该文件时,MDS会通知该客户端将文件大小的信息同时返回给它,然后MDS会将文件大小保存在元数据对象中并置上SOM标志。通过这种方式,对于没有写访问或者静态的文件,数据大小可以直接从元数据服务器获得,一般只需要一次通讯,大大降低了操作的延迟。

自适应I/O锁策略

为了减少文件I/O因锁操作而造成的延迟,当客户端试图从服务器获得数据对象资源上的范围锁时,服务器的锁管理器会根据请求锁定的范围以及该数据对象上已存在锁的范围信息,自适应的给出尽可能大且覆盖请求范围的锁,而不是请求锁定的范围。这种机制被成为自适应乐观范围锁机制。例如,对写数据对象的第一个客户端将会获得整个数据对象范围上的锁PW, [0, OBJECT_EOF]。通过这种方式,当客户端访问文件的后续部分时,需要获得的锁请求可以直接从客户端本地的锁命名空间中进行匹配而得到,从而减少了与服务器交互的次数。该策略尤其在各个客户端访问独立的文件的情况下(例如,IOR FPP模式),可降低了锁操作延迟,提高性能。当客户端获得文件I/O所需的范围锁后,即使锁的读者计数和写者计数都为零,没有客户端进程使用该锁,一般也不会主动释放它。通常,锁服务器将锁授权给客户端也就认为该锁不会被频繁的撤销。但对一些特定的密集型共享I/O应用,锁冲突会频繁发生,从而造成不停的“乒乓”锁撤销回调,会极大的影响系统的性能。为了解决这个问题,我们实现了自适应I/O锁获取策略,它的基本原理是:在一个短时间窗口内(例如10秒),当锁服务器检测到多于一定数目的客户端持有与锁请求相冲突的锁,它就会拒绝来自客户端的锁请求,同时通知客户端将数据直接写透到存储服务器,并在存储服务器OST上直接获得锁,以获得对数据对象资源的访问权限,然后将数据写入到永久存储介质中,之后直接在锁服务器OST上释放锁,通过这种方式来避免“兵乓”锁回调过程。试验表明对超大规模系统的某些I/O密集型的数据高度共享的应用,该策略可以大大的提高性能。

Lustre Lockahead (LLA)

在HPC环境中,POSIX I/O文件访问类型通常被划分为FPP(File-Per-Process)和SSF(Single-Shared-File)两种模式。在HPC IOR基准测试中,发现FFP访问模式比SSF模式能提供更好的吞吐率。尽管共享文件访问有性能差的缺点,但是SSF由于多样的数据管理和易于使用的原因还是被广泛的使用。每个文件系统的实现以及I/O库对共享文件的性能都有影响。本小节主要讨论Lustre文件系统中影响SSF访问模式的主要因素,以及Cray实现的Lockahead算法和修改的MPI-IO(MPICH/ROMIO)库对共享文件访问性能的改进提升。Lustre文件访问行为分析首先观察FPP访问模式下的I/O行为,如下图所示:

图3 File Per Process (FPP)访问模式

FPP访问模式下,文件条带数目一般为1,也就是说文件数据只分布到一个OST上的数据对象中。每个MPI rank0(进程)一个文件,因此没有锁冲突,它是Lustre中性能最好的访问模式。但是FPP访问模式也存在问题:在动辄成上万个CPU核的HPC巨型机中(如~250,000核),如果每个rank绑定一个CPU核,对于分布到大量CPU核的大作业,在作业的启动和后处理(post-processing)过程中,会产生大量的元数据操作负载。通常HPC应用使用MPIIO库进行共享文件IO。 它一般使用一种称之为聚合collective I/O的技术,IO聚合到某个节点集合,每个节点处理文件的一部分。写操作一般是没有重叠区域的stride I/O,例如客户端1负责写块0、块2、块4等,而客户端2负责写块1、块3等。使用collective I/O时,尽管每一个进程的请求包含许多小的不连续数据片,但是所有进程的I/O请求都会很大,因此可以提高性能

图4 Single Shared File (SSF)访问模式

SSF访问模式下I/O行为如图4所示。SSF访问模式下,文件条带数目一般设置为系统中OST数目的个数。单个共享文件被所有的MPI rank所访问。多个Lustre客户端访问共享文件需要协调客户端间的锁操作冲突。在实际测试中SSF访问模式的性能要比FPP低。

图5 默认SSF访问模式锁冲突

图5展示了两个写者C1、C2以默认的LDLM锁操作访问文件的情况。出于减少锁操作的考虑,当客户端获取范围锁的时候,OST锁服务器会对锁范围进行扩展,给出涵盖指定范围的最大范围的锁。通过这种方式,当只有一个用户/客户端顺序访问文件时,例如,从起始位置0,第一次锁操作,服务器就可以给出[0,EOF]范围的锁,后续读写操作只需要在客户端本地锁命令空间进行匹配,减少了网络通讯,提高了锁操作的效率。如图所示,当C1写入块0时(这里假设每个块的大小为默认的条带大小1M),其请求的锁范围为[0, 1],由于OST的数据对象O1上没有其他冲突的锁,故OST锁服务器返回的锁范围为[0, EOF];当C2写入数据块1时,其范围与授权给C1的扩展锁范围冲突,OST对应的锁服务器就会撤销C1获取的锁,将C1缓冲的块0写回服务器OST,然后将锁范围[1, EOF]返回给客户端C2。如此反复,使得多个客户端对OST的访问完全是串行化的。更糟糕的是,它还增加了额外的锁撤销回调延迟。当有两个以上甚至更多写者的时候,性能降低更加严重。最明显的解决方法是禁止锁范围扩展,每个客户端仅获取它需求的相互间又无重叠范围的锁,使各个多个客户端可以进行并行I/O,而不产生锁冲突。尽管如此,这样做会额外增加每次写操作的锁请求延迟。测试结果显示它使得性能更差。LLA策略根据以上的分析,可以得知,为了达到较好的性能,需要解决两个问题:错误的锁冲突造成I/O串行化问题。执行I/O的锁操作延迟要尽可能减小和优化。为此,提出了Lustre Lockahead (LLA)策略。LLA对第一个问题的解决方法是将对指定范围的锁请求和I/O执行相独立(锁服务器不允许扩展范围所)。如果应用(或者I/O库,如MPI I/O)知道它使用的I/O模式,它可以通过请求它所需要的锁范围来避免错误的锁冲突。针对第二个问题,Lockahead通过并行的异步发出锁请求,来尽可能的减小锁操作延迟。这意味着所有锁请求的延迟可以有效的重合,使得总的延迟与一个请求时间相当。

图6 Lockahead示例

图6展示了一个针对strided I/O的Lustre Lockahead的示例。C1首先请求获取对O1的块(0,2,4,6,8)的锁,锁服务器返回客户端请求的指定锁范围,然后C1写入条带0。同样的,C2获取到与C1没有冲突的所(1,3,5,7,9),并将数据写入条带1。后续I/O的锁操作可以直接在客户端本地锁命名空间进行匹配。它展示了Lockahead策略不仅可以避免锁冲突,还可以降低锁操作延迟,从而提高性能。

参考文献

[1] Sang Jun Hwang, Jaechun No, Sung Soon Park. A Case Study in Distributed Locking Protocol on Linux Clusters [J].Computer Science, ICCS 2005: 380-387.[2] J Menon, D A Pease, R Rees, L Duyanovich, B Hillsberg. IBM Storage Tank – A Heterogeneous Scalable SAN File System [J]. IBM Systems Journal, 2003, 42(2): 250-267.[3] Randal Chilton Burns. Data Management in a Distributed File System for Storage Area Networks [D].PhD thesis, University of California Samta Curz, March 2000.[4] NFS Version 4 Protocol, RFC 3530 [EB/OL].http://www.ietf.org/rfc/rfc3530.txt?number=3530.[5] F. Schmuck, R. Haskin. GPFS: A Shared-Disk File System for Large Computing Clusters [C] //Proceedings of the 2002 Conference on File and Storage Technologies (FAST). Monterey, CA: USENIX, Jan. 2002:231-244.[6] Lustre Lockahead: Early Experience and Performance using Optimized Locking.[7] 大规模Lustre文件系统关键技术的研究 [D].

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20171215G001GP00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券