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

桔妹导读:Ceph是国际知名的开源分布式存储系统,在工业界和学术界都有着重要的影响。Ceph的架构和算法设计发表在国际系统领域顶级会议OSDI、SOSP、SC等上。Ceph社区获得Red Hat、SUSE、Intel等大公司的大力支持。Ceph是国际云计算领域应用最普遍的开源分布式存储系统,此外,Ceph也普遍应用在文件、对象等存储领域。Ceph在滴滴也支撑了不少关键业务的运行。在Ceph的大规模部署和使用过程当中,咱们发现了Ceph的一些性能问题。围绕Ceph的性能优化,咱们作了不少深刻细致的工做。这篇文章主要介绍咱们经过调试分析发现的Ceph在锁方面存在的问题和咱们的优化方法。html



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%app



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%



团队介绍

滴滴云平台事业群滴滴云存储团队原隶属于滴滴基础平台部,现隶属于新成立的滴滴云事业部。团队承担着公司在线非结构化存储服务的研发,并参与运维工做。具体来讲,团队承担了公司内外部业务的绝大部分的对象、块、文件存储需求,数据存储量数十PB。团队技术氛围浓厚,同时具有良好的用户服务意识,立足于用技术创造客户价值,业务上追求极致。团队对于分布式存储、互联网服务架构、Linux存储栈有着深刻的理解。


做者介绍

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


延伸阅读

             

内容编辑 | Charlotte
联系咱们 | DiDiTech@didiglobal.com

本文分享自微信公众号 - 滴滴技术(didi_tech)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索