背景node
企业级存储中,SSD+HDD的混合盘是一种典型应用场景,能够兼顾成本、性能与容量。ios
但网易数帆存储团队通过测试(4k随机写)发现,加了NVMe SSD作Ceph的WAL和DB后,性能提高不足一倍且NVMe盘性能余量较大。因此咱们但愿经过瓶颈分析,探讨可以进一步提高性能的优化方案。git
测试环境
Ceph性能分析通常先用单OSD来分析,这样能够屏蔽不少方面的干扰。 咱们的测试环境以下所示,1个OSD:github
usrname@hostname:~/cluster$ sudo ceph osd tree ID CLASS WEIGHT TYPE NAME STATUS REWEIGHT PRI-AFF -5 1.09099 root single-wal-db -6 1.09099 host single-wal-db-69 1 hdd 1.09099 osd.1 up 1.00000 1.00000 ====== osd.1 ======= devices /dev/sdc [ wal] /dev/sdv2 PARTUUID 6b3f8b48-99ad-4ede-a4ab-0c23d5b5e162 [ db] /dev/sdv1 PARTUUID b7debf8e-0907-4b80-90b9-04443a2e5c82
性能优化初览
上图主要是BlueStore 对于defer write写的一个整体流程。能够看到这里的性能优化主要是两个点:docker
1. 重耗时模块影响上下文api
- 问题:返回客户端写成功函数+落盘在同一个线程(_kv_finalize_thread)
- 优化:返回写成功前保证元数据写成功便可,故可把这两个阶段拆分到不一样的线程:
2. 重耗时模块在IO核心路径数组
- 问题:刷盘函数fdatasync在IO关键路径上(kv_sync_thread)
- 优化:函数目的是确保数据落盘。故可把其移动到非IO核心路径(_deferred_aio_finish)
IO瓶颈分析
[global] ioengine=rbd pool=single_wal_db rbdname=volume01(100g) invalidate=0 rw=randwrite bs=4k runtime=180 [rbd_iodepth32] iodepth=128 write: IOPS=1594, BW=6377KiB/s (6530kB/s)(374MiB/60106msec); 0 zone resets slat (nsec): min=1249, max=721601, avg=5098.70, stdev=6069.83 clat (usec): min=1157, max=589139, avg=80279.11, stdev=77925.94 lat (usec): min=1166, max=589141, avg=80284.20, stdev=77926.06 # osd "op_w_latency": { "avgcount": 95824, "sum": 7593.745711498, "avgtime": 0.079246803 }, "op_w_process_latency": { "avgcount": 95824, "sum": 597.747938957, "avgtime": 0.006237977 }, "op_before_queue_op_lat": { "avgcount": 95887, "sum": 3.172325348, "avgtime": 0.000033083 }, "op_before_dequeue_op_lat": { "avgcount": 95895, "sum": 7001.039474373, "avgtime": 0.073007346 }, # bluestore "state_kv_queued_lat": { "avgcount": 95858, "sum": 103.287853014, "avgtime": 0.001077508 }, "state_kv_commiting_lat": { "avgcount": 95858, "sum": 49.291618042, "avgtime": 0.000514214 }, "throttle_lat": { "avgcount": 95858, "sum": 280.404541330, "avgtime": 0.002925207 }, "commit_lat": { "avgcount": 95858, "sum": 436.058305735, "avgtime": 0.004549002 },
代码深度分析与代码优化
从上面耗时分析能够看出,op_before_dequeue_op_lat这个阶段的耗时占了大头,从以下代码能够看出,该阶段是从收到op到op出队列的时间:性能优化
void OSD::dequeue_op() { utime_t now = ceph_clock_now(); utime_t latency = now - op->get_req()->get_recv_stamp(); logger->tinc(l_osd_op_before_dequeue_op_lat, latency); pg->do_request(op, handle); }
另外发现还有一个关键阶段的耗时统计,即op_before_queue_op_lat,如以下代码能够看出,该阶段是从收到op到op入队列以前的时间:app
void OSD::enqueue_op(spg_t pg, OpRequestRef& op, epoch_t epoch) { utime_t latency = ceph_clock_now() - op->get_req()->get_recv_stamp(); ogger->tinc(l_osd_op_before_queue_op_lat, latency); op_shardedwq.queue(make_pair(pg, PGQueueable(op, epoch))); }
从OSD的时延统计能够看出,op_before_dequeue_op_lat耗时很长,可是op_before_queue_op_lat耗时很短,这能够说明耗时主要花费在工做线程入队到出队这块。async
基于这个认识,因此首先考虑到的即是PG锁或者线程数太少处理不过来,第一步即是考虑调大Ceph逻辑pool的pg数,可是调大后,发现性能未有改变;因此进一步考虑调大线程数量,以下:
#osd_op_num_shards_hdd = 5(10) #osd_op_num_threads_per_shard_hdd = 1(2) write: IOPS=1571, BW=6286KiB/s (6437kB/s)(369MiB/60057msec); 0 zone resets slat (nsec): min=1169, max=459761, avg=4347.07, stdev=5058.94 clat (usec): min=1903, max=8069.2k, avg=81438.80, stdev=294739.08 lat (usec): min=1919, max=8069.2k, avg=81443.15, stdev=294739.04 # osd "op_w_latency": { "avgcount": 94385, "sum": 7544.120278825, "avgtime": 0.079929228 }, "op_w_process_latency": { "avgcount": 94385, "sum": 5289.258407670, "avgtime": 0.056039184 }, "op_before_queue_op_lat": { "avgcount": 94420, "sum": 2.903951913, "avgtime": 0.000030755 }, "op_before_dequeue_op_lat": { "avgcount": 94421, "sum": 2255.264719617, "avgtime": 0.023885202 }, # bluestore "state_kv_queued_lat": { "avgcount": 94391, "sum": 899.530260004, "avgtime": 0.009529830 }, "state_kv_commiting_lat": { "avgcount": 94391, "sum": 86.274165030, "avgtime": 0.000914008 }, "throttle_lat": { "avgcount": 94391, "sum": 804.876332278, "avgtime": 0.008527045 }, "commit_lat": { "avgcount": 94391, "sum": 1795.371420011, "avgtime": 0.019020578 }, # finish "finisher-finisher-0": { "queue_len": 0, "complete_latency": { "avgcount": 2927, "sum": 50.626880235, "avgtime": 0.017296508 } },
发现op_before_dequeue_op_lat的时延仍是很长,且这个时候发现finish线程的耗时显著拉长了。此时尝试增大finish线程数量(bluestore_shard_finishers=true),发现finish线程耗时下去了,可是总体耗时并未变短。
经过以上的优化,发现iops并未获得提高,只是耗时的时间段发生了迁移而已。可是耗时时间段无论怎么迁移,op_before_dequeue_op_lat阶段的耗时都很长且磁盘的使用率都基本接近100%。
基于此,但愿观察下OSD进程的IO吞吐状况,经过iotop观察IO吞吐状况,发现一个异常现象,以下:
633 be/3 root 0.00 B/s 102.37 K/s 0.00 % 17.85 % [jbd2/sda2-8] 4803 be/4 root 0.00 B/s 58.50 K/s 0.00 % 6.77 % etcd --advertise-client-urls=http://10.185.0.69:2379 --auto~=etcd1 --peer-client-cert-auth=false --snapshot-count=10000 4805 be/4 root 0.00 B/s 40.22 K/s 0.00 % 4.57 % etcd --advertise-client-urls=http://10.185.0.69:2379 --auto~=etcd1 --peer-client-cert-auth=false --snapshot-count=10000 7774 be/4 root 0.00 B/s 29.25 K/s 0.00 % 2.33 % kubelet --node-ip 10.185.0.69 --allowed-unsafe-sysctls=kern~.io/master=,beta.kubernetes.io/instance-type=bareMetal -v=4 12193 be/4 root 0.00 B/s 10.97 K/s 0.00 % 1.97 % etcd --advertise-client-urls=http://10.185.0.69:2379 --auto~=etcd1 --peer-client-cert-auth=false --snapshot-count=10000 4796 be/4 root 0.00 B/s 7.31 K/s 0.00 % 1.10 % etcd --advertise-client-urls=http://10.185.0.69:2379 --auto~=etcd1 --peer-client-cert-auth=false --snapshot-count=10000 4855 be/4 root 0.00 B/s 14.62 K/s 0.00 % 1.07 % kube-apiserver --authorization-mode=Node,RBAC --advertise-a~tls-private-key-file=/etc/kubernetes/pki/apiserver.key -v=4 1385 be/4 root 0.00 B/s 3.66 K/s 0.00 % 0.93 % rsyslogd -n -iNONE [rs:main Q:Reg] 444638 be/4 ceph 0.00 B/s 7.31 K/s 0.00 % 0.86 % ceph-mon -f --cluster ceph --id pubt1-ceph69 --setuser ceph --setgroup ceph [safe_timer] 444633 be/4 ceph 0.00 B/s 7.31 K/s 0.00 % 0.46 % ceph-mon -f --cluster ceph --id pubt1-ceph69 --setuser ceph --setgroup ceph [fn_monstore] 114000 be/4 ceph 186.47 K/s 40.22 K/s 0.00 % 0.33 % ceph-osd -f --cluster ceph --id 1 --setuser ceph --setgroup ceph [tp_osd_tp] 113997 be/4 ceph 186.47 K/s 58.50 K/s 0.00 % 0.31 % ceph-osd -f --cluster ceph --id 1 --setuser ceph --setgroup ceph [tp_osd_tp] 114001 be/4 ceph 175.50 K/s 3.66 K/s 0.00 % 0.27 % ceph-osd -f --cluster ceph --id 1 --setuser ceph --setgroup ceph [tp_osd_tp] 113999 be/4 ceph 168.18 K/s 36.56 K/s 0.00 % 0.24 % ceph-osd -f --cluster ceph --id 1 --setuser ceph --setgroup ceph [tp_osd_tp] 113998 be/4 ceph 131.62 K/s 25.59 K/s 0.00 % 0.24 % ceph-osd -f --cluster ceph --id 1 --setuser ceph --setgroup ceph [tp_osd_tp] 113866 be/4 ceph 0.00 B/s 12.77 M/s 0.00 % 0.03 % ceph-osd -f --cluster ceph --id 1 --setuser ceph --setgroup ceph [bstore_kv_sync] 113864 be/4 ceph 0.00 B/s 4.65 M/s 0.00 % 0.00 % ceph-osd -f --cluster ceph --id 1 --setuser ceph --setgroup ceph [dfin] 113867 be/4 ceph 0.00 B/s 950.61 K/s 0.00 % 0.00 % ceph-osd -f --cluster ceph --id 1 --setuser ceph --setgroup ceph [bstore_kv_final]
如上,能够看到tp_osd_tp线程居然有持续的IO写入。这一点很奇怪,由于该线程是osd层线程,在个人理解里面,只有store层才会有IO写入。
因此开始分析OSD层IO代码,最终找到了IO写入的地方,以下:
int BlueStore::queue_transactions() { utime_t tstart = ceph_clock_now(); throttle_bytes.get(txc->cost); if (txc->deferred_txn) { if (!throttle_deferred_bytes.get_or_fail(txc->cost)) { ++deferred_aggressive; deferred_try_submit(); } } } BlueStore::deferred_try_submit() --> BlueStore::_deferred_submit_unlock --> bdev->aio_submit // 向磁盘提交io
从代码能够看出,若是达到限流了,那么便会调用deferred_try_submit函数:而deferred_try_submit函数最终会向磁盘提交io。
接下来增大throttle参数(bluestore_throttle_bytes 与bluestore_throttle_deferred_bytes ),开始新一轮的io测试,此时依然使用iotop观察,发现tp_osd_tp线程已经没有IO写入了,达到了预期,可是此时fio的延时仍然没有减小,以下:
write: IOPS=1637, BW=6548KiB/s (6705kB/s)(384MiB/60072msec); 0 zone resets slat (nsec): min=1258, max=165928, avg=2967.35, stdev=3197.09 clat (usec): min=1459, max=912584, avg=78184.47, stdev=66475.08 lat (usec): min=1483, max=912587, avg=78187.44, stdev=66474.83 # osd "op_w_latency": { "avgcount": 98340, "sum": 7232.328193895, "avgtime": 0.073544114 }, "op_w_process_latency": { "avgcount": 98340, "sum": 7225.606938417, "avgtime": 0.073475767 }, "op_before_queue_op_lat": { "avgcount": 98405, "sum": 3.198549769, "avgtime": 0.000032503 }, "op_before_dequeue_op_lat": { "avgcount": 98625, "sum": 63.275331523, "avgtime": 0.000641574 }, # bluestore "state_kv_queued_lat": { "avgcount": 98376, "sum": 149.342751192, "avgtime": 0.001518081 }, "state_kv_commiting_lat": { "avgcount": 98376, "sum": 7050.792108282, "avgtime": 0.071671872 }, "throttle_lat": { "avgcount": 98376, "sum": 0.119449686, "avgtime": 0.000001214 }, "submit_lat": { "avgcount": 98376, "sum": 4.676406535, "avgtime": 0.000047536 }, "commit_lat": { "avgcount": 98376, "sum": 7204.522819923, "avgtime": 0.073234557 },
此时op_before_dequeue_op_lat的延时已经不多了,可是延时的大头又到了state_kv_commiting_lat阶段,接下来就要看这个阶段的代码逻辑了:
void BlueStore::_kv_sync_thread() { for (auto txc : kv_committing) { if (txc->state == TransContext::STATE_KV_QUEUED) { txc->log_state_latency(logger, l_bluestore_state_kv_queued_lat); int r = cct->_conf->bluestore_debug_omit_kv_commit ? 0 : db->submit_transaction(txc->t); } txc->state = TransContext::STATE_KV_SUBMITTED; } // other } void BlueStore::_kv_finalize_thread() { while (true) { if (kv_committing_to_finalize.empty() &&deferred_stable_to_finalize.empty()) { kv_finalize_cond.wait(l); } else { while (!kv_committed.empty()) { TransContext *txc = kv_committed.front(); // 在这个函数里面会调用 _txc_committed_kv 函数,该函数表示写io完成。 // 最后会调用txc->log_state_latency(logger, l_bluestore_state_kv_committing_lat);也即从这里到上面的txc->log_state_latency(logger, l_bluestore_state_kv_queued_lat);程序语句中间便是state_kv_commiting_lat阶段 _txc_state_proc(txc); kv_committed.pop_front(); } for (auto b : deferred_stable) { auto p = b->txcs.begin(); while (p != b->txcs.end()) { TransContext *txc = &*p; p = b->txcs.erase(p); // unlink here because _txc_state_proc(txc); // this may destroy txc } delete b; } deferred_stable.clear(); if (!deferred_aggressive) { if (deferred_queue_size >= deferred_batch_ops.load() ||throttle_deferred_bytes.past_midpoint()) { deferred_try_submit(); } } } }
state_kv_commiting_lat耗时表示的是txc->log_state_latency(logger, l_bluestore_state_kv_queued_lat)到txc->log_state_latency(logger, l_bluestore_state_kv_committing_lat)阶段的耗时,因此分析这之间的程序语句。这个阶段也包括线程切换。
仔细查看该阶段代码,始终没想出来哪一个地方会耗时这么多。而后意识到_kv_finalize_thread是信号驱动的线程,那么有可能信号即便来了,因为该线程一直在忙其余的事情,txc->log_state_latency(logger, l_bluestore_state_kv_queued_lat)阶段前面的任务也没有处理。
因此继续查看_kv_finalize_thread函数txc->log_state_latency(logger, l_bluestore_state_kv_queued_lat)以后的代码,这一看,就看到了熟悉的函数deferred_try_submit,那么显然在线程里面存在IO提交,使用iotop,发现确实有_kv_finalize_thread线程。以下:
78739 be/4 ceph 0.00 B/s 5.36 M/s 0.00 % 97.70 % ceph-osd -f --cluster ceph --id 1 --setuser ceph --setgroup ceph [bstore_kv_final] 633 be/3 root 0.00 B/s 7.31 K/s 0.00 % 5.61 % [jbd2/sda2-8] 3937 be/4 root 0.00 B/s 7.31 K/s 0.00 % 3.31 % kubelet --node-ip 10.185.0.69 --allowed-unsafe-sysctls=kern~.io/master=,beta.kubernetes.io/instance-type=bareMetal -v=4 13694 be/4 root 0.00 B/s 21.92 K/s 0.00 % 2.98 % etcd --advertise-client-urls=http://10.185.0.69:2379 --auto~=etcd1 --peer-client-cert-auth=false --snapshot-count=10000 4796 be/4 root 0.00 B/s 3.65 K/s 0.00 % 1.01 % etcd --advertise-client-urls=http://10.185.0.69:2379 --auto~=etcd1 --peer-client-cert-auth=false --snapshot-count=10000 5101 be/4 root 0.00 B/s 7.31 K/s 0.00 % 0.93 % kube-apiserver --authorization-mode=Node,RBAC --advertise-a~tls-private-key-file=/etc/kubernetes/pki/apiserver.key -v=4 444633 be/4 ceph 0.00 B/s 7.31 K/s 0.00 % 0.89 % ceph-mon -f --cluster ceph --id pubt1-ceph69 --setuser ceph --setgroup ceph [fn_monstore] 444638 be/4 ceph 0.00 B/s 7.31 K/s 0.00 % 0.81 % ceph-mon -f --cluster ceph --id pubt1-ceph69 --setuser ceph --setgroup ceph [safe_timer] 24086 be/4 root 0.00 B/s 7.31 K/s 0.00 % 0.63 % etcd --advertise-client-urls=http://10.185.0.69:2379 --auto~=etcd1 --peer-client-cert-auth=false --snapshot-count=10000 1385 be/4 root 0.00 B/s 0.00 B/s 0.00 % 0.62 % rsyslogd -n -iNONE [rs:main Q:Reg] 2452 be/4 root 0.00 B/s 3.65 K/s 0.00 % 0.00 % dockerd -H fd:// --containerd=/run/containerd/containerd.sock 78738 be/4 ceph 0.00 B/s 8.66 M/s 0.00 % 0.00 % ceph-osd -f --cluster ceph --id 1 --setuser ceph --setgroup ceph [bstore_kv_sync] 706587 be/4 ceph 0.00 B/s 3.65 K/s 0.00 % 0.00 % ceph-osd -f --cluster ceph --id 0 --setuser ceph --setgroup ceph [bstore_kv_sync]
在kv_sync_thread线程中已经完成了元数据的提交,而后其经过信号量通知_kv_finalize_thread线程遍历已经完成元数据提交的IO,处理接下来的IO回复。
仔细思考发现这里存在一个比较大的时间剪刀差:由于kv_sync_thread提交元数据到SSD,而_kv_finalize_thread线程是提交IO到HDD; 因此这里就想到能够把IO回复这个动做迁移到kv_sync_thread线程中处理。
另外,在某些场景下发现会因为kv_flush_lat耗时比较长,而后致使排队时间state_kv_queued_lat耗时较长,并进一步致使写耗时较长的问题,以下:
"op_w_latency": { "avgcount": 137259, "sum": 13948.293318081, "avgtime": 0.101620245 }, "op_w_process_latency": { "avgcount": 137259, "sum": 9339.461465035, "avgtime": 0.068042616 }, "op_w_prepare_latency": { "avgcount": 137259, "sum": 399.964157986, "avgtime": 0.002913937 }, "kv_flush_lat": { "avgcount": 27184, "sum": 172.947431577, "avgtime": 0.006362103 }, "kv_commit_lat": { "avgcount": 27184, "sum": 5.953508519, "avgtime": 0.000219007 }, "kv_lat": { "avgcount": 27184, "sum": 178.900940096, "avgtime": 0.006581111 }, "state_kv_queued_lat": { "avgcount": 142499, "sum": 8800.295888690, "avgtime": 0.061756895 }, "state_kv_commiting_lat": { "avgcount": 142499, "sum": 94.338791604, "avgtime": 0.000662031 }, "commit_lat": { "avgcount": 142499, "sum": 9284.327304726, "avgtime": 0.065153631 },
该问题的解决方案就如性能优化初览一节中所说,对于defer write,把其flush移动到_deferred_aio_finish函数里便可。
经过上述代码修改,发现fio已经能够上去了。可是接下来会有一系列的新问题,好比调整pg_num会形成IO hang死,迁移逻辑pool会致使OSD挂掉,OSD异常会致使IO长时间卡死等问题,目前这些问题已经所有解决。
性能优化结果
本次优化对于大写(默认64kb以上)没有优化效果,如下是4k随机写的性能对比图,
从优化结果能够看出,优化后而且未达限流前,性能能够提高数十倍以上,即便达到了限流(限流的参数能够根据机器内存状况配置)。
性能优化后的异常问题以及解决
迁移逻辑池时OSD挂掉
- 现象
迁移逻辑池ceph osd pool set poolname crush_rule rulename
时OSD会挂掉,出错日志以下:
2021-01-13 14:43:47.187236 7f8e5133f700 -1 *** Caught signal (Segmentation fault) ** 8402273 in thread 7f8e5133f700 thread_name:bstore_kv_final 8402274 8402275 ceph version 12.2.12+netease+1.0+pri+buster (a372ba6cea5cfc83ebfac2204aba6a2225a7263c) luminous (stable) 8402276 1: (ceph::BackTrace::BackTrace(int)+0x45) [0x55ae756f1cad] 8402277 2: (()+0x227da4b) [0x55ae7598ba4b] 8402278 3: (()+0x12730) [0x7f8e59c54730] 8402279 4: (coll_t::to_str[abi:cxx11]() const+0x30) [0x55ae750c4160] 8402280 5: (operator<<(std::ostream&, coll_t const&)+0x33) [0x55ae750c42da] 8402281 6: (BlueStore::_reap_collections()+0x22c) [0x55ae757cdb6e] 8402282 7: (BlueStore::_kv_finalize_thread()+0x11de) [0x55ae757ec71a] 8402283 8: (BlueStore::KVFinalizeThread::entry()+0x1c) [0x55ae75822ee8] 8402284 9: (Thread::entry_wrapper()+0xc1) [0x55ae75bc43df] 8402285 10: (Thread::_entry_func(void*)+0x18) [0x55ae75bc4314] 8402286 11: (()+0x7fa3) [0x7f8e59c49fa3] 8402287 12: (clone()+0x3f) [0x7f8e596344cf]
- 缘由
_kv_finalize_thread --> _txc_state_proc --> _txc_finish --> _queue_reap_collection --> removed_collections.push_back(c); --> _reap_collections --> removed_colls.swap(removed_collections);
如上,能够看到,在Ceph的原生版本中,对于removed_collections的push以及pop操做是在_kv_finalize_thread这同一个线程中,因此操做这个队列时没有加锁。
性能优化代码里把_txc_state_proc的操做移动到了_kv_sync_thread函数,可是_reap_collections却没有移动,因此出现了这个问题
- 解决办法
把_reap_collections函数也一并移动到_kv_sync_thread
OSD异常退出
- 问题
若是在OSD退出时,有defer write的数据没有下刷到数据盘,那么OSD重启后,会调用+deferred_replay
函数进行回放。若是要回放的数据量太大,那么即便是Ceph的原生版本也是会有问题的,这个问题也向社区提了tracker: https://tracker.ceph.com/issues/48696 48696
- 解决办法
把submit_batch
形参中的uint16_t aios_size
修改成uint64_t aios_size
;
另外为了防止函数里面piocb
数组的栈溢出,能够设置一次提交的最大aio数量,若是aio数量过多,能够在这个函数里面进行循环屡次提交。
数据盘被打满
- 问题
因为性能优化后会尽量地完成defer write的元数据落盘并返回客户写成功。因此数据一直在经过_kv_finalize_thread
调用deferred_try_submit
进行提交。 那么数据盘一直会被持续打满。
数据盘被持续打满时,若是此时有big write带来的非defer write或者是读,或者是数据恢复,那么显然这些IO都会迟迟得不到响应。
- 解决办法
在上面的OSD异常退出
一小节咱们提到,咱们会对aio进行循环屡次提交。 因此基于此咱们能够引入两个可配置参数分别控制一次提交的aio数量以及每次提交中间的sleep时间。这样就能够很好地经过控制因defer write带来的磁盘IO繁忙程度了。
本次修改的其余问题
- 问题
上述经过引入两个参数控制了submit_batch
函数提交aio的形态。可是问题在于这个函数是通用函数,除了defer write的数据盘最终会调用这个函数来落盘,big write以及WAL、DB对应的盘都要经过这个函数落盘,而且OSD启动时若是须要调用deferred_replay
来回放,也会掉欧阳那个这个函数,可是这些场景咱们是须要他正常来作的。
- 解决办法
引入bool值block
,只有当它为true时,在submit_batch
中才会sleep。 另外经过在KernelDevice::aio_submit
函数中控制block
是true仍是false,具体增长代码以下:
+ // only defer write and the block device can be blocked(sleep) + string::size_type wal = path.find("block.wal"); + string::size_type db = path.find("block.db"); + bool block = false; + if (enable_wal_db_perf_optimize && wal == string::npos + && db == string::npos && ioc->defer_write) { + dout(20) << __func__ << "path is: " << path << dendl; + block = true; + } else { + dout(20) << __func__ << "path is: : " << path << dendl; + } void *priv = static_cast<void*>(ioc); int r, retries = 0; r = aio_queue.submit_batch(ioc->running_aios.begin(), e,
fio过程当中io hang状况严重
- 现象
fio过程当中,IO会长时间hang住,而后IO恢复,很快又hang住,循环反复。
- 缘由
IO过程当中BlueStore::queue_transactions
函数会获取本次IO须要的限流值,若是已经达到限流阈值,那么须要等待限流释放。
限流的释放在_deferred_aio_finish
函数中,可是只有当一个osr里面的全部defer write(deferred_pending队列)提交给磁盘而且完成后,才会被调用。咱们优化后的代码因为IO一直在尽量提交,因此deferred_pending队列可能比较长,那么当_deferred_submit_unlock
函数把他们提交给磁盘后,可能要较长时间全部的IO才能得以完成.那么限流可能长时间得不到释放
- 解决办法
对于defer write,每一个IO完成后都调用下_deferred_aio_finish
,若是IO没有所有完成,那么仅仅只作释放部分限流的工做。
另外,因为defer write以及非defer write以及OSD启动replay使用的都是同一套磁盘IO模型,因此要解决好通用问题,也即只有是defer write时,才能够在IO没有所有完成,也调用下回调函数。
调整pg num时OSD挂掉
- 问题
用指令ceph osd pool set poolname pg_num
时,fio卡死,增长的pg状态一直 成为不了active+clean ,最终OSD挂掉。
pg分裂,peering线程中最终会调用_split_collection
函数, 进而会调用_osr_drain_preceding(txc)
函数, 该函数会把全部的deferred op下刷到数据盘。
若是deferred_queue
很长,致使下刷时间超过了osd_op_thread_suicide_timeout
,那么peering线程便会超时致使OSD挂掉。
- 解决办法
经过使得默认不能分裂。 而且在ceph osd perf
指令中添加OSD的defer长度这以显示项,当OSD中的defer长度不是太长时 ,才进行pg的分裂操做。
fio过程当中OSD挂掉
- 问题
fio过程当中OSD会挂掉,缘由是_txc_finish
中会调用deferred_try_submit
, 修改后的性能优化版本因为尽量完成IO,因此可能致使该函数耗时很长。
- 解决办法
在txc_finish
中去掉deferred_try_submit
的调用。
其余说明
中止一个OSD以前,down掉后还会调用deferred_try_submit刷全部的defer wrirte,若是defer比较多,超过了90s,那么OSD便会被杀掉 (由于若是默认90s进程尚未退出,就会发送kill -9 https://unix.stackexchange.com/questions/310146/how-do-i-skip-the-90s-timeout-in-systemd )
可是这个无所谓,反正osd start的时候还会从新作repaly。
代码提交
使用说明
上述代码修改相关功能默认是没有开启的。若是要开启,能够修改以下配置并重启OSD:
bluestore_wal_db_perf_optimize = true bluestore_throttle_bytes = 67108864000 # 根据机器内存配置 bluestore_throttle_deferred_bytes = 134217728000
说明
本文纯属记录一下本身性能分析和优化的一些过程及思路,鉴于水平有限,若是有错漏的地方,期待与你们一块儿交流。
做者:吴宏松,网易数帆存储技术专家