分布式执行计划的 qc-sqc, transmit-receive 的 channel 分别是干嘛用的?

【 使用环境 】测试环境
【 OB or 其他组件 】observer
【 使用版本 】4.3.0
【问题描述】
transmit 和 receive 之间通过 channel 进行通信,channel 的数量 = M(transmit dfo 的 worker 数量) * N(receive dfo 的 worker 数量)
所有 channel 的 role_ 都是 DTL_CR_PUSHER

QC-SQC 之间也还会建立 channel,数量是 1(QC 数量) * N(SQC 数量),这些 channel 中,QC 的 channel 的 role_ 是 DTL_CR_PULLER,SQC 的 channel 的 role_ 是 DTL_CR_PUSHER

问题是:transmit 和 receive 之间的 channel 用来干啥的?qc 和 sqc 之间的 channel 又是用来干啥的呢?

【复现路径】无
【附件及日志】无
【备注】无

你这些信息 在哪看的 有文档么 可以发一下链接么?

没有文档,代码里看的

ObSerialDfoScheduler::do_schedule_dfo() 里会创建 QC-SQC 之间的 channel

ObDtlChannelUtil::get_transmit_dtl_channel_set() 里会创建 transmit-receive 之间的 channel
ObDtlChannelUtil::get_receive_dtl_channel_set() 里会创建 receive-transmit 之间的 channel

上面两个 transmit, receive 两端的 channel 数量都是 M * N

首先提一下并行执行的基本背景:并行执行调度时会由QC每次派发两个DFO (可以理解为其他AP数据库的plan fragment),一个DFO作为生产者, 一个DFO作为消费者,每个DFO会调度在不同的observer节点上,一个节点上的调度DFO就是一个SQC实例,一个SQC实例会根据并行度在该节点上启动N个线程执行(参考其他AP数据库的pipelines)。

Q1:transmit 和 receive 之间的 channel 用来干啥的
A: transmit和receive算子之间的channle是用来进行流式DB数据传输的。生产者DFO的M个线程和消费者DFO的N个线程 传递数据 就是就是依靠这个,所以是M*N个channel的全连接网络,每个生产者线程都有可能发送数据到任意一个消费者线程上。

Q2: QC 和 SQC 之间的 channel 又是用来干啥的呢?
qc和sqc之间的channel是用来控制并行执行调度的。QC始终只有一个,而每个DFO调度在N台observer节点上就会产生N个SQC,所以是1 * N的关系。通常我们把QC 和 SQC间的channel叫做控制channel。一个最常见的例子是由QC向SQC传输「构建数据channel所需的信息」。DFO由QC派发,只有QC知道这个DFO在每个observer上启动了多上个线程,那么如何把生产者DFO的线程数和消费者DFO线程数让两者互相感知,并确定数据channel的个数?这个信息就是由控制channel传输从QC告知给每一个SQC的。

1 个赞

非常感谢,这样就把这两类 channel 搞清楚了。我又产生了 2 个问题,辛苦您帮忙解答,如下:

  1. ObDtlChannelGroup::make_channel() 方法部分代码如下:
    const uint64_t chid = ObDtlChannel::generate_id();
    if (producer_exec_addr != consumer_exec_addr) {
    ci_producer.chid_ = chid << 1;
    ci_producer.type_ = DTL_CT_RPC;
    ci_producer.peer_ = consumer_exec_addr;
    ci_producer.role_ = DTL_CR_PUSHER;
    ci_producer.tenant_id_ = tenant_id;
    ci_consumer.chid_ = (chid << 1) + 1;
    ci_consumer.type_ = DTL_CT_RPC;
    ci_consumer.peer_ = producer_exec_addr;
    ci_consumer.role_ = DTL_CR_PULLER;
    ci_consumer.tenant_id_ = tenant_id;
    } else {

    }
    这是生成 QC 和 SQC 的 channel,也就是控制 channel 信息的,这里的 role_ 不太好理解。
    按照您说的控制 channel 是用于控制并行度的,要么是 QC 所在 observer 给 SQC 所在 observer 发送数据 channel 及控制信息,或者 QC 和 SQC 的双向通信。
    对于控制 channel 来说,是否就没有生产者和消费者的概念了?如果没有的话,这个方法里的 role_ 等于 DTL_CR_PUSHER、DTL_CR_PULLER 要怎么理解呢?
    而且我搜索代码里,没发现有其它地方判断 role_ 的类型,然后进行不同的操作。

  2. transmit 和 receive 的 channel 的 role_ 都是 DTL_CR_PUSHER,是否意味着 transmit 和 receive 之间会相互发送信息。
    对于 select * from t_partition 这种对多分区表的简单查询,应该是只有 transmit 会给 receive 发送数据。
    但是如果要查询多分区表,需要在各 observer 之间广播数据,这种情况下,transmit 和 receive 应该会双向发送数据吧?

1.role_这个变量应该是已经废弃掉了,个人感觉没必要太过关注role这个意义。
2.控制channel里面QC和SQC是双向通信,QC会发channel信息给SQC,转发 datahub whole message给SQC等;SQC会发finish消息告知QC调度结束,发送datahub piece message给QC等等很多种功能,所有非DB数据类的信息都会由控制channel传输。
3.transmit 和 receive的数据也是双向通信。transmit向receive发送DB数据,receive给transmit发信息应该只有一种情况,那就是发drain消息。

drain简单理解就是提前终止,比如说limit 10,limit算子已经收到了10行之后,就可以告知下所有child算子停止工作了,如果往下遇到了receive算子,那就需要由receive通过channel告知transmit算子,再由transmit继续往下,一直到底层的table scan算子,然后提前结束扫描。当然从实现上讲也可以由控制channel完成drain这个事情,由limit算子通过SQC channel告知QC,再由QC转发给其他所有child算子所在的SQC,只不过我们实现上使用了数据channel,优点可以省去一次转发到QC的rpc过程。

1 个赞

收到,非常感谢您的解答