您当前的位置: 首页 > 

凌云时刻

暂无认证

  • 0浏览

    0关注

    1437博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

NFS Write IO 不对齐深度分析

凌云时刻 发布时间:2020-10-13 18:30:00 ,浏览量:0

 

凌云时刻 · 技术

导读:NFSClient 对大多数的应用写入没有做对齐优化,本文根据 IO 不对齐的原因给出了若干实践建议。

作者 | 裴晓辉

来源 | 凌云时刻(微信号:linuxpk)

背景

最近团队小伙伴弗曼统计了线上用户数据写入对齐情况,通过统计数据发现了一个有趣的现象: 用户写入请求中近 70% 的数据块 4K 不对齐,这也就是说 NFSClient 对大多数的应用写入没有做对齐优化。

下面会从 NFSClient BufferWrite 实现流程的维度解释 IO 不对齐的原因,最后据此给出了若干实践建议。

场景分析

应用程序一般可以使用 DirectIO 或 BufferIO 两种方式向文件写入数据。在 DirectIO 模式时 NFSClient 直接将用户 IO 通过 RPC 发送给服务端,因此 DirectIO 方式写入 NFSClient 不会做对齐,但考虑到应用程序使用 DirectIO 时一般会在应用侧做对齐,因此非对齐 IO 的多数应该是 BufferIO 方式。

内核使用 struct nfs_req 对象记录某个缓存页更改情况,同时使用 struct page 对象的 private 字段保存,因此只需分析 BufferIO 时 nfs_req 的处理逻辑就能够知道对齐规则。NFSClient 调用 nfs_updatepage() 更新 nfs_req 对象,其核心代码如下:

从上图红色框中代码可以看出来,NFSClient 会尝试按照缓存页大小对 offset/count 对齐,继续查看 nfs_can_extent_write() 函数实现:

从上述代码可知:

1) 若为同步写,则不尝试对齐,这是因为对同步写做对齐并没有明显收益还会放大 IO;

2) 若缓存页内容不是最新,则不允许对齐,否则就要读惩罚,注意 nfs_write_begin() 函数已处理是否需要读惩罚;

3) 若持有 Write Delegation,则允许对齐,因为 Write Delegation 保证本地缓存数据一定是最新,关于 Delegation 可参考文章《NFSClient Delegation 实现与陷阱》;

4) 若不持有文件锁,则允许对齐,注意此处依据 NFSClient 设计哲学的一个假设:应用在不持有文件锁写入数据时,应用侧应该保证文件不会被多客户端修改;

5) 若持有全文件的写锁,则允许对齐,因为写锁保证了本地缓存数据肯定是有效的且不存在多客户端更改,此外这里要求全文件锁的原因是为了简化代码逻辑。

合并规则

上面提到内核使用 struct nfs_req 对象表示每个缓存页的更改情况,考虑到 struct nfs_req 只能表示单区间,因此 BufferIO 需合并同一个缓存页的多次更改,合并规则较为简单:

两个写合并后必须是一个连续的区间。

相关代码实现可参考 nfs_try_to_update_request() 函数,代码较为简单,故不再详细描述。从合并规则还可以知道:同一个缓存页上的两个非连续 IO 需要两次 RPC 写入。

缓存页 UpdateToDate 设置

从场景分析中知道缓存页是否设置了 UpdateToDate 标记是同一缓存页上的两个 IO 能否合并的重要前提条件,那 NFSClient 是什么时候将缓存页设置为 UpdateToDate 状态的呢?总的来说,NFSClient 在两个地方尝试设置该标记:

1) 将缓存页加入到 address space 时,实现代码是 nfs_write_begin() 函数,相关逻辑如下:

继续查看 nfs_want_read_modify_write() 函数实现:

上图中蓝色框中是 pNFS 相关处理暂时不做讨论,红色框则是一般情况下将缓存页加入到 address space 时是否需要读惩罚(读取数据后缓存页内容自然为最新),具体的需满足如下几个条件:

a) 若应用打开文件模式允许读,则允许读惩罚,这是因为根据数据的局部性原理,刚写入的数据很可能再次读;

b) 缓存页内容不是最新,若已经是最新很显然没必要再次读老数据;

c) 当前缓存页没有正在进行的 IO,也就是说没有向服务端有读写请求,注意 nfs_req 只能表示单个 IO,显然此时不允许读;

d) 本次修改只是缓存页的局部内容,显然如果全覆盖是没有必要读入老数据;

2) 当内核将待修改的数据拷贝到缓存页后会调用 nfs_write_end() 函数通知 NFSClient 数据已经拷贝到缓存页,此时 NFSClient 需根据写入情况设置缓存页是否为最新的状态:

通过上面代码可知:

a) 蓝色框中表示缓存页是文件变大时新追加的缓存页,此时缓存页内容为全零,自然可设置为最新;

b) 红色框中表示缓存页中所有的有效数据均被覆盖,此时缓存页内容必然为最新;

总之,数据写入后若完全覆盖该缓存页的所有原有效数据,则设置为最新。

实践建议

至此基本搞清楚了 NFSClient BufferWrite 的对齐实现逻辑,感兴趣的同学可自行编写测试用例验证。结合对齐实现和测试验证,初步的可给出如下建议:

1) O_SYNC 方式不会做缓存页对齐;

2) 当文件被大量小块 IO 重复覆盖写时,可考虑用 O_RDWR 方式打开(注意副作用是会有读惩罚),有利于聚合同一个缓存页的写 IO,减少 RPC 次数;

3) 使用 O_WRONLY 方式打开时,同一缓存页的不连续更改不会做聚合,每个 IO 都会触发一次 RPC,降低访问性能;

4) 使用类 MPI 方式多客户端并发修改同一文件时,条带大小应该做到缓存页对齐,否则可能会导致数据被错误覆盖;

5) 不恰当的使用文件锁会导致不做缓存页对齐;

6) 不使用文件锁时小块 IO 可能会缓存页对齐,导致 IO 放大。

总结

缓存页对齐是提高 BufferIO 访问性能地有效手段,NFSClient 在设计上尽量会尝试缓存页对齐,但受限于 NFS 共享特性的约束,也只能对较为有限的情况做缓存页对齐,这就潜在地要求应用侧配合才可以达到最优性能。

END

往期精彩文章回顾

通过 IDEA 黑掉你

Sentinel 实战应用中的小技巧

基于 Multiple Teacher Single Student 框架的多领域对话模型

【精彩回顾】企业IT治理样板间直播

洛神云网络 SLB 负载均衡新姿势

业务中台实践助力企业数字化转型

中保车服灾备云,为保险公司“上保险”

阿里云重磅发布应用负载均衡ALB,加速企业应用交付

“电”亮数字生活,阿里云助力南方电网智能调度

阿里云为自动驾驶量身打造一体化解决方案,助力行业突破技术瓶颈

长按扫描二维码关注凌云时刻

每日收获前沿技术与科技洞见

关注
打赏
1663816507
查看更多评论
立即登录/注册

微信扫码登录

0.0411s