桔妹导读:Ceph是国内出名的开源分布式存储系统,在工业界和学术界都有着重要的影响。Ceph的架构和算法设计发表在国内零碎畛域顶级会议OSDI、SOSP、SC等上。Ceph社区失去Red Hat、SUSE、Intel等大公司的大力支持。Ceph是国内云计算畛域利用最宽泛的开源分布式存储系统,此外,Ceph也广泛应用在文件、对象等存储畛域。Ceph在滴滴也撑持了很多要害业务的运行。在Ceph的大规模部署和应用过程中,咱们发现了Ceph的一些性能问题。围绕Ceph的性能优化,咱们做了很多深入细致的工作。这篇文章次要介绍咱们通过调试剖析发现的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写入数据的量为31k10k,另一个pool写入数据的量为4k100k, 掂量所有申请apply的总耗时。优化前总耗时434ks, 优化后总耗时45ks,缩小89.6%。 ### !
团队介绍
滴滴云平台事业群滴滴云存储团队原隶属于滴滴根底平台部,现隶属于新成立的滴滴云事业部。团队承当着公司在线非结构化存储服务的研发,并参加运维工作。具体来说,团队承当了公司内外部业务的绝大部分的对象、块、文件存储需要,数据存储量数十PB。团队技术气氛浓重,同时具备良好的用户服务意识,立足于用技术发明客户价值,业务上谋求极致。团队对于分布式存储、互联网服务架构、Linux存储栈有着深刻的了解。
作者介绍
负责滴滴在线非结构化存储研发,曾任国防科技大学计算机学院副研究员,教研室主任,天河云存储负责人
延长浏览
内容编辑 | Charlotte
分割咱们 | DiDiTech@didiglobal.com
滴滴技术 出品