写在前面
ceph的对象存储关于indexless方面的资料不多,在实际应用上场景还是比较多的,所以松鼠哥还是想一下,偏应用方面的。
多说一句,有老铁问松鼠哥书还出不出。。。
啊哈哈哈~~ 鸽了挺久,有点尴尬,目前进度的话,写了6万字左右,还没有加各种图表,总体过程写写停停,所以拖了挺久的,但肯定会出的,届时还请大家多多支持,尤其是催得厉害的那几位老铁,你们可别说下次一定。
关于indexless
默认情况下,ceph的对象存储在创建用户和bucket后,使用带bi(bucket index)的方式去存储数据,简单来说就是将客户端上传的数据分成2个部分,一部分是上传对象的元数据,另外一部分数据是对象的数据部分(全指南课程已经讲得很清楚了,有兴趣的老铁赶紧下单支持^_^):
1 | [root@test-mon1 ~]$ ceph df |
将上传的对象分成元数据和数据部分的好处有几个:
- 可以针对数据特点将其指定不同的存储池,同时针对不同的存储池使用不同的磁盘,来优化数据的读写性能,例如默认情况下,元数据部分存储在xxx.buckets.index存储池中,且使用omap的方式直接放在rocksdb中存储,这部分元数据的特点是每条元数据都很小,大概200-300B,但是数量往往很大,这个index存储池的osd如果使用ssd的磁盘,那么将大大加速元数据的读写速度,使得对象读写的整体性能得到提高。
- 在逻辑上实现其他功能更友好,具体来说,就是bucket的一些功能会比较依赖这种结构,例如bucket的list操作,不需要去读取对象的数据部分,只需要读取元数据部分即可,另外,bucket的lc、gc功能,也同样只需要元数据据的支持,数据部分标记为删除,存放在队列,后台异步处理即可。
- 可以支持存储池的横向拓展,这种场景更为实用,在集群扩容的时候,在元数据存储池index容量足够的情况下,可以仅扩容数据池data,使得集群的扩容具有更大的灵活性。
- 可以实现一定程度的故障隔离,某个池故障了可以不影响另外一些存储池的功能。
- 不同存储池可以使用不同的策略,例如分布在ssd上的index存储池使用三副本(胆子大的可以两副本)。而数据池可以使用EC。
- 等等。。。。
说完了它的好处,还要说说它的弊端,要是只有好处没有弊端,那还搞什么indexless是吧,它的弊端包括:
- 元数据池的bi数据,在数量非常大的情况下,rocksdb的问题很突出,一方面rocksdb的compaction对性能的影响很大,当不同level的sst做compaction时,会产生很高的读写,榨干磁盘io性能,哪怕是nvme盘,也不能避免对业务的读写影响,而且rocksdb的空间放大不容忽视(毕竟ssd比较贵),另外一方面,当数据量大时,osd的震荡(比如经常up/down,重启等)产生的影响持续时间长,有时候重启一个osd需要差不多20min,多个osd同时震荡的话集群可能完全不可用。
- shard的问题,当shard设置不合理需要reshard,或者使用了动态reshard时,相信遇到过的老铁都知道它有多坑,你的bucket将长时间不可用,对生产来说简直就是噩梦。
- 实现逻辑有点硬伤,比如bucket的list,如果单bucket存了亿级数据,它经常list的话,index池很快就有slow req了,因为资源都给list了,遍历一个这么大的bucket,shard会长时间锁定,其他op就都在
wait for rw lock了。 - 等等。。。
当然了,针对它的坑,都可以提出一些比较有效的解决方案,像设计时搞大shard值,深度调优rocksdb使得它的compaction更为平滑,不至于影响正常业务等等,都可以去做,这里我们展开介绍的indexless则是另外的思路,就是完全不存储它的元数据,只存储数据部分,相对于有bi的方式,去bi的indexless好处还是不少的,先说它的好处吧:
- 不存储元数据意味着可以省点钱。毕竟元数据通常放在ssd/nvme上,这种盘不便宜。
- 性能更好。对象上传的流程中去掉了写元数据这一步,总体性能肯定是有提高的。
- 不容易出问题了。ceph的对象存储稳如老狗,出问题很多是bi导致的,去bi直接使用EC的data池挺香的。
- 等等。。。
弊端呢?
- 不能做一些基本动作了。比如bucket list,gc、lc等,因为没有了元数据,rgw无从下手。
- 容易出现空间泄露。因为写进bucket的对象没有办法进行遍历,如果其他地方没有记录写入bucket的对象ley,那么不知道key就没办法读到它(rados中其实还能捞到,不过量大的话实现起来挺麻烦的),后面就没办法删了,这是比较大的隐患。
因此,为了能够保证一定的bucket功能,在使用对象存储的indexless时,应该同时配一个元数据组件,记录bucket中的元数据,最起码的key还是要记录的吧,比如有的场景就是用mongoDB集群来记录bucket的key,具体来说就是客户端在上传对象完成后,往mongoDB中写入一条记录,记下bucket+key,这样就可以知道bucket中有哪些对象了,但是其他非必要的元数据信息就不用记录,而且不会有compaction之类的问题,也没有什么读写放大和空间放大的烦恼,这种db的性能也不错啊,mongoDB好像也很成熟?至于管理嘛,DBA给我上~
使用indexless
要使用ceph的对象存储indexless功能,一般来说有2种情况:
- 1、s3用户直接使用indexless的功能。这种情况是将s3用户绑定到某个指定为indexless的zone来实现的,首先需要创建indexless的zone,例如下面是官方的zone例子:
1 | $ radosgw-admin zone get |
也就是创建zone的时候,将index_type指定为1即可。
创建好indexless的zone之后,创建s3用户并绑定到这个zone即可,还是官方的例子:
1 | $ radosgw-admin metadata get user:<user-id> > user.json |
绑定完成后,该s3用户后续创建的bucket全部都是indexless了,但是这个s3用户之前创建的bucket是不变的。
- 2、将现有的一个bucket从index转变为indexless。这种情况可以对一个已经有数据的bucket做indexless转变,也可以对一个没有数据的bucket做更改,但是,如果bucket原本是有数据的(带bi),做了indexless后,它的元数据就不再访问了,rgw认为它就是一个indexless的bucket,它原本的shard可以删掉,也可以不管它:
1 | 首先导出bucket的元数据 |
导入bucket元数据后立刻生效,这个bucket就转变为indexless了,至此该bucket不能进行list,也无法使用gc和lc功能,当写入该bucket时,对应的index中原来的shard将不再操作,另外,使用radosgw-admin bucket rm --bucket=xx --purge-data这种命令是无效的,因为purge-data需要Index支持,才能知道bucket里面有哪些对象。
最后
ceph的对象存储,bi的设计有利有弊,选择哪一种模式上生产要具体问题具体分析,权衡利弊,玩法也很多,bi单独存mongoDB还是别的sql DB,激进一点放redis?欢迎留言讨论你的场景~
- 本文作者: 奋斗的松鼠
- 本文链接: http://www.strugglesquirrel.com/2026/09/29/ceph对象存储的indexless/
- 版权声明: 本博客所有文章除特别声明外,创作版权均为作者个人所有,未经允许禁止转载!