本文首发于 2020-06-07 17:17:10
《ClickHouse 和他的朋友们》系列文章转载自圈内好友 BohuTANG 的博客,原文链接:
https://bohutang.me/2020/06/07/clickhouse-and-friends-mysql-protocol-read-stack/
以下为正文。
作为一个 OLAP 的 DBMS 来说,有 2 个端非常重要:
- 用户如何方便的链进来,这是入口端
- ClickHouse 除了自己的 client 外,还提供了 MySQL/PG/GRPC/HTTP 等接入方式
- 数据如何方便的挂上去,这是数据源端
- ClickHouse 除了自己的引擎外,还可以挂载 MySQL/Kafka 等外部数据源
这样内外互通,多条朋友多条路,以实现“数据”级的编排能力。
今天谈的是入口端的 MySQL 协议,也是本系列 ClickHouse 的第一个好朋友,用户可通过 MySQL 客户端或相关 Driver 直接链接到 ClickHouse,进行数据读写等操作。
本文通过 MySQL 的 Query 请求,借用调用栈来了解下 ClickHouse 的数据读取全过程。
如何实现?
入口文件在:
MySQLHandler.cpp
握手协议
- MySQLClient 发送 Greeting 数据报文到 MySQLHandler
- MySQLHandler 回复一个 Greeting-Response 报文
- MySQLClient 发送认证报文
- MySQLHandler 对认证报文进行鉴权,并返回鉴权结果
MySQL Protocol 实现在: Core/MySQLProtocol.h
最近的代码中调整为了 Core/MySQL/PacketsProtocolText.h
Query 请求
当认证通过后,就可以进行正常的数据交互了。
当 MySQLClient 发送请求:
1
mysql> SELECT * FROM system.numbers LIMIT 5;
MySQLHandler 的调用栈:
1
->MySQLHandler::comQuery -> executeQuery -> pipeline->execute -> MySQLOutputFormat::consume
MySQLClient 接收到结果
在步骤 2 里,executeQuery(executeQuery.cpp)非常重要。
它是所有前端 Server 和 ClickHouse 内核的接入口,第一个参数是 SQL 文本(‘select 1’),第二个参数是结果集要发送到哪里去(socket net)。
调用栈分析
1 | SELECT * FROM system.numbers LIMIT 5 |
1. 获取数据源
StorageSystemNumbers 数据源:
1 | DB::StorageSystemNumbers::read(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, std::__1::shared_ptr<DB::StorageInMemoryMetadata const> const&, DB::SelectQueryInfo const&, DB::Context const&, DB::QueryProcessingStage::Enum, unsigned long, unsigned int) StorageSystemNumbers.cpp:135 |
这里最主要的是 ReadFromStorageStep 函数,从不同 storage 里获取数据源 pipe:
1 | Pipes pipes = storage->read(required_columns, metadata_snapshot, query_info, *context, processing_stage, max_block_size, max_streams); |
2. Pipeline 构造
1 | DB::LimitTransform::LimitTransform(DB::Block const&, unsigned long, unsigned long, unsigned long, bool, bool, std::__1::vector<DB::SortColumnDescription, std::__1::allocator<DB::SortColumnDescription> >) LimitTransform.cpp:21 |
3. Pipeline 执行
1 | DB::LimitTransform::prepare(std::__1::vector<unsigned long, std::__1::allocator<unsigned long> > const&, std::__1::vector<unsigned long, std::__1::allocator<unsigned long> > const&) LimitTransform.cpp:67 |
4. Output 执行发送
1 | DB::MySQLOutputFormat::consume(DB::Chunk) MySQLOutputFormat.cpp:53 |
总结
ClickHouse 的模块化比较清晰,像乐高积木一样可以组合拼装,当我们执行:
1 | SELECT * FROM system.numbers LIMIT 5 |
首先内核解析 SQL 语句生成 AST,然后根据 AST 获取数据源 Source,pipeline.Add(Source)。
其次根据 AST 信息生成 QueryPlan,根据 QueryPlan 再生成相应的 Transform,pipeline.Add(LimitTransform)。
然后添加 Output Sink 作为数据发送对象,pipeline.Add(OutputSink)。
执行 pipeline, 各个 Transformer 开始工作。
ClickHouse 的 Transformer 调度系统叫做 Processor,也是决定性能的重要模块,详情见 Pipeline 处理器和调度器。
ClickHouse 是一辆手动挡的豪华跑车,免费拥有,海啸们!
欢迎关注我的微信公众号【数据库内核】:分享主流开源数据库和存储引擎相关技术。
标题 | 网址 |
---|---|
GitHub | https://dbkernel.github.io |
知乎 | https://www.zhihu.com/people/dbkernel/posts |
思否(SegmentFault) | https://segmentfault.com/u/dbkernel |
掘金 | https://juejin.im/user/5e9d3ed251882538083fed1f/posts |
CSDN | https://blog.csdn.net/dbkernel |
博客园(cnblogs) | https://www.cnblogs.com/dbkernel |