50万奖金+官方证书,深圳国际金融科技大赛正式启动,点击报名 了解详情
写点什么

滴滴 Ceph 分布式存储系统优化之锁优化

  • 2020-09-08
  • 本文字数:3508 字

    阅读完需:约 12 分钟

滴滴Ceph分布式存储系统优化之锁优化

1. 背景

在支撑一些延迟敏感的在线应用过程中,我们发现 Ceph 的尾延迟较差,当应用并发负载较高时,Ceph 很容易出现延迟的毛刺,对延迟敏感的应用造成超时甚至崩溃。我们对 Ceph 的尾延迟问题进行了深入细致的分析和优化。造成尾延迟的一个重要原因就是代码中锁的使用问题,下面根据锁问题的类型分别介绍我们的优化工作。本文假设读者已熟悉 Ceph 的基本读写代码流程,代码的版本为 Luminous。

2. 持锁时间过长

2.1 异步读优化

Ceph 的 osd 处理客户端请求的线程池为 osd_op_tp,在处理操作请求的时候,线程会先锁住操作对应 pg 的 lock。其中,处理对象读请求的代码如下图所示,在锁住对象所属 pg 的 lock 后,对于最常用的多副本存储方式,线程会同步进行读操作,直到给客户端发送返回的数据后,才会释放 pg lock。


在进行读操作时,如果数据没有命中 page cache 而需要从磁盘读,是一个耗时的操作,并且 pg lock 是一个相对粗粒度的锁,在 pg lock 持有期间,其它同属一个 pg 的对象的读写操作都会在加锁上等待,增大了读写延迟,降低了吞吐率。同步读的另一个缺点是读操作没有参与流量控制。



我们对线上集群日志的分析也验证了上述问题,例如,一个日志片段如下图所示,图中列举了两个 op 的详细耗时信息,这两个 op 均为同一个 osd 的线程所执行,且操作的是同一个 pg 的对象。根据时间顺序,第一个 op 为 read,总耗时为 56ms。第二个 op 为 write,总耗时为 69ms。图中信息显示,第二个 op 处理的一个中间过程,即副本写的完成消息在处理之前,在 osd 请求队列中等待了 36ms。结合上图的代码可以知道,这 36ms 都是耗在等待 pg lock 上,因为前一个 read 操作持有 pg lock,而两个对象属于相同 pg。



我们的优化如下图所示,我们创建了独立的读线程,负责处理读请求,osd_op_tp 线程只需将读请求提交到读线程的队列即可返回解锁,大大减少了 pg lock 的持有时间。读线程完成磁盘读之后,将结果放到 finisher 线程的队列,finisher 线程重新申请 pg lock 后负责后续处理,这样将耗时的磁盘访问放在了不持有 pg lock 的流程中,结合我们在流量控制所做的优化,读写操作可以在统一的框架下进行流量控制,从而精准控制磁盘的利用率,以免磁盘访问拥塞造成尾延迟。



我们用 fio 进行了异步读优化效果的测试,测试方法:对同一个 pool 的两个 rbd,一个做随机读,另一个同时做随机写操作,将 pg number 配置为 1,这样所有对象读写会落到同一个 osd 的同一个 pg。异步读优化后,随机写平均延迟下降了 53% 。下图为某业务的 filestore 集群异步读上线前后读吞吐率的数据,箭头所指为上线时间,可见上线之后,集群承载的读操作的吞吐率增加了 120%



上述优化在使用 filestore 存储后端时取得了明显的效果,但在使用 bluestore 存储后端时,bluestore 代码中还存在持有 pg 粒度锁同步读的问题,具体见 BlueStore::read 的代码。我们对 bluestore 的读也进行了异步的优化,这里就不详细介绍了。

3. 锁粒度过粗

3.1 object cache lock 优化

Ceph 在客户端实现了一个基于内存的 object cache,供 rbd 和 cephfs 使用。但 cache 只有一把大的互斥锁,任何 cache 中对象的读写都需要先获得这把锁。在使用写回模式时,cache flusher 线程在写回脏数据之前,也会锁住这个锁。这时对 cache 中缓存对象的读写都会因为获取锁而卡住,使读写延迟增加,限制了吞吐率。


我们实现了细粒度的对象粒度的锁,在进行对象的读写操作时,只需获取对应的对象锁,无需获取全局锁。只有访问全局数据结构时,才需要获取全局锁,大大增加了对象间操作的并行。并且对象锁采用读写锁,增加了同一对象上读的并行。测试表明,高并发下 rbd 的吞吐率增加了超过 20%

4. 不必要的锁竞争

4.1 减少 pg lock 竞争

Ceph 的 osd 对客户端请求的处理流程为,messenger 线程收到请求后,将请求放入 osd_op_tp 线程池的缓存队列。osd_op_tp 线程池的线程从请求缓存队列中出队一个请求,然后根据该请求操作的对象对应的 pg 将请求放入一个与 pg 一一对应的 pg slot 队列的尾部。然后获取该 pg 的 pg lock,从 pg slot 队列首部出队一个元素处理。


可见,如果 osd_op_tp 线程池的请求缓存队列中连续两个请求操作的对象属于相同的 pg,则一个 osd_op_tp 线程出队前一个请求加入 pg slot 队列后,获取 pg lock,从 pg slot 队列首部出队一个请求开始处理。另一个 osd_op_tp 线程从请求缓存队列出队第二个请求,因为两个请求是对应相同的 pg,则它会加入相同的 pg slot 队列,然后,第二个线程在获取 pg lock 时会阻塞。这降低了 osd_op_tp 线程池的吞吐率,增加了请求的延迟。


我们的优化方式是保证任意时刻每个 pg slot 队列只有一个线程处理。因为在处理 pg slot 队列中的请求之前需要获取 pg lock,因此同一个 pg slot 队列的请求是无法并行处理的。我们在每个 pg slot 队列增加一个标记,记录当前正在处理该 pg slot 的请求的线程。当有线程正在处理一个 pg slot 的请求时,别的线程会跳过处理该 pg slot,继续从 osd_op_tp 线程池的请求缓存队列出队请求。

4.2 log lock 优化

Ceph 的日志系统实现是有一个全局的日志缓存队列,由一个全局锁保护,由专门的日志线程从日志缓存队列中取日志打印。工作线程提交日志时,需要获取全局锁。日志线程在获取日志打印之前,也需要获取全局锁,然后做一个交换将队列中的日志交换到一个临时队列。另外,当日志缓存队列长度超过阈值时,提交日志的工作线程需要睡眠等待日志线程打印一些日志后,再提交。锁的争抢和等待都增加了工作线程的延迟。


我们为每个日志提交线程引入一个线程局部日志缓存队列,该队列为经典的单生产者单消费者无锁队列。线程提交日志直接提交到自己的局部日志缓存队列,该过程是无锁的。只有队列中的日志数超过阈值后,才会通知日志线程。日志线程也会定期轮询各个日志提交线程的局部日志缓存队列,打印一些日志,该过程也是无锁的。通过上述优化,基本避免了日志提交过程中因为锁竞争造成的等待,降低了日志的提交延迟。测试在高并发日志提交时,日志的提交延迟可降低接近 90%

4.3 filestore apply lock 优化

对于 Ceph filestore 存储引擎,同一个 pg 的 op 需要串行 apply。每个 pg 有一个 OpSequencer(简称 osr),用于控制 apply 顺序,每个 osr 有一个 apply lock 以及一个 op 队列。对于每个待 apply 的 op,首先加入对应 pg 的 osr 的队列,然后把 osr 加到 filestore 的负责 apply 的线程池 op_tp 的队列,简称为 apply 队列。op_tp 线程从 apply 队列中取出一个 osr,加上它的 apply lock,再从 osr 的队列里取出一个 op apply,逻辑代码如下图左所示。可见,每个 op 都会把其对应的 osr 加入到 apply 队列一次。如果多个 op 是针对同一个 pg 的对象,则这个 pg 的 osr 可能多次加入到 apply 队列。如果 apply 队列中连续两个 osr 是同一个 pg 的,也就是同一个 osr,则前一个 op 被一个线程进行 apply 时,osr 的 apply lock 已经加锁,另一个线程会在该 osr 的 apply lock 上阻塞等待,降低了并发度。



这个问题也体现在日志中。一个线上集群日志片段如下图,有两个 op_tp 线程 6700 和 5700,apply 队列里三个对象依次来自 pg: 1.1833, 1.1833. 1.5f2。线程 6700 先拿到第一个对象进行 apply, 线程 5700 拿第二个对象进行 apply 时卡在 apply lock 上,因为两个对象都来自 pg 1.1833,直到 6700 做完才开始 apply。而 6700 拿到第三个对象,即 1.5f2 的对象进行 apply 即写 page cache 只用了不到 1ms,但实际 apply 延迟 234ms,可见第三个对象在队列里等待了 233ms。如果 5700 不用等待 apply lock,则第二和第三个对象的 apply 延迟可以大大缩短。



我们优化后的逻辑代码如上图右所示,同一个 osr 只加入 apply 队列一次,取消 apply lock,利用原子操作实现无锁算法。上面的算法可以进一步优化,在将一个 osr 出队之后,可以一次从它的队列中取 m(m>1)个 op 进行 apply,在 op apply 完成阶段,改为如果 atomic::fetch_sub(osr->queue_length, m) > m,则将 osr 重新入队以提高吞吐率。


我们用 fio 进行了 apply lock 优化效果测试,方法为建两个 pool,每个 pool 的 pg number 为 1,每个 pool 一个 rbd, 对两个 rbd 同时进行随机写的操作,一个 pool 写入数据的量为 31k*10k,另一个 pool 写入数据的量为 4k*100k, 衡量所有请求 apply 的总耗时。优化前总耗时 434ks, 优化后总耗时 45ks,减少 89.6%


作者介绍


汪黎,滴滴首席工程师


负责滴滴在线非结构化存储研发,曾任国防科技大学计算机学院副研究员,教研室主任,天河云存储负责人


本文转载自公众号滴滴技术(ID:didi_tech)。


原文链接


滴滴Ceph分布式存储系统优化之锁优化


2020-09-08 10:082938

评论

发布
暂无评论
发现更多内容

NebulaAI携手Eolink:AI落地,快人一步

行云创新

API 接口 AI Agent AI 智能体

(网页CAD SDK)在线CAD中线型表的二次开发

WEB CAD SDK

网页CAD 在线CAD

GitLab 发布安全版本(修复多个安全漏洞)

极狐GitLab

gitlab 安全漏洞

全球通信云服务最佳基础设施「融云」,受邀参加 Singapore FinTech Festival

融云 RongCloud

柔性算力、业界首创!双十一华为云Flexus X实例革新云服务体验

YG科技

Go Web服务中如何优雅关机?

左诗右码

AI 1.0公司的节节败退

脑极体

AI

Rust + 云原生 | Greptime 在 COSCon'24 中国开源年会上说了什么?

Greptime 格睿科技

数据库 开源 rust 云原生 活动

SEO-AI内容生成策略

Quincy

CMS SEO SEO优化 AI 基础设施

移动端弱网优化专题(十四):携程APP移动网络优化实践(弱网识别篇)

JackJiang

即时通讯;IM;网络编程

东南大学鲲鹏昇腾科教创新孵化中心正式成立  助力科研创新与人才培养

极客天地

客户案例|智能进化:通过大模型重塑企业智能客服体验

澜舟孟子开源社区

人工智能 智能体 智能客服 大模型

柔性算力、行业首创!双十一华为云Flexus云服务器X实例革新轻量云体验

YG科技

LowCode:低代码平台,2024国内十大主流低代码平台年终盘点

优秀

低代码 低代码开发 低代码开发平台 低代码平台 低代码paas平台

Cuimin

陈皮

火山引擎AI for Science研讨会与Bio-OS大赛收官,“四驱飞轮”助力科研提效

新消费日报

行业首创,性能更强!双十一华为云Flexus云服务器X实例重新定义性价比

YG科技

【教程】第六章:合作伙伴——协作无间,灵活掌控

NocoBase

开源 低代码 零代码 教程 无代码

ECCV2024 | 京东零售广告创意:基于人类反馈的可信赖图像生成

京东零售技术

论文解读 ECCV 2024

文心快码3.0 引领智能代码助手率先进入智能体时代

百度安全

滴滴Ceph分布式存储系统优化之锁优化_数据库_滴滴技术_InfoQ精选文章