TiKV 源码解析系列文章(十四)Coprocessor 概览

Easter79
• 阅读 865

作者: Shirly

本文将简要介绍 TiKV Coprocessor 的基本原理,面向想要了解 TiKV 数据读取执行过程的同学,同时也面向想对该模块贡献代码的同学。阅读本文前,建议读者对 TiDB 整体架构有所了解,先阅读三篇文章了解 TiDB 技术内幕:说存储说计算谈调度

什么是 Coprocessor

熟悉 TiDB 整体框架 的同学可能记得,TiDB 是无状态的,数据存储在 TiKV 层。当 TiDB 在收到一个来自客户端的查询请求时,会向 TiKV 获取具体的数据信息。那么一个读请求最朴素的处理过程如下:

TiKV 源码解析系列文章(十四)Coprocessor 概览

首先需要肯定的是这种方式固然能解决问题,但是性能如何呢?我们来一起分析一下:

  1. TiKV 将所有数据返回,网络开销太大。
  2. TiDB 需要计算所有数据,CPU 消耗很大,相对的,TiKV 却并没有什么计算,很闲。

看到以上问题后,聪明如你,可能很容易就想到,能不能让 TiKV 把自己负责的那部分数据做一次计算,再返回给 TiDB 呢?

有何不可呢?

TiKV 读取数据并计算的模块,我们定义为 Coprocessor,该概念灵感来自于 HBase,目前在 TiDB 中的实现类似于 HBase 中的 Coprocessor 的 Endpoint 部分,也可类比 MySQL 存储过程。

有了 Coprocessor 后,从宏观看一个读请求是如何下发到 TiKV 的呢?以下面的请求为例:

TiKV 源码解析系列文章(十四)Coprocessor 概览

如图,以上查询语句在 TiDB 中处理如下:

  1. TiDB 收到查询语句,对语句进行分析,计算出物理执行计划,组织称 TiKV 的 Coprocessor 请求。
  2. TiDB 将该 Coprocessor 请求根据数据的分布,分发到所有相关的 TiKV 上。
  3. TiKV 在收到该 Coprocessor 请求后,根据请求算子对数据进行过滤聚合,然后返回给 TiDB。
  4. TiDB 在收到所有数据的返回结果后,进行二次聚合,并将最终结果计算出来,返回给客户端。

主要功能及处理概览

TiKV Coprocessor 处理的读请求目前主要分类三种:

  • DAG:执行物理算子,为 SQL 计算出中间结果,从而减少 TiDB 的计算和网络开销。这个是绝大多数场景下 Coprocessor 执行的任务。
  • Analyze:分析表数据,统计、采样表数据信息,持久化后被 TiDB 的优化器采用。
  • CheckSum:对表数据进行校验,用于导入数据后一致性校验。

那么 TiKV 在收到 Coprocessor 请求后,何时区分这三种请求的呢?

TiKV 源码解析系列文章(十四)Coprocessor 概览

请求到了 TiKV 层,处理过程如下:

  • 由 gRPC server 接收并将请求分发给 Coprocessor Endpoint 进行处理。
  • Endpoint 在收到请求后,根据请求的优先级,将请求分发给对应的线程池。
  • 所有请求会先异步从存储层获取 snapshot,然后开始真正的处理阶段。
  • 根据请求的不同类型,构造不同的 Handler 进行数据的处理。

目前 Coprocessor 支持的三种接口中,后面两种接口相对比较简单,而 DAG 是里面最复杂也是最常用的,所以本文后续将重点介绍 DAG 类请求。

DAG Request 概览

DAG 顾名思义,是由一系列算子组成的有向无环图,算子在代码中称为 Executors。

目前 DAG 请求主要实现了两种计算模型:

  • 火山模型:每个算子按行按需吐出,3.0 之后开始弃用。
  • 向量化计算模型:每个算子批量化处理数据,3.0 之后开始推广。

在目前的 TiKV master 上,处于火山模型向向量化模型的过度阶段,因而两种计算模型同时存在。TiKV 收到请求时,会优先检测是否可走向量化模型,若部分功能在向量化模型中没有实现,则走旧的计算模型,具体处理逻辑流程如下:

TiKV 源码解析系列文章(十四)Coprocessor 概览

相关代码在:src/coprocessor/dag/mod.rs

因为火山模型已在被弃用中,所以下文我们只讲向量化计算模型。

算子概览

在向量化计算模型中,所有算子都实现了 BatchExecutor接口,其主要定义了一个 get_batch 的函数:

pub trait BatchExecutor: Send {
   fn next_batch(&mut self, scan_rows: usize) -> BatchExecuteResult;
}

pub struct BatchExecuteResult {
   pub physical_columns: LazyBatchColumnVec,
   pub logical_rows: Vec<usize>,
   pub is_drained: Result<bool, Error>,
   ...
}

参数说明:

  • next_batchscan_rows 由上层控制,由于扫的数据过多会慢,因此该数字从 32 倍增到 1024。

  • 返回值 BatchExecuteResult 中,由于返回了一批空数据不代表所有数据都处理完毕了,例如可能只是全被过滤,因而使用单独字段表示所有数据处理完毕。

目前 TiKV 支持的算子主要有以下几类。

TableScan

  • 定义:根据指定主键范围扫表数据,并过滤出一部分列返回。它只会作为最底层算子出现,从底层 KV 获取数据。

  • 源码路径:components/tidb_query/src/batch/executors/table_scan_executor.rs

  • 案例:select col from t

IndexScan

  • 定义:根据指定索引返回扫索引数据,并过滤出一部分索引列返回。它只会作为最底层算子出现,从底层 KV 获取数据。

  • 源码路径:components/tidb_query/src/batch/executors/index_scan_executor.rs

  • 案例:select index from t

Selection

  • 定义:对底层算子的结果按照过滤条件进行过滤,其中这些条件由多个表达式组成。

  • 源码路径:components/tidb_query/src/batch/executors/selection_executor.rs

  • 案例:select col from t where a+b=10

TiKV 源码解析系列文章(十四)Coprocessor 概览

Limit

  • 定义:从底层算子吐出的数据中,限定返回若干行。

  • 源码路径:components/tidb_query/src/batch/executors/limit_executor.rs

  • 案例:select col from t limit 10

TiKV 源码解析系列文章(十四)Coprocessor 概览

TopN

  • 定义:按照给定表达式进行排序后,取出前若干行数据。

  • 源码路径:components/tidb_query/src/batch/executors/top_n_executor.rs

  • 案例:select col from t order by a+1 limit 10

TiKV 源码解析系列文章(十四)Coprocessor 概览

Aggregation

  • 定义:按照给定表达式进行分组、聚合。

  • 源码路径:components/tidb_query/src/batch/executors/*_aggr_executor.rs

  • 案例: select count(1) from t group by score + 1

TiKV 源码解析系列文章(十四)Coprocessor 概览

混合使用各个算子

综上,各个算子之间可以按照以下方式任意组合,如下图所示:

TiKV 源码解析系列文章(十四)Coprocessor 概览

案例:select count(1) from t where age>10

TiKV 源码解析系列文章(十四)Coprocessor 概览

小结

由于篇幅原因,本文只是讲了一些 Coprocessor 的概要,读者对此有个概念即可。后续我们将推出该模块相关的更多更深的源码细节分析,欢迎大家继续阅读并给出建设性的改进意见。

原文阅读https://pingcap.com/blog-cn/tikv-source-code-reading-14/

TiKV 源码解析系列文章(十四)Coprocessor 概览

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k