searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Ceph副本读策略实现介绍

2023-06-30 09:56:16
115
0

    Ceph在写数据的时候,是先写主PG,然后去写副本PG,而读取的时候,实际上只有主PG能够提供服务,这对于磁盘的整体带宽来说,并没有充分地发挥其性能,所以能够读取副本当然是会有很大好处的。

    Ceph客户端是基于crush规则定义的地址去进行文件的读取,这样从读取的客户端角度,理论上可自行选择地址去读取,当然这个区域没有的时候就按正常读取就可以了。

    此设计最早应用于ceph-fuse,在参数中加上--localize-reads,即可根据crush规则,就近读取,主要使用于hadoop场景,解决其并发读能力问题。

    Ceph曾在F版librbd实现了类似的功能,通过配置项rbd_localize_parent_reads可实现就近读取,但因测试情况不佳,后被弃用。

    Ceph在O版再次在librbd中实现了此功能。通过配置项rbd_read_from_replica_policy可以选择副本读策略,默认配置项default为现有的从主PG所在的OSD读取,而配置项localize可实现就近读取,此外配置项balance可实现从PG所在的各个OSD负载均衡读取。

    读策略体现在将请求发往哪个OSD,在osdc/Objecter.cc的_calc_target方法中实现。首先根据Object找到PG,再通过PG找到OSD集合。在OSD集合中再根据策略选择需要访问的OSD,策略只对读请求有效。对于localize策略,使用get_common_ancestor_distance计算距离,选择距离最小的OSD(距离相等时选择集合中的第一个OSD,即主PG所在的OSD)。对于balance策略,则随机选择OSD。除此以外的请求都将发给主PG所在的OSD。

int osd;

bool read = is_read && !is_write;

if (read && (t->flags & CEPH_OSD_FLAG_BALANCE_READS)) {

  int p = rand() % acting.size();

  if (p)

    t->used_replica = true;

  osd = acting[p];

  ldout(cct, 10) << " chose random osd." << osd << " of " << acting

                 << dendl;

} else if (read && (t->flags & CEPH_OSD_FLAG_LOCALIZE_READS) &&

           acting.size() > 1) {

  // look for a local replica.  prefer the primary if the

  // distance is the same.

  int best = -1;

  int best_locality = 0;

  for (unsigned i = 0; i < acting.size(); ++i) {

    int locality = osdmap->crush->get_common_ancestor_distance(

           cct, acting[i], crush_location);

    ldout(cct, 20) << __func__ << " localize: rank " << i

                   << " osd." << acting[i]

                   << " locality " << locality << dendl;

    if (i == 0 ||

        (locality >= 0 && best_locality >= 0 &&

         locality < best_locality) ||

        (best_locality < 0 && locality >= 0)) {

      best = i;

      best_locality = locality;

      if (i)

        t->used_replica = true;

    }

  }

  ceph_assert(best >= 0);

  osd = acting[best];

} else {

  osd = acting_primary;

}

    在OSD侧,会对请求是否正确发给自己做判断。假如OSD处理的请求所属的PG不是主PG,且请求中不含有localize或balance标签,将不予执行(osd/PrimaryLogPG.cc的do_op方法)。

if ((m->get_flags() & (CEPH_OSD_FLAG_BALANCE_READS |

                       CEPH_OSD_FLAG_LOCALIZE_READS)) &&i

    op->may_read() &&

    !(op->may_write() || op->may_cache())) {

  // balanced reads; any replica will do

  if (!(is_primary() || is_nonprimary())) {

    osd->handle_misdirected_op(this, op);

    return;

  }

} else {

  // normal case; must be primary

  if (!is_primary()) {

    osd->handle_misdirected_op(this, op);

    return;

  }

}

    在Librbd中,只需根据配置对读请求打下对应标签即可。

librados::Rados rados(md_ctx);

int8_t require_osd_release;

int r = rados.get_min_compatible_osd(&require_osd_release);

if (r == 0 && require_osd_release >= CEPH_RELEASE_OCTOPUS) {

  read_flags = 0;

  auto read_policy = config.get_val<std::string>("rbd_read_from_replica_policy");

  if (read_policy == "balance") {

    read_flags |= CEPH_OSD_FLAG_BALANCE_READS;

  } else if (read_policy == "localize") {

    read_flags |= CEPH_OSD_FLAG_LOCALIZE_READS;

  }

}

0条评论
0 / 1000