目录

概述

服务器实例启动逻辑

语句的执行链路

结论


概述

ClickHouse 简要整体架构图

clickhouse remote查询sql clickhouse select_ClickHouse

 

 

服务器实例启动逻辑

源码存放目录

clickhouse remote查询sql clickhouse select_Parse_02

 

ClickHouse程序启动服务,绑定端口逻辑

// 文件名称: /dbms/programs/server/Server.cpp
int Server::main()
{
    // 初始化上下文, 上下文包含查询执行所依赖的所有内容:设置可用函数、数据类型、聚合函数、数据库等等
    global_context = std::make_unique<Context>(Context::createGlobal());
    global_context->setApplicationType(Context::ApplicationType::SERVER);
     
    // zk初始化
    zkutil::ZooKeeperNodeCache main_config_zk_node_cache([&] { return global_context->getZooKeeper(); });
    
    //其他config的初始化
    //...
    
    //绑定端口,对外提供服务
    auto address = make_socket_address(host, port);
    socket.bind(address, /* reuseAddress = */ true);

    //根据网络协议建立不同的server类型
    //现在支持的server类型有:HTTP,HTTPS,TCP,Interserver,mysql
    /// HTTP
    create_server("http_port", [&](UInt16 port)
    {
        Poco::Net::ServerSocket socket;
        auto address = socket_bind_listen(socket, listen_host, port);
        auto handler_factory = createDefaultHandlerFatory<HTTPHandler>(*this, 
                                "HTTPHandler-factory");
        servers.emplace_back(std::make_unique<Poco::Net::HTTPServer>(...);
    });

    /// HTTPS
    create_server("https_port", [&](UInt16 port)
    {
        Poco::Net::SecureServerSocket socket;
        auto address = socket_bind_listen(socket, listen_host, port, /* secure = */ true);
        servers.emplace_back(std::make_unique<Poco::Net::HTTPServer>(...);
    });

    /// TCP
    create_server("tcp_port", [&](UInt16 port)
    {
        Poco::Net::ServerSocket socket;
        auto address = socket_bind_listen(socket, listen_host, port);
        servers.emplace_back(std::make_unique<Poco::Net::TCPServer>(...);
    });

    /// TCP with SSL
    create_server("tcp_port_secure", [&](UInt16 port)
    {
        Poco::Net::SecureServerSocket socket;
        auto address = socket_bind_listen(socket, listen_host, port, /* secure = */ true);
        servers.emplace_back(std::make_unique<Poco::Net::TCPServer>(...);
    });

    /// Interserver IO HTTP
    create_server("interserver_http_port", [&](UInt16 port)
    {
        Poco::Net::ServerSocket socket;
        auto address = socket_bind_listen(socket, listen_host, port);
        servers.emplace_back(std::make_unique<Poco::Net::HTTPServer>(...);
    });

    create_server("interserver_https_port", [&](UInt16 port)
    {
        Poco::Net::SecureServerSocket socket;
        auto address = socket_bind_listen(socket, listen_host, port, /* secure = */ true);
        servers.emplace_back(std::make_unique<Poco::Net::HTTPServer>(...);
    });
    
    create_server("mysql_port", [&](UInt16 port)
    {
        Poco::Net::ServerSocket socket;
        auto address = socket_bind_listen(socket, listen_host, port, /* secure = */ true);
        servers.emplace_back(std::make_unique<Poco::Net::TCPServer>(...);
    });

    /// Prometheus (if defined and not setup yet with http_port)
    create_server("prometheus.port", [&](UInt16 port)
    {
        Poco::Net::ServerSocket socket;
        auto address = socket_bind_listen(socket, listen_host, port);
        auto handler_factory = new HTTPRequestHandlerFactoryMain(...);
        handler_factory->addHandler<PrometheusHandlerFactory>(async_metrics);
        servers.emplace_back(std::make_unique<Poco::Net::HTTPServer>(...);
    });
    
    //启动server
    for (auto & server : servers)
            server->start();
   
}

 

归纳总结主要逻辑说明

  • 解析应用的配置文件信息;
  • 初始化上下文(包含查询执行所依赖的所有内容:设置可用函数、数据类型、聚合函数、数据库等等);
  • 初始化Zookeeper(分布式DDL执行、ReplicatedMergeTree表主备节点之间的状态同步);
  • 常规配置初始化(flags目录、用户文件目录、dictionaries库目录);
  • 绑定服务端的端口,根据网络协议初始化Handler,对客户端提供服务。

 

 

语句的执行链路

TCP端口响应用户SQL请求逻辑

// 文件名称: /dbms/programs/server/TCPHandler.cpp
void TCPHandler::runImpl()
{
    //设置socket属性
    socket().setReceiveTimeout(global_receive_timeout);
    socket().setSendTimeout(global_send_timeout);
    socket().setNoDelay(true);
    
    //实例化套接字对应的输入和输出流缓冲区
    in = std::make_shared<ReadBufferFromPocoSocket>(socket());
    out = std::make_shared<WriteBufferFromPocoSocket>(socket());
    
    while (1){
        // 接收请求报文
        receivePacket();
        
        // 执行Query
        state.io = executeQuery(state.query, *query_context, false, state.stage, may_have_embedded_data);
    
        //根据Query种类来处理不同的Query
        //处理insert Query
        processInsertQuery();
        //使用pipeline处理的Query和结果发送客户端
        processOrdinaryQueryWithProcessors();
        //不使用pipeline处理的Query和结果发送客户端
        processOrdinaryQuery();
    }
}

 

执行结果发送给客户端逻辑

// 文件名称: /dbms/programs/server/TCPHandler.cpp
void TCPHandler::processOrdinaryQuery()
{
    /// Pull query execution result, if exists, and send it to network.
    if (state.io.in)
    {
        /// This allows the client to prepare output format
        if (Block header = state.io.in->getHeader())
            sendData(header);

        AsynchronousBlockInputStream async_in(state.io.in);
        while (true)
        {

            if (async_in.poll(...))
            {
                const auto block = async_in.read();
                sendData(block);
            }
        }
        async_in.readSuffix();
        sendData({});
    }
    state.io.onFinish();
}

 

归纳总结主要逻辑说明

客户端发来的请求根据各自网络协议的不同,转发给对应的“XXXHandler”来进行的,server在启动的时候“XXXHandler”会被初始化并绑定在指定端口中。

我们以TCPHandler为例,看看服务端是如何处理客户端发来的请求的,重点关注TCPHandler::runImpl 的函数实现:

  • 设置Socket的属性;
  • 初始化输入和输出流的缓冲区;
  • 接受请求报文,拆包;
  • 执行Query(包括整个词法语法分析,Query重写,物理计划生成和生成结果);
  • 把Query结果保存到输出流,然后发送到Socket的缓冲区,等待发送回客户端。

 

SQL执行逻辑

// 文件名称: /dbms/src/Interpreters/executeQuery.cpp
static std::tuple<ASTPtr, BlockIO> executeQueryImpl()
{
    // 构造SQL解析器
    ParserQuery parser(end, settings.enable_debug_queries);
    ASTPtr ast;

    // 解析Query并转化为AST(抽象语法树)
    ast = parseQuery(parser, begin, end, "", max_query_size);

    // 根据AST的类型生成interpreter实例
    auto interpreter = InterpreterFactory::get(ast, context, stage);
    
    // SQL资源管理,检查当前节点是否还能处理SQL
    std::shared_ptr<const EnabledQuota> quota;
    if (!interpreter->ignoreQuota())
    {
        quota = context.getQuota();
        if (quota)
        {
            quota->used(Quota::QUERIES, 1);
            quota->checkExceeded(Quota::ERRORS);
        }
    }
        
    // interpreter执行AST,结果是BlockIO
    res = interpreter->execute();

    // 返回结果是抽象语法树和解析后的结果组成的二元组,让上层把数据返回给客户端
    return std::make_tuple(ast, res);
}

 

归纳总结主要逻辑说明

  • 通过Parser,把Query解析成AST(抽象语法树)。
  • InterpreterFactory根据AST生成对应的Interpreter实例并进行AST优化改写。
  • 检测资源使用情况,判断节点是否能够执行这个Query。
  • AST是由Interpreter来解析的,执行结果是一个BlockIO,并把BlockIO返回给上层,让上层发送给客户端。

 

这个函数是执行SQL的核心,那么我们把这个函数拆解一个一个单独的讲解。

 

Parser(解析器)中抽象语法树(AST)的生成逻辑

ClickHouse选择采用手写一个递归下降的Parser来对SQL进行解析,生成的结果是这个SQL对应的抽象语法树(AST),抽象语法树由表示各个操作的节点(IAST)表示。而本节主要介绍Parser背后的核心逻辑:

ClickHouse的Parser利用lexer将扫描用户下发的SQL字符串,将其分割为一个个的Token, token_iterator 即一个Token流迭代器,然后parser再对Token流进行解析生成AST抽象语法树。

// 文件名称: dbms/src/Parsers/parseQuery.cpp
ASTPtr tryParseQuery()
{
    // Token为lexer词法分析后的基本单位,词法分析后生成的是Token流
    Tokens tokens(pos, end, max_query_size);
    IParser::Pos token_iterator(tokens, max_parser_depth);
    ASTPtr res;
    
    // Token流经过语法分析生成AST抽象语法树
    bool parse_res = parser.parse(token_iterator, res, expected);
    return res;
}

 

通过上面的代码,我们可以看到核心的代码是Parser中的parse方法,我们看下parse的实现方式

bool ParserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
{
    /*
    ShowTablesQuery、SelectWithUnionQuery、TablePropertiesQuery、       DescribeTableQuery、
    ShowProcesslistQuery、CreateQuery、AlterQuery、RenameQuery、DropQuery、CheckQuery、
    OptimizeQuery、KillQueryQuery、WatchQuery、        ShowCreateAccessEntityQuery、
    ShowGrantsQuery、ShowQuotasQuery、ShowRowPoliciesQuery
    */
    ParserQueryWithOutput query_with_output_p(enable_explain);
    // Parse INSERT INTO [db.]table xxxxx
    ParserInsertQuery insert_p(end);
    // Parse Query USE db
    ParserUseQuery use_p;
    // Parse SET name1 = value1, name2 = value2
    ParserSetQuery set_p;
    // Parse System Query
    ParserSystemQuery system_p;
    // Parse CREATE USER | ALTER USER
    ParserCreateUserQuery create_user_p;
    // Parse CREATE ROLE | ALTER ROLE
    ParserCreateRoleQuery create_role_p;
    // Parse CREATE QUOTA | ALTER QUOTA
    ParserCreateQuotaQuery create_quota_p;
    // Parse CREATE [ROW] POLICY | ALTER [ROW] POLICY
    ParserCreateRowPolicyQuery create_row_policy_p;
    // Parse CREATE SETTINGS PROFILE | ALTER SETTINGS PROFILE
    ParserCreateSettingsProfileQuery create_settings_profile_p;
    // Parse DROP USER | DROP ROLE | DROP QUOTA | DROP [SETTINGS] PROFILE | DROP [ROW] POLICY
    ParserDropAccessEntityQuery drop_access_entity_p;
    // Parse GRANT access_type | REVOKE access_type
    ParserGrantQuery grant_p;
    // Parse SET ROLE | SET DEFAULT ROLE
    ParserSetRoleQuery set_role_p;

    bool res = query_with_output_p.parse(pos, node, expected)
        || insert_p.parse(pos, node, expected)
        || use_p.parse(pos, node, expected)
        || set_role_p.parse(pos, node, expected)
        || set_p.parse(pos, node, expected)
        || system_p.parse(pos, node, expected)
        || create_user_p.parse(pos, node, expected)
        || create_role_p.parse(pos, node, expected)
        || create_quota_p.parse(pos, node, expected)
        || create_row_policy_p.parse(pos, node, expected)
        || create_settings_profile_p.parse(pos, node, expected)
        || drop_access_entity_p.parse(pos, node, expected)
        || grant_p.parse(pos, node, expected);

    return res;
}

 

通过上面的代码可知,ClickHouse数据库大致把SQL类型分为下面几种情况

  • QueryWithOutput
  • InsertQuery
  • UseQuery
  • SetQuery
  • SystemQuery
  • CreateUserQuery
  • CreateRoleQuery
  • CreateQuotaQuery
  • CreateRowPolicyQuery
  • CreateSettingsProfileQuery
  • DropAccessEntityQuery
  • GrantQuery
  • SetRoleQuery

 

每一种SQL类型都有自己专属的解析器,如果遇到接收到一个SQL,则调用专门的解析器进行SQL解析。以QueryWithOutput中的SELECT解析器为例

// 文件名称: dbms/src/Parsers/ParserSelectQuery.cpp
bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
{
    ParserKeyword s_select("SELECT");
    ParserKeyword s_distinct("DISTINCT");
    ParserKeyword s_from("FROM");
    ParserKeyword s_prewhere("PREWHERE");
    ParserKeyword s_where("WHERE");
    ParserKeyword s_group_by("GROUP BY");
    ParserKeyword s_with("WITH");
    ParserKeyword s_totals("TOTALS");
    ParserKeyword s_having("HAVING");
    ParserKeyword s_order_by("ORDER BY");
    ParserKeyword s_limit("LIMIT");
    ParserKeyword s_settings("SETTINGS");
    ParserKeyword s_by("BY");
    ParserKeyword s_rollup("ROLLUP");
    ParserKeyword s_cube("CUBE");
    ParserKeyword s_top("TOP");
    ParserKeyword s_with_ties("WITH TIES");
    ParserKeyword s_offset("OFFSET");

    // ........

    //依次对Token流爬取上述关键字
    xxxx.parse(pos, tables, expected)

    //根据语法分析结果设置AST的Expression属性,可以理解为如果SQL存在该关键字,这个关键字都会转化为AST上的一个节点
    ast->setExpression(ASTSelectQuery::Expression::WITH, std::move(with_expression_list));
    ast->setExpression(ASTSelectQuery::Expression::SELECT, std::move(select_expression_list));
    ast->setExpression(ASTSelectQuery::Expression::TABLES, std::move(tables));
    ast->setExpression(ASTSelectQuery::Expression::PREWHERE, std::move(prewhere_expression));
    ast->setExpression(ASTSelectQuery::Expression::WHERE, std::move(where_expression));
    ast->setExpression(ASTSelectQuery::Expression::GROUP_BY, std::move(group_expression_list));
    ast->setExpression(ASTSelectQuery::Expression::HAVING, std::move(having_expression));
    ast->setExpression(ASTSelectQuery::Expression::ORDER_BY, std::move(order_expression_list));
    ast->setExpression(ASTSelectQuery::Expression::LIMIT_BY_OFFSET, std::move(limit_by_offset));
    ast->setExpression(ASTSelectQuery::Expression::LIMIT_BY_LENGTH, std::move(limit_by_length));
    ast->setExpression(ASTSelectQuery::Expression::LIMIT_BY, std::move(limit_by_expression_list));
    ast->setExpression(ASTSelectQuery::Expression::LIMIT_OFFSET, std::move(limit_offset));
    ast->setExpression(ASTSelectQuery::Expression::LIMIT_LENGTH, std::move(limit_length));
    ast->setExpression(ASTSelectQuery::Expression::SETTINGS, std::move(settings));
}

 

归纳总结主要逻辑说明

  • 通过内置的SQL解析器逐个尝试解析用户下发的SQL,如果可以解析器可以解析则使用当前解析器;
  • 解析器中通过TokenIterator进行词法分析,再Token流中爬取这些关键词;
  • 如果成功爬取,则 setExpression 函数会组装该关键字对应的AST节点。

clickhouse remote查询sql clickhouse select_SQL_03

 

Interpreter(解析器)的构建逻辑

根据抽象语法树的类型生成各种的Interpreter。

// 文件名称: /dbms/src/Interpreters/InterpreterFactory.cpp
std::unique_ptr<IInterpreter> InterpreterFactory::get(...)
{
    if (query->as<ASTSelectQuery>())
        return std::make_unique<InterpreterSelectQuery>(...);
    else if (query->as<ASTSelectWithUnionQuery>())
        return std::make_unique<InterpreterSelectWithUnionQuery>(...);
    else if (query->as<ASTInsertQuery>())
        return std::make_unique<InterpreterInsertQuery>(...);
    else if (query->as<ASTCreateQuery>())
        return std::make_unique<InterpreterCreateQuery>(...);
    else if (query->as<ASTDropQuery>())
        return std::make_unique<InterpreterDropQuery>(...);
    else if (query->as<ASTRenameQuery>())
        return std::make_unique<InterpreterRenameQuery>(...);
    else if (query->as<ASTShowTablesQuery>())
        return std::make_unique<InterpreterShowTablesQuery>(...);
    else if (query->as<ASTUseQuery>())
        return std::make_unique<InterpreterUseQuery>(...);
    else if (query->as<ASTSetQuery>())
        return std::make_unique<InterpreterSetQuery>(...);
    else if (query->as<ASTSetRoleQuery>())
        return std::make_unique<InterpreterSetRoleQuery>(...);
    else if (query->as<ASTOptimizeQuery>())
        return std::make_unique<InterpreterOptimizeQuery>(...);
    else if (query->as<ASTExistsTableQuery>())
        return std::make_unique<InterpreterExistsQuery>(...);
    else if (query->as<ASTExistsDictionaryQuery>())
        return std::make_unique<InterpreterExistsQuery>(...);
    else if (query->as<ASTShowCreateTableQuery>())
        return std::make_unique<InterpreterShowCreateQuery>(...);
    else if (query->as<ASTShowCreateDatabaseQuery>())
        return std::make_unique<InterpreterShowCreateQuery>(...);
    else if (query->as<ASTShowCreateDictionaryQuery>())
        return std::make_unique<InterpreterShowCreateQuery>(...);
    else if (query->as<ASTDescribeQuery>())
        return std::make_unique<InterpreterDescribeQuery>(...);
    else if (query->as<ASTExplainQuery>())
        return std::make_unique<InterpreterExplainQuery>(...);
    else if (query->as<ASTShowProcesslistQuery>())
        return std::make_unique<InterpreterShowProcesslistQuery>(...);
    else if (query->as<ASTAlterQuery>())
        return std::make_unique<InterpreterAlterQuery>(...);
    else if (query->as<ASTCheckQuery>())
        return std::make_unique<InterpreterCheckQuery>(...);
    else if (query->as<ASTKillQueryQuery>())
        return std::make_unique<InterpreterKillQueryQuery>(...);
    else if (query->as<ASTSystemQuery>())
        return std::make_unique<InterpreterSystemQuery>(...);
    else if (query->as<ASTWatchQuery>())
        return std::make_unique<InterpreterWatchQuery>(...);
    else if (query->as<ASTCreateUserQuery>())
        return std::make_unique<InterpreterCreateUserQuery>(...);
    else if (query->as<ASTCreateRoleQuery>())
        return std::make_unique<InterpreterCreateRoleQuery>(...);
    else if (query->as<ASTCreateQuotaQuery>())
        return std::make_unique<InterpreterCreateQuotaQuery>(...);
    else if (query->as<ASTCreateRowPolicyQuery>())
        return std::make_unique<InterpreterCreateRowPolicyQuery>(...);
    else if (query->as<ASTCreateSettingsProfileQuery>())
        return std::make_unique<InterpreterCreateSettingsProfileQuery>(...);
    else if (query->as<ASTDropAccessEntityQuery>())
        return std::make_unique<InterpreterDropAccessEntityQuery>(...);
    else if (query->as<ASTGrantQuery>())
        return std::make_unique<InterpreterGrantQuery>(...);
    else if (query->as<ASTShowCreateAccessEntityQuery>())
        return std::make_unique<InterpreterShowCreateAccessEntityQuery>(...);
    else if (query->as<ASTShowGrantsQuery>())
        return std::make_unique<InterpreterShowGrantsQuery>(...);
    else if (query->as<ASTShowQuotasQuery>())
        return std::make_unique<InterpreterShowQuotasQuery>(...);
    else if (query->as<ASTShowRowPoliciesQuery>())
        return std::make_unique<InterpreterShowRowPoliciesQuery>(...);
    else
        throw Exception(...)
}

 

AST(抽象语法树)进一步优化逻辑

InterpreterSelectQuery::InterpreterSelectQuery(....)
{
    // 获取AST
    ASTSelectQuery & query = getSelectQuery();

    // 对AST做进一步语法分析,对语法树做优化重写
    syntax_analyzer_result = SyntaxAnalyzer(...).analyzeSelect(...);

    // 每一种Query都会对应一个特有的表达式分析器,用于爬取AST生成执行计划(操作链)
    query_analyzer = std::make_unique<SelectQueryExpressionAnalyzer>(...);
}

语法分析直接生成的AST转化成执行计划可能性能上并不是最优的,因此需要SyntaxAnalyzer 对其进行优化重写,在其源码中可以看到其涉及到非常多基规则优化RBO(rule based optimization) 的功能。 SyntaxAnalyzer 会逐个针对这些规则对查询进行检查,确定其是否满足转换规则,一旦满足就会对其进行转换。

SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyzeSelect(...) const
{
    // 删除重复列
    renameDuplicatedColumns(...);

    // 根据配置判断是否进行谓词下推
    replaceJoinedTable(...);

    // 优化查询中的布尔表达式的函数
    LogicalExpressionsOptimizer().perform();

    // 公共子表达式的消除
    QueryNormalizer(...).visit(...);

    // 消除select从句后的冗余列
    removeUnneededColumnsFromSelectClause(...);

    // 执行标量子查询,并且用常量替代标量子查询结果
    executeScalarSubqueries(...);

    // 谓词表达式下推到子查询
    PredicateExpressionsOptimizer(...).optimize();

    // group by语句中删除内射函数调用和常量表达式
    optimizeGroupBy(...);

    // 删除order by后面的重复项
    optimizeOrderBy(...);

    // 删除order by limit后面的重复项
    optimizeLimitBy(...);

    // 删除using 的重复项
    optimizeUsing(...);
}

 

物理执行器执行物理计划

ExpressionAnalyzer类的作用可以理解为解析优化重写后的AST,然后对所要进行的操作组成一条操作链,即物理执行计划,举个例子如下所示:

/** These methods allow you to build a chain of transformations over a block, that receives values in the desired sections of the query.
      *
      * Example usage:
      *   ExpressionActionsChain chain;
      *   analyzer.appendWhere(chain);
      *   chain.addStep();
      *   analyzer.appendSelect(chain);
      *   analyzer.appendOrderBy(chain);
      *   chain.finalize();
*/

上述代码把where,select,orderby操作都加入到操作链中,接下来就可以从Storage层读取Block,对Block数据应用上述操作链的操作。而执行的核心逻辑,就在对应Interpreter的 executeImpl 方法实现中,这里以select语句的Interpreter来了解下读取Block数据并且对block数据进行相应操作的流程。

 

void InterpreterSelectQuery::executeImpl(...)
{

   // 对应Query的AST
   auto & query = getSelectQuery();

   // 物理计划,判断表达式是否有where,aggregate,having,order_by,litmit_by等字段
   AnalysisResult expressions = ExpressionAnalysisResult(...);

   // 从Storage读取数据
   executeFetchColumns(...);

   // 根据SQL的关键字在BlockStream流水线中执行相应的操作, 如where,aggregate,distinct, order by, order by limit, rollup|cube, having, union都分别由一个函数负责执行
   executeWhere(...);
   executeAggregation(...);
   executeDistinct(...);
   executeOrder(...);
   executeLimitBy(...);
   executeSubqueriesInSetsAndJoins(...);
   executeMergeAggregated(...);
   executeRollupOrCube(...);
   executeHaving(...);
   executeMergeSorted(...);
   executeUnion(...);
}

分析出了执行计划AnalysisResult(即物理执行计划),接下来就需要从storage层中读取数据来执行对应的操作,核心逻辑在 executeFetchColumns 中: 核心操作就是从storage层读取所要处理列的Block,并组织成BlockStream。

 

void InterpreterSelectQuery::executeFetchColumns(...)
{

    // 实例化Block Stream
    auto streams = storage->read(...)

    // 读取列对应的Block,并且组织成Block Stream
    streams = {std::make_shared<NullBlockInputStream>(storage->getSampleBlockForColumns(...))};

    streams.back() = std::make_shared<ExpressionBlockInputStream>(...);
}

读取完Block Stream之后就是对其执行各种execute操作如 executeWhere、executeAggregation、executeDistinct操作。

 

归纳总结主要逻辑说明

  • 对AST进行优化重写 ;
  • 解析重写后的AST并生成操作链(执行计划) ;
  • 从存储引擎中读取要处理的Block数据 ;
  • 对读取的Block数据应用操作链上的操作。

Interpreter处理后的结果会通过TCPHandler发送给客户端。详细代码请见上面的TCPHandler::processOrdinaryQuery函数的处理逻辑。

 

结论

上面讲述了一条查询语句在ClickHouse数据库中的执行流程和涉及到的模块,下面进行归纳总结一下。

  • 服务端接收客户端发来的SQL请求,具体形式是一个网络包,Server的协议层需要拆包把SQL解析出来;
  • Server负责初始化上下文与Network Handler,然后 Parser 对Query做词法和语法分析,解析成AST;
  • Interpreter的 SyntaxAnalyzer 会应用一些启发式规则对AST进行优化重写;
  • Interpreter的 ExpressionAnalyzer 根据上下文信息以及优化重写后的AST生成物理执行计划;
  • 物理执行计划分发到本地或者分布式的executor,各自从存储引擎中获取数据,应用执行计划;
  • Server把执行后的结果以Block流的形式输出到Socket缓冲区,Client从Socket中读取即可得到结果。

 

本文还比较粗略,还有一些细节内容会在后续文章中描述,比如说ClickHouse的表引擎的实现方式什么、Piepline处理器和调度器是如何运转的,Codegen和SIMD的实现方式等等。

 

参考资料