大咖直播-鸿蒙原生开发与智能提效实战!>>> 了解详情
写点什么

滴滴 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:082980

评论

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

2023开源数据库排行榜发布,“新晋黑马”瀚高IvorySQL跻身三十强

极客天地

支付宝小程序云李铮:科技赋能,敏捷增长

TRaaS

支付宝小程序 小程序云开放 蚂蚁

科研类项目核算的“法、术、器”(二)

用友BIP

项目管理 科研项目

揭秘ChaosBlade CPU故障:实现CPU故障的黑科技

柠檬汁Code(binbin0325)

源码分析 cpu 混沌工程 ChaosBlade 故障模拟

火山引擎DataLeap的Data Catalog系统公有云实践 (上)

字节跳动数据平台

大数据 数据中台 数据治理 数据安全 企业号 7 月 PK 榜

Coral Finance 将为 Zepoch 节点空投,Nautilus生态空投季开启

西柚子

基于因果关系知识库的因果事件图谱构建

汀丶人工智能

人工智能 自然语言处理 知识图谱

KaiwuDB 资深解决方案专家周幸骏:打造核心时序引擎,释放数据新价值

KaiwuDB

时序数据 KaiwuDB

基于开源IM即时通讯框架MobileIMSDK:RainbowChat v9.0版已发布

JackJiang

网络编程 即时通讯 IM

中小微企业选择哪家云管平台好?理由有哪些?

行云管家

云计算 云管平台 云管理

飞桨大模型分布式训练技术

Baidu AICLOUD

飞桨 百度百舸 AI 大底座

万字长文浅析配置对MySQL服务器的影响 | 京东物流技术团队

京东科技开发者

MySQL 数据库 服务器 企业号 7 月 PK 榜 MySQL服务器

Sugar BI:大模型时代的智能 BI

Baidu AICLOUD

BI 数据智能

超强阵容!HarmonyOS极客马拉松2023专家评审团来袭!

HarmonyOS开发者

HarmonyOS

从分片传输到并行传输之大文件传输加速技术

镭速

大文件传输

HDMI接口需注意的PCB可制造性设计问题

华秋PCB

接口 工具 PCB PCB设计 可制造性

浅聊一下大模型

鲸品堂

大模型训练 大模型

数据库集群方案详解

KaiwuDB

KaiwuDB 数据库集群技术

动态QPS压测模型【Go语言】

FunTester

明晚直播:可重构计算芯片的AI创新应用分享!

飞桨PaddlePaddle

人工智能 百度 paddle 飞桨 百度飞桨

618技术揭秘 - 大促弹窗搭投实践 | 京东云技术团队

京东科技开发者

前端 弹窗 xview 企业号 7 月 PK 榜

2023年广西等保测评机构名单看这里!新增一家哦!

行云管家

广西 等级保护 等保测评

Docker 入门教程(简明易懂、零基础篇)

搞大屏的小北

Docker 容器 Docker-compose 入门 Docker 镜像

一种配置化的数据脱敏与反脱敏框架实现 | 京东云技术团队

京东科技开发者

数据安全 脱敏 数据脱敏 企业号 7 月 PK 榜

详解!视频直播源码布谷科技平台搭建开发:录制功能

山东布谷科技

软件开发 视频直播 源码搭建 短视频直播源码 视频录制

广东省《5A物理抗菌纺织品》团体标准颁布

极客天地

源码解析Collections.sort ——从一个逃过单测的 bug 说起 | 京东云技术团队

京东科技开发者

排序算法 源码解读 企业号 7 月 PK 榜 Collections.sort

中文人物关系知识图谱(含码源):中文人物关系图谱构建、数据回标、基于远程监督人物关系抽取、知识问答等应用.

汀丶人工智能

人工智能 nlp 知识图谱 智能问答

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