ceph的核心之rocksdb

最近调优及其他工作实在太忙,没有太多时间写心得,今天抽空来总结一下阶段性成果吧。从一开始的ceph调研、系统调优开始,ceph集群存储大规模数据之后(集群文件数超过2亿),rgw并发写性能下降的问题一直困扰我们,终于在最近找到了原因及相关解决办法

问题引入

在测试集群的并发性能的时候,我们注意到,经过系统调优后,集群创建初期,至少,在写入数据低于1亿文件数的时期,集群能够保持比较好的性能,并发虽然会有不少波动,但是基本能维持到7500ops(cosbench),但是再继续写,性能就会出现明显的暴跌,降低到1500ops左右,这显然是不可接受的;

我们跟踪排查后发现,每当性能下降剧烈的时候,往往是磁盘有非常厉害的读:

集群概况:
  cluster:
    id:     b4136ac5-ad0d-45b9-9f74-7e6a6f6d572c
    health: HEALTH_WARN
            noscrub,nodeep-scrub flag(s) set

  services:
    mon: 1 daemons, quorum ceph-c204
    mgr: ceph-c204(active)
    osd: 24 osds: 24 up, 24 in
         flags noscrub,nodeep-scrub
    rgw: 2 daemons active

  data:
    pools:   6 pools, 2208 pgs
    objects: 201M objects, 48946 GB
    usage:   109 TB used, 39119 GB / 147 TB avail
    pgs:     2208 active+clean

  io:
    client:   11944 kB/s rd, 390 MB/s wr, 11944 op/s rd, 26640 op/s wr
磁盘读写情况:
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           6.97    0.05    3.21    3.77    0.00   85.99

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
sda               0.00     6.00    0.00    6.00     0.00    60.00    20.00     0.01    1.00    0.00    1.00   1.00   0.60
sdb               0.00     0.00  105.00 3725.00  3320.00 29008.00    16.88     0.34    0.09    0.54    0.08   0.06  23.70
sde               0.00     0.00    5.00 2111.00    40.00  7084.00     6.73     0.23    0.11    0.60    0.11   0.11  22.60
sdm               0.00     0.00    0.00 1909.00     0.00  6996.00     7.33     0.21    0.11    0.00    0.11   0.11  21.00
sdk               0.00    64.00   16.00  523.00  2184.00 44412.00   172.90     0.67    1.24   13.81    0.86   0.61  32.80
sdh               0.00    67.00   64.00  520.00 10052.00 45028.00   188.63     2.46    4.32   20.00    2.39   0.88  51.10
sdj               0.00    84.00   33.00  596.00  4268.00 50524.00   174.22     1.81    2.75   29.61    1.26   0.88  55.50
sdl               0.00    65.00   54.00  560.00  7832.00 51004.00   191.65     2.07    3.37   20.52    1.72   0.79  48.40
sdd               0.00    75.00   49.00  575.00  7604.00 52504.00   192.65     1.29    1.98   10.96    1.22   0.69  43.00
sdg               0.00    71.00   78.00  512.00 11604.00 46308.00   196.31    24.12   39.48   98.64   30.47   1.69 100.00
sdf               0.00    93.00   21.00  589.00  2820.00 52000.00   179.74     1.49    2.43   37.38    1.18   0.74  45.30
sdi               0.00    80.00  208.00  539.00 47312.00 45628.00   248.84    39.40   49.48   82.35   36.80   1.33  99.70
sdc               0.00    67.00   64.00  539.00  9932.00 47136.00   189.28     2.30    3.68   18.72    1.90   0.99  59.60
sdn               0.00    84.00   39.00  497.00  5592.00 41968.00   177.46     2.15    3.91   27.21    2.09   0.98  52.40

根据观察,当磁盘有读操作的时候,写性能往往会下降很多,使用iotop发现,产生大量读的线程主要是[rocksdb:bg0]一类的线程,那么这些都是些什么线程,又在做什么读操作呢?不急,我们先学习一下rocksdb

从LSM-Tree说起

Rocksdb源自于Leveldb,fork了Leveldb的代码后,facebook在Leveldb的基础上做了很多的改进,然后开源并重新命名为Rocksdb,leveldb和rocksdb一样,都是基于LSM-Tree思想所设计的

LSM-TREE

LSM-Tree全称是Log-Structed-Merge-Tree,是一种数据结构思想,它的理论基础是,传统磁盘顺序读写速度比随机读写速度快得多,因此它想了个办法:将随机的读写转换成顺序读写,从而提高读写性能

LSM-Tree的组成结构是:
1、memtable - 常驻内存,主要负责保存要写入的数据
2、immutable - 当memtable写满后,就会变成只读的immutable
3、wal file - 实现log功能,掉电后用以恢复数据
4、SStable - 持久化存在磁盘中的数据库文件,保存了所有的数据

注:图片来自rocksdb detail

这里解释一下rocksdb的读写流程:

写流程

1、首先,为了保证掉电数据恢复,rocksdb先写wal中的log文件,注意,这里是追加模式写,因此速度很快
2、然后写位于内存中的memtable;
3、客户端写返回
4、rocksdb检查memtable否写满,如果写满,则memtable变成immutable,只允许读,等待flush
5、当immutable满足flush条件后,会将immutable的内容一次性flush到磁盘上,作为level0的sst文件保存在db的磁盘分区中
注意,当memtable的内容flush到磁盘上后,所有level的记录都是有序的,因此,在memtable flush到磁盘上时,会进行去重和排序

读流程

1、读请求首先到达memtable,在memtable中查找,查到即返回,否则继续查找
2、在level0中查找,因为rocksdb保证了每一个level的所有记录都是有序的,因此使用二分查找速度很快
3、在上级level中未查到,则需要继续往下查找,直到最后一个level,若仍未查到,则返回查找失败,否则,返回第一次查找到的记录

关于删除和修改

对于删除和修改,与写入并没有太大的区别,删除的时候,会写入一条kv记录,但是该kv记录带有删除标记,待进行SStable合并的时候,会进行实际的删除操作,而修改操作也是,写入一条带有修改标记的kv记录,SStable合并的时候会进行对比,保留最新的一条kv记录。

小总结

LSM-Tree为了提高io性能,将随机写进行了合并,使得每次写操作都能够在一次磁盘io和内存io后返回,速度很快,并且其规定,删除和修改也是作为一条操作记录写入,在后续才进行实际的删除和修改

Rocksdb的SST文件合并

LSM-Tree保证了写操作在一次磁盘和一次内存写后就能返回,但在leveldb/rocksdb端后续处理上,还是有很多的工作,最重要的就是数据落盘后SST文件的合并处理;对于Leveldb来说,写入磁盘后的SStable文件会被分成多个不同的level,这也是leveldb名称的由来;leveldb/rocksdb的组织方式是,当某个level的文件数量或者总大小达到预设的值后,该level的一个文件(level0为所有文件)就会与下一层的文件发生合并,合并后的文件会进入下一个level,例如level0文件大小或数量达到阈值后就会与level1的文件发生合并,原来的level1的文件就可以安全删除了,当然,在合并的时候会进行去重和排序操作,这样就能保证每一层的数据都是有序的,此时的排序相当于N路归并排序。

注:图片来自Leveled Compaction
上图可以清楚地看出,memtable刷入磁盘后形成level0的SST文件,而level0会往下合并,最终形成一棵LSM-Tree

关于rocksdb的相关内容就先做个简单的梳理,网上有非常优秀的资源对其进行了详细、深入的介绍,尤其是facebook的官方,更是给出了最权威最有价值的资料,对深入学习rocksdb提供了很大的帮助

posted @ 2020-06-30 13:40  在路上ing~~  阅读(3265)  评论(0编辑  收藏  举报