在上篇ceph性能调优历程-rocksdb篇(1)中,已经对rocksdb的细节做了初步的介绍,接下来就是关于参数调整的相关内容
写在前面
松鼠哥的ceph专业课程上线啦!
面向新手同学,从0实战,全面入门ceph安装部署与运维,有需要的同学赶紧联系松鼠哥订购吧:
继续完成分析
SST合并的控制参数
memtable:
write_buffer_size - 控制memtable的大小,ceph默认是256MB
max_write_buffer_number - 控制内存中memtable的最大数量,根据内存大小来设置
min_write_buffer_number_to_merge - 控制触发flush所需要的immutable数
level:
max_background_compactions - SST文件合并并发线程数
level0_file_num_compaction_trigger - 触发level0向level1合并所需要的level0的文件数目
max_bytes_for_level_base - level1的大小,一旦level1的文件总大小超过该值,就会触发level1向下合并
target_file_size_base - level1-levelN的单个SST文件大小
max_bytes_for_level_multiplier - level1-levelN中,相邻两个level的大小的差距,例如level1为256MB,差距为10的话,level2就为2560MB
num_levels - 最多可以有几个level
这里详细介绍一下合并操作的触发条件,了解不同level之间的合并触发是非常重要的,因为通过设置参数来控制level间的触发,才能精细控制合并带来的性能问题:
memtable -> level0 触发条件是memtable写满的个数,例如min_write_buffer_number_to_merge=2,表示,有两个memtable写满后,就会触发immutable flush到level0
level0 -> level1 触发条件是level0的文件个数,例如level0_file_num_compaction_trigger=4,就表示,当level0有4个文件的时候,就会触发level0与level1的文件发生合并,对于大小,如果level0上文件总大小超过了max_bytes_for_level_base,也会触发合并
level1 -> level2+ 触发条件是level1的文件总大小,例如max_bytes_for_level_base=67108864,表示,level1文件总大小超过64MB后,就会触发向下合并;更一般的情况是,当levelN的文件总大小超过当层的预定值时,就会触发levelN与levelN+1的合并
合并细节与读写放大
首先介绍一下读放大与写放大;所谓读放大,就是在rocksdb中,一次读所读出的总数据量/真正需要读出的数据量,例如目标数据在level3的某个sst文件中,且前面的level中都没有包含该记录,那么为了读出该记录,需要遍历memtable、level0、level1、level2及level3的部分文件;所谓写放大,就是指写入的数据总量/真正需要写入的数据总量,例如需要写入level1的数据总量是120MB,而合并的时候因为要进行排序,会读出部分level1的文件,这时候就会产生写放大
针对上述描述,这里结合rocksdb的日志来具体说明:
1 | Compaction start summary: Base version 39 Base level 1, inputs: [121(64MB) 122(3521KB)], [103(65MB) 104(64MB) 106(64MB) 107(64MB) 109(64MB) 110(64MB) 113(51MB)] |
需要读出下层文件,就是为了保证下层的所有记录都是有序且不重复的,当插入的记录应该位于某个sst文件内部位时,为了插入该数据,就不得不读出该sst文件,这是LSM-Tree的机制所决定的,在sst文件合并的时候,磁盘会产生大量的读写,更糟糕的是,为了写入一个sst文件,不惜要读出比该sst文件大得多的数据量,例如下面的写放大很惊人的情况:
1 | compacted to: base level 1 max bytes base 536870912 files[0 3 10 33 0 0 0] max score 1.07, MB/sec: 31.0 rd, 30.3 wr, level 3, files in(1, 15) out(16) MB in(262.2, 3889.9) out(4060.9), read-write-amplify(31.3) write-amplify(15.5) OK, records in: 3871217, records dropped |
总结
由此,我们基本上对rocksdb的机制和可能产生的问题有了基本了解,但是针对不同的场景,对写入的情况会有不同的要求,所以在调优前要考虑清楚,笔者的使用场景是持续大规模写入,没有删除和更新,极少的读,所以在这样的场景下,最重要的是保证集群持续写入的高性能,即使要牺牲一些读性能,也是无奈之举
下面给出一些关于参数调试的参考经验
1、由于memtable -> level0 是由memtable写满的个数触发的,因此,设置write_buffer_size=536870912并设置max_write_buffer_number=2,则内存占用会接近1G,每次flush到level0的sst文件大小为143.7MB左右,注意,所有的memtale数据会作为一个sst文件写入level0
2、level0 -> level1 是由level0的文件数触发合并,因此,设置level0_file_num_compaction_trigger=4时,会一次性将4个文件合并到level1,这个过程中,level0的全部文件和level1的部分文件将会全部被读出来,level0_file_num_compaction_trigger设置得太大会增加读放大,实测发现,level0的总大小控制得比level1的大小大,会有比较低的写放大,笔者环境中level0->level1写放大不超过2.0
3、level1->level2+ 是由每层的空间大小触发,并且在合并的时候,会读出部分levelN+1的文件,因此,level之间的差距max_bytes_for_level_multiplier不能太大,否则合并的时候会产生非常大的写放大,另外,levelN->levelN+1过程,是每次取一个levelN的sst文件进行合并,如果target_file_size_base太大,也会导致磁盘长时间处于合并的读写中,读写延时就会很高
特别说明
max_bytes_for_level_base、max_bytes_for_level_multiplier非常重要,rocksdb在进行level推进的时候,会进行levelN+1的空间剩余计算,比如:
1、level1->level2时,level1选择一个256MB的SST文件合并到level2,level2读出了4个SST文件,此时level2的那4个文件未被删除,直到合并完成后,才会进行删除
2、当levelN满时,会检查当前路径下磁盘剩余空间是否足够放下levelN+1的文件,若不满足,将把levelN+1全部直接放入db.slow中
因此,对db空间的规划需要max_bytes_for_level_base、max_bytes_for_level_multiplier、write_buffer_size等参数一起考虑,否则,一旦空间不符合rocksdb的规则,就会导致db空间的浪费及slow分区的使用,进而表现为性能的下降
假如在ceph daemon osd.x perf dump的输出中发现slow_used_bytes不为0的情况,说明悲剧已经发生,集群性能会直线下降,重新考虑一下前面的参数吧
参考资料
RocksDB-Tuning-Guide
rocksdb-tuning
Direct-IO
compaction_picker.cc
- 本文作者: 奋斗的松鼠
- 本文链接: http://www.strugglesquirrel.com/2018/05/20/ceph性能调优历程-rocksdb篇-2/
- 版权声明: 本博客所有文章除特别声明外,创作版权均为作者个人所有,未经允许禁止转载!