一、ceph scrub介绍
ceph通过scrub保证数据的一致性,scrub 以PG 为单位,对于每一个pg,ceph 分析该pg下的object, 产生一个类似于元数据信息摘要的数据结构,如对象大小,属性等,叫scrubmap, 比较主与副scrubmap,来保证是不是有object 丢失或者不匹配。lightscrub(daily)比较object size 和属性。deep scrub (weekly)读取数据部分并通过checksum(这里是CRC32)比较保证数据一致性。 每次scrub 只取chunk(chunk大小可以通过ceph的配置选项进行配置)数量的object比较,这期间被校验对象的数据是不能被修改的,所以write请求会被block. scrub操作可以手动触发,也会根据配置项和系统负载情况每天定时触发。
二、代码流程分析
1、手动触发scrub
a、mon接收到scurb命令进入PGMonitor::preprocess_query函数
case MSG_PGSTATS:
return preprocess_pg_stats(op);
case MSG_MON_COMMAND: //scrub
return preprocess_command(op);
b、preprocess_command(op)函数在mon/PGMonitor.cc文件中,给主osd发送scrub指令
else if (prefix == "pg scrub" ||
prefix == "pg repair" ||
prefix == "pg deep-scrub") {
.........
mon->try_send_message(new MOSDScrub(mon->monmap->fsid, pgs, //给主osd发送scrub指令
scrubop == "repair",
scrubop == "deep-scrub"),
mon->osdmon()->osdmap.get_inst(osd));
ss << "instructing pg " << pgid << " on osd." << osd << " to " << scrubop;
r = 0;
c、主osd,接收到MOSDScrub的命令后,在OSD::ms_dispatch函数中调用_dispatch(m);
do_waiters();
_dispatch(m); //scrub
do_waiters();
d、在_dispatch(m)中调用handle_scrub进行处理
case MSG_OSD_SCRUB:
handle_scrub(static_cast<MOSDScrub*>(m));
break;
e、在handle_scrub中调用pg->reg_next_scrub()注册要处理的pg,初始化了ScrubJob,ScrubJob中有任务执行需要的信息。
if (pg->is_primary()) {
pg->unreg_next_scrub();
pg->scrubber.must_scrub = true;
pg->scrubber.must_deep_scrub = m->deep || m->repair;
pg->scrubber.must_repair = m->repair;
pg->reg_next_scrub();
f、在osd初始化的时候注册了一个定时任务
tick_timer.add_event_after(cct->_conf->osd_heartbeat_interval, new C_Tick(this));
{
Mutex::Locker l(tick_timer_lock);
tick_timer_without_osd_lock.add_event_after(cct->_conf->osd_heartbeat_interval, new C_Tick_WithoutOSDLock(this));
}
g、该定时任务会每隔osd_heartbeat_interval时间段,就会触发定时器回调函数OSD::tick_without_osd_lock()
h、在回调函数中调用 sched_scrub();
if (!scrub_random_backoff()) {
sched_scrub();
}
i、在OSD::sched_scrub();中主要检查时间和系统的负载,并取得ScrubJob中的第一个任务。
if (pg->sched_scrub()) { //调用PG::sched_scrub()
pg->unlock();
break;
j、在PG::sched_scrub()中完成资源的预约,调用bool PG::queue_scrub()最后加入OpWq队列.
scrub_queued = true;
osd->queue_for_scrub(this); //osd是OSDService类
k、在OSDService::queue_for_scrub中加入OpWq队列
void queue_for_scrub(PG *pg) {
op_wq.queue(
make_pair(
pg,
PGQueueable(
PGScrub(pg->get_osdmap()->get_epoch()),
cct->_conf->osd_scrub_cost,
pg->get_scrub_priority(),
ceph_clock_now(cct),
entity_inst_t())));
}
l、op_wq队列在OSDService类中定义,在OSDService初始化的时候op_wq初始化OSD::op_shardedwq
419 ShardedThreadPool::ShardedWQ < pair <PGRef, PGQueueable> > &op_wq;
在osd/OSD.cc中
216 op_wq(osd->op_shardedwq),
m、op_shardedwq队列绑定OSD:: ShardedThreadPool osd_op_tp线程池。
1669 op_shardedwq(
1670 cct->_conf->osd_op_num_shards,
1671 this,
1672 cct->_conf->osd_op_thread_timeout,
1673 cct->_conf->osd_op_thread_suicide_timeout,
1674 &osd_op_tp),
n、最后线程池调用pg->scrub处理
pg scrub处理和副本osd处理,流程参考:https://my.oschina.net/linuxhunter/blog/681050
https://blog.csdn.net/younger_china/article/details/75450607