浅谈 Apache Doris FE 处理查询 SQL 源码解析
一、前言
在使用 Apache Doris 时,我们可以通过 Apache Doris FE Web 页面或者 Mysql 协议执行 SQL 语句,但是对于 Apache Doris 背后如何对 SQL 进行处理,我们无从所知。本文章内容主要讲解 Apache Doris 查询 SQL 在 FE 节点处理原理。Doris 查询语句和市面主流的数据库处理阶段都差不多,需要经过 Parse,Analyze,Optimize,Plan,Schedule,Execute 等阶段。 在 Doris 中,FE 负责查询的 Parse,Analyze,Optimize,Plan, Schedule,BE 负责执行 FE 下发 Plan Fragment
二、名词解释
- FE:Frontend,即 Doris 的前端节点。主要负责接收和返回客户端请求、元数据以及集群管理、查询计划生成等工作。
- BE:Backend,即 Doris 的后端节点。主要负责数据存储与管理、查询计划执行等工作。
- slot:计算槽,是一个资源单位, 只有给 task 分配了一个 slot 之后, 这个 task 才可以运行
- planNode : 逻辑算子
- planNodeTree: 逻辑执行计划
三、执行流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h4BVik75-1655092164198)(assets/16550518736154.jpg)]
四、Apache Doris 查询原理
(一)SQL 接收
本文只说 mysql 协议如何接收 SQL 语句, 如果感兴趣的同学可以看看 Apache Doris FE Web 的 Rest Api。Apache Doris 兼容 Mysql 协议,用户可以通过 Mysql 客户端和其他支持 Mysql 协议的工具向 Doris 发送查询请求。MysqlServer Listener() 负责监听客户端发送来的 Mysql 连接请求,每个连接请求都被封装成一个 ConnectContext 对象,并被提交给 ConnectScheduler。ConnectScheduler 会维护一个线程池,每个 ConnectContext 会在线程池中由一个 ConnectProcessor 线程处理。
- MysqlServer 类 Listener 处理:
private class Listener implements Runnable {
@Override
public void run(){while (running && serverChannel.isOpen()) {
SocketChannel clientChannel;
try {clientChannel = serverChannel.accept();
if (clientChannel == null) {continue;}
// 构建 ConnectContext 对象
ConnectContext context = new ConnectContext(clientChannel);
// catelog 日志
context.setCatalog(Catalog.getCurrentCatalog());
// 向 ExecutorService 提交 new LoopHandler(context) ==>(源码)executor.submit(new LoopHandler(context))
if (!scheduler.submit(context)) {LOG.warn("Submit one connect request failed. Client=" + clientChannel.toString());
// clear up context
context.cleanup();}
} catch (IOException e) {
// ClosedChannelException
// AsynchronousCloseException
// ClosedByInterruptException
// Other IOException, for example "to many open files" ...
LOG.warn("Query server encounter exception.", e);
try {Thread.sleep(100);
} catch (InterruptedException e1) {// Do nothing}
} catch (Throwable e) {
// NotYetBoundException
// SecurityException
LOG.warn("Query server failed when calling accept.", e);
}
}
}
}
- ExecutorService 线程 LoopHandler 处理:
@Override
public void run() {
try {
// Set thread local info
context.setThreadLocalInfo();
context.setConnectScheduler(ConnectScheduler.this);
// authenticate check failed.
if (!MysqlProto.negotiate(context)) {return;}
if (registerConnection(context)) {MysqlProto.sendResponsePacket(context);
} else {context.getSt