From e5ce970f8ff7d5f7dfacbcd2e1bc1eb6e6a4297d Mon Sep 17 00:00:00 2001 From: baurine <2008.hbl@gmail.com> Date: Sun, 28 Jun 2020 17:46:02 +0800 Subject: [PATCH 01/56] wip --- media/tidb-computing-dist-sql-flow.png | Bin 0 -> 24658 bytes media/tidb-computing-native-sql-flow.jpeg | Bin 0 -> 22755 bytes media/tidb-computing-tidb-sql-layer.png | Bin 0 -> 82398 bytes tidb-computing-zh.md | 158 ++++++++++++++++++++++ tidb-computing.md | 158 ++++++++++++++++++++++ 5 files changed, 316 insertions(+) create mode 100644 media/tidb-computing-dist-sql-flow.png create mode 100644 media/tidb-computing-native-sql-flow.jpeg create mode 100644 media/tidb-computing-tidb-sql-layer.png create mode 100644 tidb-computing-zh.md create mode 100644 tidb-computing.md diff --git a/media/tidb-computing-dist-sql-flow.png b/media/tidb-computing-dist-sql-flow.png new file mode 100644 index 0000000000000000000000000000000000000000..f68a7d9f2417accb7f53cc5f385f663419b16491 GIT binary patch literal 24658 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

?CGNH>rqidBDwvDlmherb67Lp~&LFXT5 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

8mTXEyA?;~o?(g&Ne1 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 literal 0 HcmV?d00001 diff --git a/media/tidb-computing-tidb-sql-layer.png b/media/tidb-computing-tidb-sql-layer.png new file mode 100644 index 0000000000000000000000000000000000000000..c496493bdb4d9f682ace48277e1ab36c36b98080 GIT binary patch literal 82398 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

KuTyxnH!o#p(0*G5vpsbT5~eVq1dHk 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 ["TiDB", "SQL Layer", 10] +t10_r2 --> ["TiKV", "KV Engine", 20] +t10_r3 --> ["PD", "Manager", 30] +``` + +除了主键外,该表还有一个非唯一的普通二级索引 `idxAge`,假设这个索引的 `IndexID` 为 1,则其存储在 TiKV 上的索引数据为: + +``` +t10_i1_10_1 --> null +t10_i1_20_2 --> null +t10_i1_30_3 --> null +``` + +以上例子展示了 TiDB 中关系模型到 Key-Value 模型的映射规则,以及选择该方案背后的考量。 + +## 元信息管理 + +TiDB 中每个 `Database` 和 `Table` 都有元信息,也就是其定义以及各项属性。这些信息也需要持久化,TiDB 将这些信息也存储在了 TiKV 中。 + +每个 `Database`/`Table` 都被分配了一个唯一的 ID,这个 ID 作为唯一标识,并且在编码为 Key-Value 时,这个 ID 都会编码到 Key 中,再加上 `m_` 前缀。这样可以构造出一个 Key,Value 中存储的是序列化后的元信息。 + +除此之外,TiDB 还用一个专门的 (Key, Value) 键值对存储当前所有表结构信息的最新版本号。这个键值对是全局的,每次 DDL 操作的状态改变时其版本号都会加 1。目前,TiDB 把这个键值对持久化存储在 PD Server 中,其 Key 是 "/tidb/ddl/global_schema_version",Value 是类型为 int64 的版本号值。TiDB 参考了 Google F1 的 Online Schema 变更算法,有一个后台线程在不断地检查 PD Server 中存储的表结构信息的版本号是否发生变化,并且保证在一定时间内一定能够获取版本的变化。 + +## SQL 层简介 + +TiDB 的 SQL 层,即 TiDB Server,负责将 SQL 翻译成 Key-Value 操作,将其转发给共用的分布式 Key-Value 存储层 TiKV,然后组装 TiKV 返回的结果,最终将查询结果返回给客户端。 + +这一层的节点都是无状态的,节点本身并不存储数据,节点之间完全对等。 + +### SQL 运算 + +最简单的方案就是通过上一节所述的[表数据与 Key-Value 的映射关系](#表数据与-key-value-的映射关系)方案,将 SQL 查询映射为对 KV 的查询,再通过 KV 接口获取对应的数据,最后执行各种计算。 + +比如 `select count(*) from user where name = "TiDB"` 这样一个 SQL 语句,它需要读取表中所有的数据,然后检查 `name` 字段是否是 `TiDB`,如果是的话,则返回这一行。具体流程如下: + +1. 构造出 Key Range:一个表中所有的 `RowID` 都在 `[0, MaxInt64)` 这个范围内,使用 `0` 和 `MaxInt64` 根据行数据的 `Key` 编码规则,就能构造出一个 `[StartKey, EndKey)`的左闭右开区间。 +2. 扫描 Key Range:根据上面构造出的 Key Range,读取 TiKV 中的数据。 +3. 过滤数据:对于读到的每一行数据,计算 `name = "TiDB"` 这个表达式,如果为真,则向上返回这一行,否则丢弃这一行数据。 +4. 计算 `Count(*)`:对符合要求的每一行,累计到 `Count(*)` 的结果上面。 + +**整个流程示意图如下:** + +![naive sql flow](/media/tidb-computing-native-sql-flow.jpeg) + +这个方案是直观且可行的,但是在分布式数据库的场景下有一些显而易见的问题: + +- 在扫描数据的时候,每一行都要通过 KV 操作从 TiKV 中读取出来,至少有一次 RPC 开销,如果需要扫描的数据很多,那么这个开销会非常大。 +- 并不是所有的行都满足过滤条件 `name = "TiDB"`,如果不满足条件,其实可以不读取出来。 +- 符合要求的行的值并没有什么意义,实际上这里只需要有几行数据这个信息就行。 + +### 分布式 SQL 运算 + +为了解决上述问题,计算应该需要尽量靠近存储节点,以避免大量的 RPC 调用。首先,SQL 中的谓词条件 `name = "TiDB"` 应被下推到存储节点进行计算,这样只需要返回有效的行,避免无意义的网络传输。然后,聚合函数 `Count(*)` 也可以被下推到存储节点,进行预聚合,每个节点只需要返回一个 `Count(*)` 的结果即可,再由 SQL 层将各个节点返回的 `Count(*)` 的结果累加求和。 + +以下是数据逐层返回的示意图: + +![dist sql flow](/media/tidb-computing-dist-sql-flow.png) + +### SQL 层架构 + +通过上面的例子,希望大家对 SQL 语句的处理有一个基本的了解。实际上 TiDB 的 SQL 层要复杂得多,模块以及层次非常多,下图列出了重要的模块以及调用关系: + +![tidb sql layer](/media/tidb-computing-tidb-sql-layer.png) + +用户的 SQL 请求会直接或者通过 `Load Balancer` 发送到 TiDB Server,TiDB Server 会解析 `MySQL Protocol Packet`,获取请求内容,对 SQL 进行语法解析和语义分析,制定和优化查询计划,执行查询计划并获取和处理数据。数据全部存储在 TiKV 集群中,所以在这个过程中 TiDB Server 需要和 TiKV 交互,获取数据。最后 TiDB Server 需要将查询结果返回给用户。 diff --git a/tidb-computing.md b/tidb-computing.md new file mode 100644 index 0000000000000..deb91699acd91 --- /dev/null +++ b/tidb-computing.md @@ -0,0 +1,158 @@ +--- +title: TiDB database computation +summary: Understand the computation layer of the TiDB database. +category: introduction +--- + +# Computation of TiDB database + +Bases on the distributed storage capability provided by TiKV, TiDB builds the computing engine that combines superior transaction processing with good data analysis capabilties. This article starts with a data mapping algorithm to describe how TiDB maps data from database tables to TiKV's (Key, Value) key-value pairs, then a description of how TiDB manages meta-information, and finally a description of the main architecture of the TiDB SQL layer. + +For computation layer dependent storage schemes, this paper only introduces TiKV based row storage structures. For analytic services, TiDB introduces a column storage scheme as a TiKV extension [TiFlash](/tiflash/tiflash-overview.md). + +## Mapping of table data to Key-Value + +This section describes the scheme for mapping data to (Key, Value) key-value pairs in TiDB. Data here consists of the following two main aspects. + +- Data for each row in the table, hereinafter referred to as table data. +- Data for all indexes in the table, hereinafter referred to as index data. + +### Mapping of table data to Key-Value + +In a relational database, a table may have many columns. To map the data from each column in a row to a (Key, Value) key-value pair, you need to consider how to construct the Key. First of all, OLTP scenarios have a large number of operations such as adding, deleting, changing and searching for single or multiple rows, which require database to read a line of data quickly. Therefore, it is best to have a unique ID (either displayed or implicit) for the corresponding key to facilitate quick location. Second, many OLAP queries require a full table scan. If you can encode the keys of all rows in a table into an interval, the whole table can be efficiently scanned by range queries. Scanning assignments. + +Based on the above considerations, the mapping of table data to Key-Value in TiDB is designed as follows. + +- To ensure that data from the same table is kept together for easy searching, TiDB assigns a table ID to each table, using the denoted by `TableID`. Table ID is an integer that is unique throughout the cluster. +- TiDB assigns a row ID, represented by `RowID`, to each row of data in the table. The row ID is also an integer, unique within the table. For row ID, TiDB has made a small optimization, if a table has integer type primary key, TiDB will use primary key value as the row ID of this row of data. + +Each row of data is encoded as a (Key, Value) key-value pair according to the following rules. + +``` +Key: tablePrefix{TableID}_recordPrefixSep{ RowID} +Value: [col1, col2, col3, col4] +``` + +where `tablePrefix` and `recordPrefixSep` are both specific to `tablePrefix` and `recordPrefixSep`. A string constant used to distinguish other data in Key space. The exact value is given in the summary that follows. + +### Mapping of Indexed Data to Key-Values + +TiDB supports both primary keys and secondary indexes (both unique and non-unique indexes). Similar to the table data mapping scheme, TiDB assigns an index ID to each index in the table, using ` IndexID` is indicated. + +For primary keys and unique indexes, it is necessary to quickly locate the corresponding RowID based on the key value, so it is encoded as follows (Key, Value) Key-value pairs. + +``` +Key: tablePrefix{tableID}_indexPrefixSep{ indexID}_indexedColumnsValue +Value: RowID +``` + +For ordinary secondary indexes that do not need to satisfy the uniqueness constraint, a single key may correspond to multiple rows, which need to be queried according to the range of keys. Therefore, it is encoded as a (Key, Value) key-value pair according to the following rules. + +``` +Key: tablePrefix{TableID}_indexPrefixSep{ IndexID}_indexedColumnsValue_{RowID} +Value: null +``` + +### Summary of mapping relationships + +`tablePrefix`, `recordPrefixSep` in all of the above encoding rules. and `indexPrefixSep` are string constants that are used to distinguish between other Data, defined as follows. + +``` +tablePrefix = []byte{'t'} +recordPrefixSep = []byte{'r'} +indexPrefixSep = []byte{'i'} +``` + +Also note that in the above scheme, all rows in a table are keyed, regardless of whether they are table data or index data. All have the same Key prefix, and all the data of an index also has the same prefix. Data with the same prefixes are thus arranged together in TiKV's key space. Therefore, by carefully designing the encoding scheme of the suffix section to ensure that the pre- and post-encoding comparisons remain the same, the table data can be or index data is stored in the TiKV in an ordered manner. With this encoding, all rows of data in a table are arranged in the TiKV's In the key space, the data for a particular index will also be indexed according to the specific value of the index data (the ` indexedColumnsValue`) is sequentially arranged in the keyspace. + +### Example of Key-Value mapping relationship + +Finally, a simple example is used to understand the Key-Value mapping relationship of TiDB. Suppose the following table exists in TiDB. + +``sql +CREATE TABLE User { + ID int, + Name varchar(20), + Role varchar(20), + Age int, + PRIMARY KEY (ID), + KEY idxAge (Age) +}; +``` + +Suppose there are 3 rows of data in the table. + +``` +1, "TiDB", "SQL Layer", 10 +2, "TiKV", "KV Engine", 20 +3, "PD", "Manager", 30 +``` + +First, each row of data is mapped to a (Key, Value) key-value pair, and the table has an ``int'' value. type `, so the value of `RowID` is the value of this primary key. Suppose the table has `TableID` of 10, then its table data stored on TiKV is. + +``` +t10_r1 --> ["TiDB", "SQL Layer", 10] +t10_r2 --> ["TiKV", "KV Engine", 20] +t10_r3 --> ["PD", " Manager", 30] +``` + +In addition to the primary key, the table has a non-unique ordinary secondary index, `idxAge`, which is assumed to have a ` IndexID` is 1, then its index data stored on TiKV is. + +``` +t10_i1_10_1 --> null +t10_i1_20_2 --> null +t10_i1_30_3 --> null +``` + +The above example shows the mapping rule from a relational model to a Key-Value model in TiDB, and the selection of this The considerations behind the program. + +## Meta-information management + +Each `Database` and `Table` in TiDB has meta information, i.e., its definition of the and various attributes. This information also needs to be persistent, and TiDB stores this information in TiKV as well. + +Each `Database` / `Table` is assigned a unique ID, which is the ID of the as a unique identifier, and when encoded as Key-Value, this ID is encoded in the Key and the `m_` prefix. This constructs a Key, which stores the serialized meta information in Value. + +In addition, TiDB also uses a dedicated (Key, Value) key pair to store the current structure of all tables. The latest version number of the information. This key-value pair is global, and its version number is increased by 1 each time the state of the DDL operation changes. Store this key-value pair persistently in the PD Server with a key of "/". tidb/ddl/global_schema_version", Value is a version number value of type int64. TiDB references Google F1's Online Schema change algorithm with a background thread constantly checking the PD Server The version number of the table structure information stored in the + +## Introduction to the SQL layer + +TiDB's SQL layer, TiDB Server, is responsible for translating the SQL into Key- Value operation to the common distributed Key-Value storage layer TiKV, and then the Assembling the results returned by TiKV ultimately returns the query results to the client. + +The nodes at this level are stateless, the nodes themselves do not store data, and the nodes are completely peer-to-peer. + +### SQL algorithm + +The simplest solution is through the [mapping of table data to Key-Value] as described in the previous section (#TableData mapping to -key-value-) scheme, mapping SQL queries to KV queries, and then the The KV interface acquires the corresponding data and performs various computations. + +For example, `select count(*) from user where name = "TiDB"` such a SQL statement, which takes all the data in the table and reads it. Then check if the `name` field is `TiDB`, and if so, return this line. The process is as follows. + +1. construct the Key Range: all `RowID`s in a table are in `[0, MaxInt64) `, use `0` and `MaxInt64` depending on the row data within the range of `0` and `MaxInt64`. The `Key` encoding rule constructs a `[StartKey, EndKey)` left-handed closure. Right open interval. +Scan Key Range: read TiKV according to the key range constructed above. The data in. +3. filter data: for each row of data read, calculate `name = "TiDB& quot;` This expression returns up this line if true, otherwise discards this line of data. +4. Calculate `Count(*)`: for each line that meets the requirements, the total of the Results Above. + +** The entire process is illustrated as follows:** + +![naive sql flow](/media/tidb-computing-native-sql-flow.jpeg) + +This solution is intuitive and feasible, but has some obvious problems in a distributed database scenario. + +- As the data is being scanned, each row is read out of TiKV via a KV operation at least once RPC overhead, which can be very high if there is a lot of data to scan. +- Not all rows meet the filter criteria `name = "TiDB"`. If the conditions are not met, you can actually not read them out. +- The value of the rows that meet the requirements doesn't mean anything, in fact all that's needed here is a few rows of data for this information. + +### Distributed SQL operations + +To solve the above problem, the computation should need to be as close to the storage node as possible to avoid a large number of RPC calls. First of all, the SQL predicate condition `name = "TiDB"` should be replaced by the is pushed down to the storage node for computation, so that only valid rows are returned, avoiding meaningless network transfers. Then, the aggregation function `Count(*)` can also be pushed down to the storage nodes for pre-aggregation, where each node only has You need to return a result of `Count(*)`, and the SQL layer will then return the results of the ` The result of Count(*) ` is summed up. + +The following is a schematic representation of the data returned layer by layer. + +![dist sql flow](/media/tidb-computing-dist-sql-flow.png) + +### SQL layer architecture + +With the above example, I hope you have a basic understanding of how SQL statements are handled. In fact, TiDB's SQL layer is much more complex, with many modules and layers. and calling relationships. + +! [tidb sql layer](/media/tidb-computing-tidb -sql-layer.png) + +The user's SQL request is sent to TiDB either directly or via `Load Balancer`. Server, TiDB Server will parse `MySQL Protocol' Packet`, getting the content of requests, parsing syntax and semantic analysis of SQL, developing and optimizing query plans. Execute a query plan and fetch and process the data. The data is all stored in the TiKV cluster, so in this process TiDB Server needs to work with the TiKV interacts and gets the data. Finally, TiDB Server needs to return the query results to the user. \ No newline at end of file From 104a02f1c5abafba36d976cdbc78164cc988f19f Mon Sep 17 00:00:00 2001 From: baurine <2008.hbl@gmail.com> Date: Sun, 28 Jun 2020 20:40:22 +0800 Subject: [PATCH 02/56] wip --- tidb-computing-zh.md | 2 +- tidb-computing.md | 74 ++++++++++++++++++++++---------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/tidb-computing-zh.md b/tidb-computing-zh.md index 7da7e317269af..e37a3cd71e9e5 100644 --- a/tidb-computing-zh.md +++ b/tidb-computing-zh.md @@ -19,7 +19,7 @@ TiDB 在 TiKV 提供的分布式存储能力基础上,构建了兼具优异的 ### 表数据与 Key-Value 的映射关系 -在关系型数据库中,一个表可能有很多列。要将一行中各列数据映射成一个 (Key, Value) 键值对,需要考虑如何构造 Key。首先,OLTP 场景下有大量针对单行或者多行的增、删、改、查等操作,要求数据库具备快速读取一行数据的能力。因此,对应的 Key 最好有一个唯一 ID(显示或隐式的 ID),以方便快速定位。其次,很多 OLAP 型查询需要进行全表扫描。如果能够将一个表中所有行的 Key 编码到一个区间内,就可以通过范围查询高效完成全表扫描的任务。 +在关系型数据库中,一个表可能有很多列。要将一行中各列数据映射成一个 (Key, Value) 键值对,需要考虑如何构造 Key。首先,OLTP 场景下有大量针对单行或者多行的增、删、改、查等操作,要求数据库具备快速读取一行数据的能力。因此,对应的 Key 最好有一个唯一 ID(显式或隐式的 ID),以方便快速定位。其次,很多 OLAP 型查询需要进行全表扫描。如果能够将一个表中所有行的 Key 编码到一个区间内,就可以通过范围查询高效完成全表扫描的任务。 基于上述考虑,TiDB 中的表数据与 Key-Value 的映射关系作了如下设计: diff --git a/tidb-computing.md b/tidb-computing.md index deb91699acd91..ceed2773acd35 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -6,7 +6,7 @@ category: introduction # Computation of TiDB database -Bases on the distributed storage capability provided by TiKV, TiDB builds the computing engine that combines superior transaction processing with good data analysis capabilties. This article starts with a data mapping algorithm to describe how TiDB maps data from database tables to TiKV's (Key, Value) key-value pairs, then a description of how TiDB manages meta-information, and finally a description of the main architecture of the TiDB SQL layer. +Based on the distributed storage capability provided by TiKV, TiDB builds the computing engine that combines superior transaction processing with good data analysis capabilties. This article starts with a data mapping algorithm to describe how TiDB maps data from database tables to TiKV's (Key, Value) key-value pairs, then a description of how TiDB manages meta-information, and finally a description of the main architecture of the TiDB SQL layer. For computation layer dependent storage schemes, this paper only introduces TiKV based row storage structures. For analytic services, TiDB introduces a column storage scheme as a TiKV extension [TiFlash](/tiflash/tiflash-overview.md). @@ -19,51 +19,51 @@ This section describes the scheme for mapping data to (Key, Value) key-value pai ### Mapping of table data to Key-Value -In a relational database, a table may have many columns. To map the data from each column in a row to a (Key, Value) key-value pair, you need to consider how to construct the Key. First of all, OLTP scenarios have a large number of operations such as adding, deleting, changing and searching for single or multiple rows, which require database to read a line of data quickly. Therefore, it is best to have a unique ID (either displayed or implicit) for the corresponding key to facilitate quick location. Second, many OLAP queries require a full table scan. If you can encode the keys of all rows in a table into an interval, the whole table can be efficiently scanned by range queries. Scanning assignments. +In a relational database, a table may have many columns. To map the data from each column in a row to a (Key, Value) key-value pair, you need to consider how to construct the Key. First of all, OLTP scenarios have a large number of operations such as adding, deleting, changing and searching for single or multiple rows, which require database to read a line of data quickly. Therefore, it is best to have a unique ID (either explicit or implicit) for the corresponding key to facilitate quick location. Second, many OLAP queries require a full table scan. If you can encode the keys of all rows in a table into a range, the whole table can be efficiently scanned by range queries. -Based on the above considerations, the mapping of table data to Key-Value in TiDB is designed as follows. +Based on the above considerations, the mapping of table data to Key-Value in TiDB is designed as follows: -- To ensure that data from the same table is kept together for easy searching, TiDB assigns a table ID to each table, using the denoted by `TableID`. Table ID is an integer that is unique throughout the cluster. +- To ensure that data from the same table is kept together for easy searching, TiDB assigns a table ID to each table denoted by `TableID`. Table ID is an integer that is unique throughout the cluster. - TiDB assigns a row ID, represented by `RowID`, to each row of data in the table. The row ID is also an integer, unique within the table. For row ID, TiDB has made a small optimization, if a table has integer type primary key, TiDB will use primary key value as the row ID of this row of data. -Each row of data is encoded as a (Key, Value) key-value pair according to the following rules. +Each row of data is encoded as a (Key, Value) key-value pair according to the following rule: ``` -Key: tablePrefix{TableID}_recordPrefixSep{ RowID} +Key: tablePrefix{TableID}_recordPrefixSep{RowID} Value: [col1, col2, col3, col4] ``` -where `tablePrefix` and `recordPrefixSep` are both specific to `tablePrefix` and `recordPrefixSep`. A string constant used to distinguish other data in Key space. The exact value is given in the summary that follows. +`tablePrefix` and `recordPrefixSep` are both special string constant used to distinguish other data in Key space. The exact value is given in the summary that follows. -### Mapping of Indexed Data to Key-Values +### Mapping of Indexed Data to Key-Value -TiDB supports both primary keys and secondary indexes (both unique and non-unique indexes). Similar to the table data mapping scheme, TiDB assigns an index ID to each index in the table, using ` IndexID` is indicated. +TiDB supports both primary keys and secondary indexes (both unique and non-unique indexes). Similar to the table data mapping scheme, TiDB assigns an index ID to each index in the table indicated by `IndexID`. -For primary keys and unique indexes, it is necessary to quickly locate the corresponding RowID based on the key value, so it is encoded as follows (Key, Value) Key-value pairs. +For primary keys and unique indexes, it is needed to quickly locate the corresponding RowID based on the key value, so it is encoded as follows (Key, Value) Key-value pairs. ``` -Key: tablePrefix{tableID}_indexPrefixSep{ indexID}_indexedColumnsValue +Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue Value: RowID ``` -For ordinary secondary indexes that do not need to satisfy the uniqueness constraint, a single key may correspond to multiple rows, which need to be queried according to the range of keys. Therefore, it is encoded as a (Key, Value) key-value pair according to the following rules. +For ordinary secondary indexes that do not need to satisfy the uniqueness constraint, a single key may correspond to multiple rows. It needs to query corresponding RowID according to the range of keys. Therefore, it is encoded as a (Key, Value) key-value pair according to the following rule: ``` -Key: tablePrefix{TableID}_indexPrefixSep{ IndexID}_indexedColumnsValue_{RowID} +Key: tablePrefix{TableID}_indexPrefixSep{IndexID}_indexedColumnsValue_{RowID} Value: null ``` ### Summary of mapping relationships -`tablePrefix`, `recordPrefixSep` in all of the above encoding rules. and `indexPrefixSep` are string constants that are used to distinguish between other Data, defined as follows. +`tablePrefix`, `recordPrefixSep`, and `indexPrefixSep` in all of the above encoding rules are string constants that are used to distinguish between other Data in Key space, defined as follows: ``` -tablePrefix = []byte{'t'} +tablePrefix = []byte{'t'} recordPrefixSep = []byte{'r'} -indexPrefixSep = []byte{'i'} +indexPrefixSep = []byte{'i'} ``` -Also note that in the above scheme, all rows in a table are keyed, regardless of whether they are table data or index data. All have the same Key prefix, and all the data of an index also has the same prefix. Data with the same prefixes are thus arranged together in TiKV's key space. Therefore, by carefully designing the encoding scheme of the suffix section to ensure that the pre- and post-encoding comparisons remain the same, the table data can be or index data is stored in the TiKV in an ordered manner. With this encoding, all rows of data in a table are arranged in the TiKV's In the key space, the data for a particular index will also be indexed according to the specific value of the index data (the ` indexedColumnsValue`) is sequentially arranged in the keyspace. +Also note that in the above schemes, regardless table data or index data key encoding scheme, all rows in a table have the same key prefix, and all the data of an index also has the same prefix. Data with the same prefixes are thus arranged together in TiKV's key space. Therefore, by carefully designing the encoding scheme of the suffix part to ensure that the pre and post-encoding comparisons remain the same, the table data or index data can be stored in the TiKV in an ordered manner. With this encoding, all rows of data in a table are arranged orderly by `RowID` in the TiKV's Key space, and the data for a particular index will also be arranged sequentially in the Key space according to the specific value of the index data (the `indexedColumnsValue`). ### Example of Key-Value mapping relationship @@ -88,7 +88,7 @@ Suppose there are 3 rows of data in the table. 3, "PD", "Manager", 30 ``` -First, each row of data is mapped to a (Key, Value) key-value pair, and the table has an ``int'' value. type `, so the value of `RowID` is the value of this primary key. Suppose the table has `TableID` of 10, then its table data stored on TiKV is. +First, each row of data is mapped to a (Key, Value) key-value pair, and the table has an `int` type primary key, so the value of `RowID` is the value of this primary key. Suppose the table has `TableID` of 10, then its table data stored on TiKV is: ``` t10_r1 --> ["TiDB", "SQL Layer", 10] @@ -96,7 +96,7 @@ t10_r2 --> ["TiKV", "KV Engine", 20] t10_r3 --> ["PD", " Manager", 30] ``` -In addition to the primary key, the table has a non-unique ordinary secondary index, `idxAge`, which is assumed to have a ` IndexID` is 1, then its index data stored on TiKV is. +In addition to the primary key, the table has a non-unique ordinary secondary index, `idxAge`. Suppose the `IndexID` is 1, then its index data stored on TiKV is: ``` t10_i1_10_1 --> null @@ -104,32 +104,32 @@ t10_i1_20_2 --> null t10_i1_30_3 --> null ``` -The above example shows the mapping rule from a relational model to a Key-Value model in TiDB, and the selection of this The considerations behind the program. +The above example shows the mapping rule from a relational model to a Key-Value model in TiDB, and the consideration behind the selection of this scheme. ## Meta-information management -Each `Database` and `Table` in TiDB has meta information, i.e., its definition of the and various attributes. This information also needs to be persistent, and TiDB stores this information in TiKV as well. +Each `Database` and `Table` in TiDB has meta information, aka its definition and various attributes. This information also needs to be persistent, and TiDB stores this information in TiKV as well. -Each `Database` / `Table` is assigned a unique ID, which is the ID of the as a unique identifier, and when encoded as Key-Value, this ID is encoded in the Key and the `m_` prefix. This constructs a Key, which stores the serialized meta information in Value. +Each `Database` / `Table` is assigned an unique ID. As the unique identifier, when encoded as Key-Value, this ID is encoded in the Key with the `m_` prefix. This constructs a Key, and stores the serialized meta information in Value. -In addition, TiDB also uses a dedicated (Key, Value) key pair to store the current structure of all tables. The latest version number of the information. This key-value pair is global, and its version number is increased by 1 each time the state of the DDL operation changes. Store this key-value pair persistently in the PD Server with a key of "/". tidb/ddl/global_schema_version", Value is a version number value of type int64. TiDB references Google F1's Online Schema change algorithm with a background thread constantly checking the PD Server The version number of the table structure information stored in the +In addition, TiDB also uses a dedicated (Key, Value) key pair to store the latest version number of the current all tables structure information. This key-value pair is global, and its version number is increased by 1 each time the state of the DDL operation changes. TiDB stores this key-value pair persistently in the PD Server with a key of "/tidb/ddl/global_schema_version", and Value is the version number value of int64 type. Refers to Google F1's Online Schema change algorithm, TiDB keeps a background thread constantly checking whether the version number of the table structure information stored in the PD Server changes, and ensuring to get the changes of version in a certain time. ## Introduction to the SQL layer -TiDB's SQL layer, TiDB Server, is responsible for translating the SQL into Key- Value operation to the common distributed Key-Value storage layer TiKV, and then the Assembling the results returned by TiKV ultimately returns the query results to the client. +TiDB's SQL layer, TiDB Server, is responsible for translating the SQL into Key- Value operation to the common distributed Key-Value storage layer TiKV, assembling the results returned by TiKV, returning the query results to the client ultimately. -The nodes at this level are stateless, the nodes themselves do not store data, and the nodes are completely peer-to-peer. +The nodes at this layer are stateless, the nodes themselves do not store data, and the nodes are completely equal. ### SQL algorithm -The simplest solution is through the [mapping of table data to Key-Value] as described in the previous section (#TableData mapping to -key-value-) scheme, mapping SQL queries to KV queries, and then the The KV interface acquires the corresponding data and performs various computations. +The simplest solution is through the [mapping of table data to Key-Value](#TableData mapping to -key-value-) as described in the previous section scheme, mapping SQL queries to KV queries, and then acquires the corresponding data through the KV interface and performs various computations. -For example, `select count(*) from user where name = "TiDB"` such a SQL statement, which takes all the data in the table and reads it. Then check if the `name` field is `TiDB`, and if so, return this line. The process is as follows. +For example, `select count(*) from user where name = "TiDB"` such a SQL statement. It needs to read all the data in the table, then checks if the `name` field is `TiDB`, and if so, returns this line. The process is as follows: -1. construct the Key Range: all `RowID`s in a table are in `[0, MaxInt64) `, use `0` and `MaxInt64` depending on the row data within the range of `0` and `MaxInt64`. The `Key` encoding rule constructs a `[StartKey, EndKey)` left-handed closure. Right open interval. -Scan Key Range: read TiKV according to the key range constructed above. The data in. -3. filter data: for each row of data read, calculate `name = "TiDB& quot;` This expression returns up this line if true, otherwise discards this line of data. -4. Calculate `Count(*)`: for each line that meets the requirements, the total of the Results Above. +1. construct the Key Range: all `RowID`s in a table are in `[0, MaxInt64) `. According to the row data `Key` encoding rule, using `0` and `MaxInt64` can construct a `[StartKey, EndKey)` range that is left-included, right-exclued. +2. scan Key Range: read the data in TiKV according to the key range constructed above. +3. filter data: for each row of data read, calculate `name = "TiDB"` expression returns up this line if true, otherwise discards this line of data. +4. calculate `Count(*)`: for each line that meets the requirements, accumulate to the result of `Count(*)`. ** The entire process is illustrated as follows:** @@ -137,13 +137,13 @@ Scan Key Range: read TiKV according to the key range constructed above. The dat This solution is intuitive and feasible, but has some obvious problems in a distributed database scenario. -- As the data is being scanned, each row is read out of TiKV via a KV operation at least once RPC overhead, which can be very high if there is a lot of data to scan. +- As the data is being scanned, each row is read out of TiKV via a KV operation at least once RPC overhead, which can be very high if there is a lot of data to scan. - Not all rows meet the filter criteria `name = "TiDB"`. If the conditions are not met, you can actually not read them out. -- The value of the rows that meet the requirements doesn't mean anything, in fact all that's needed here is a few rows of data for this information. +- The value of the rows that meet the requirements doesn't mean anything, in fact all that's needed here is the information of how many rows of data. ### Distributed SQL operations -To solve the above problem, the computation should need to be as close to the storage node as possible to avoid a large number of RPC calls. First of all, the SQL predicate condition `name = "TiDB"` should be replaced by the is pushed down to the storage node for computation, so that only valid rows are returned, avoiding meaningless network transfers. Then, the aggregation function `Count(*)` can also be pushed down to the storage nodes for pre-aggregation, where each node only has You need to return a result of `Count(*)`, and the SQL layer will then return the results of the ` The result of Count(*) ` is summed up. +To solve the above problem, the computation should need to be as close to the storage node as possible to avoid a large number of RPC calls. First of all, the SQL predicate condition `name = "TiDB"` should be pushed down to the storage node for computation, so that only valid rows are returned, avoiding meaningless network transfers. Then, the aggregation function `Count(*)` can also be pushed down to the storage nodes for pre-aggregation, and each node only has to return a result of `Count(*)`, and the SQL layer will sum up the `Count(*)` result returned by each node. The following is a schematic representation of the data returned layer by layer. @@ -151,8 +151,8 @@ The following is a schematic representation of the data returned layer by layer. ### SQL layer architecture -With the above example, I hope you have a basic understanding of how SQL statements are handled. In fact, TiDB's SQL layer is much more complex, with many modules and layers. and calling relationships. +With the above example, hope you have a basic understanding of how SQL statements are handled. In fact, TiDB's SQL layer is much more complex, with many modules and layers. The following diagram lists the important modules and calling relationships: -! [tidb sql layer](/media/tidb-computing-tidb -sql-layer.png) +![tidb sql layer](/media/tidb-computing-tidb-sql-layer.png) -The user's SQL request is sent to TiDB either directly or via `Load Balancer`. Server, TiDB Server will parse `MySQL Protocol' Packet`, getting the content of requests, parsing syntax and semantic analysis of SQL, developing and optimizing query plans. Execute a query plan and fetch and process the data. The data is all stored in the TiKV cluster, so in this process TiDB Server needs to work with the TiKV interacts and gets the data. Finally, TiDB Server needs to return the query results to the user. \ No newline at end of file +The user's SQL request is sent to TiDB Server either directly or via `Load Balancer`. TiDB Server will parse `MySQL Protocol Packet`, getting the content of requests, parsing syntax and semantic analysis of SQL, developing and optimizing query plans. Execute a query plan and fetch and process the data. The data is all stored in the TiKV cluster, so in this process TiDB Server needs to work with the TiKV interacts and gets the data. Finally, TiDB Server needs to return the query results to the user. \ No newline at end of file From 16232741c1f80a47f5ee358518a542fec9fca1ed Mon Sep 17 00:00:00 2001 From: baurine <2008.hbl@gmail.com> Date: Sun, 28 Jun 2020 21:26:21 +0800 Subject: [PATCH 03/56] wip --- tidb-computing.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tidb-computing.md b/tidb-computing.md index ceed2773acd35..36ac11019fc49 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -6,9 +6,9 @@ category: introduction # Computation of TiDB database -Based on the distributed storage capability provided by TiKV, TiDB builds the computing engine that combines superior transaction processing with good data analysis capabilties. This article starts with a data mapping algorithm to describe how TiDB maps data from database tables to TiKV's (Key, Value) key-value pairs, then a description of how TiDB manages meta-information, and finally a description of the main architecture of the TiDB SQL layer. +Based on the distributed storage capability provided by TiKV, TiDB builds the computing engine that combines superior transaction processing with good data analysis capabilities. This article starts with a data mapping algorithm to describe how TiDB maps data from database tables to TiKV's (Key, Value) key-value pairs, then a description of how TiDB manages meta-information, and finally a description of the main architecture of the TiDB SQL layer. -For computation layer dependent storage schemes, this paper only introduces TiKV based row storage structures. For analytic services, TiDB introduces a column storage scheme as a TiKV extension [TiFlash](/tiflash/tiflash-overview.md). +For the computation layer dependent storage schemes, this article only introduces TiKV based row storage structures. For analytic services, TiDB introduces a column storage scheme as a TiKV extension - [TiFlash](/tiflash/tiflash-overview.md). ## Mapping of table data to Key-Value @@ -19,7 +19,7 @@ This section describes the scheme for mapping data to (Key, Value) key-value pai ### Mapping of table data to Key-Value -In a relational database, a table may have many columns. To map the data from each column in a row to a (Key, Value) key-value pair, you need to consider how to construct the Key. First of all, OLTP scenarios have a large number of operations such as adding, deleting, changing and searching for single or multiple rows, which require database to read a line of data quickly. Therefore, it is best to have a unique ID (either explicit or implicit) for the corresponding key to facilitate quick location. Second, many OLAP queries require a full table scan. If you can encode the keys of all rows in a table into a range, the whole table can be efficiently scanned by range queries. +In a relational database, a table may have many columns. To map the data from each column in a row to a (Key, Value) key-value pair, you need to consider how to construct the Key. First of all, OLTP scenarios have a large number of operations such as adding, deleting, changing and searching for single or multiple rows, which require the database to read a line of data quickly. Therefore, it is best to have a unique ID (either explicit or implicit) for the corresponding key to facilitate a quick location. Second, many OLAP queries require a full table scan. If you can encode the keys of all rows in a table into a range, the whole table can be efficiently scanned by range queries. Based on the above considerations, the mapping of table data to Key-Value in TiDB is designed as follows: @@ -33,7 +33,7 @@ Key: tablePrefix{TableID}_recordPrefixSep{RowID} Value: [col1, col2, col3, col4] ``` -`tablePrefix` and `recordPrefixSep` are both special string constant used to distinguish other data in Key space. The exact value is given in the summary that follows. +`tablePrefix` and `recordPrefixSep` are both special string constants used to distinguish other data in Key space. The exact value is given in the summary that follows. ### Mapping of Indexed Data to Key-Value @@ -63,7 +63,7 @@ recordPrefixSep = []byte{'r'} indexPrefixSep = []byte{'i'} ``` -Also note that in the above schemes, regardless table data or index data key encoding scheme, all rows in a table have the same key prefix, and all the data of an index also has the same prefix. Data with the same prefixes are thus arranged together in TiKV's key space. Therefore, by carefully designing the encoding scheme of the suffix part to ensure that the pre and post-encoding comparisons remain the same, the table data or index data can be stored in the TiKV in an ordered manner. With this encoding, all rows of data in a table are arranged orderly by `RowID` in the TiKV's Key space, and the data for a particular index will also be arranged sequentially in the Key space according to the specific value of the index data (the `indexedColumnsValue`). +Also note that in the above schemes, regardless of table data or index data key encoding scheme, all rows in a table have the same key prefix, and all the data of an index also has the same prefix. Data with the same prefixes are thus arranged together in TiKV's Key space. Therefore, by carefully designing the encoding scheme of the suffix part to ensure that the pre and post-encoding comparisons remain the same, the table data or index data can be stored in the TiKV in an ordered manner. With this encoding, all rows of data in a table are arranged orderly by `RowID` in the TiKV's Key space, and the data for a particular index will also be arranged sequentially in the Key space according to the specific value of the index data (the `indexedColumnsValue`). ### Example of Key-Value mapping relationship @@ -112,23 +112,23 @@ Each `Database` and `Table` in TiDB has meta information, aka its definition and Each `Database` / `Table` is assigned an unique ID. As the unique identifier, when encoded as Key-Value, this ID is encoded in the Key with the `m_` prefix. This constructs a Key, and stores the serialized meta information in Value. -In addition, TiDB also uses a dedicated (Key, Value) key pair to store the latest version number of the current all tables structure information. This key-value pair is global, and its version number is increased by 1 each time the state of the DDL operation changes. TiDB stores this key-value pair persistently in the PD Server with a key of "/tidb/ddl/global_schema_version", and Value is the version number value of int64 type. Refers to Google F1's Online Schema change algorithm, TiDB keeps a background thread constantly checking whether the version number of the table structure information stored in the PD Server changes, and ensuring to get the changes of version in a certain time. +Besides, TiDB also uses a dedicated (Key, Value) key pair to store the latest version number of the current all tables structure information. This key-value pair is global, and its version number is increased by 1 each time the state of the DDL operation changes. TiDB stores this key-value pair persistently in the PD Server with a key of "/tidb/ddl/global_schema_version", and Value is the version number value of int64 type. Refers to Google F1's Online Schema change algorithm, TiDB keeps a background thread constantly checking whether the version number of the table structure information stored in the PD Server changes, and ensuring to get the changes of version in a certain time. ## Introduction to the SQL layer -TiDB's SQL layer, TiDB Server, is responsible for translating the SQL into Key- Value operation to the common distributed Key-Value storage layer TiKV, assembling the results returned by TiKV, returning the query results to the client ultimately. +TiDB's SQL layer, TiDB Server, is responsible for translating the SQL into Key-Value operation to the common distributed Key-Value storage layer TiKV, assembling the results returned by TiKV, and returning the query results to the client ultimately. The nodes at this layer are stateless, the nodes themselves do not store data, and the nodes are completely equal. ### SQL algorithm -The simplest solution is through the [mapping of table data to Key-Value](#TableData mapping to -key-value-) as described in the previous section scheme, mapping SQL queries to KV queries, and then acquires the corresponding data through the KV interface and performs various computations. +The simplest solution is through the [mapping of table data to Key-Value](#mapping-of-table-data-to-key-value) as described in the previous section scheme, mapping SQL queries to KV queries, and then acquires the corresponding data through the KV interface and performs various computations. -For example, `select count(*) from user where name = "TiDB"` such a SQL statement. It needs to read all the data in the table, then checks if the `name` field is `TiDB`, and if so, returns this line. The process is as follows: +For example, `select count(*) from user where name = "TiDB"` such a SQL statement. It needs to read all the data in the table, then check if the `name` field is `TiDB`, and if so, returns this line. The process is as follows: -1. construct the Key Range: all `RowID`s in a table are in `[0, MaxInt64) `. According to the row data `Key` encoding rule, using `0` and `MaxInt64` can construct a `[StartKey, EndKey)` range that is left-included, right-exclued. +1. construct the Key Range: all `RowID` in a table are in `[0, MaxInt64)` range. According to the row data `Key` encoding rule, using `0` and `MaxInt64` can construct a `[StartKey, EndKey)` range that is left-included, right-excluded. 2. scan Key Range: read the data in TiKV according to the key range constructed above. -3. filter data: for each row of data read, calculate `name = "TiDB"` expression returns up this line if true, otherwise discards this line of data. +3. filter data: for each row of data read, calculate `name = "TiDB"` expression. Returns up this line if true, otherwise discards this line of data. 4. calculate `Count(*)`: for each line that meets the requirements, accumulate to the result of `Count(*)`. ** The entire process is illustrated as follows:** @@ -138,8 +138,8 @@ For example, `select count(*) from user where name = "TiDB"` such a SQL stateme This solution is intuitive and feasible, but has some obvious problems in a distributed database scenario. - As the data is being scanned, each row is read out of TiKV via a KV operation at least once RPC overhead, which can be very high if there is a lot of data to scan. -- Not all rows meet the filter criteria `name = "TiDB"`. If the conditions are not met, you can actually not read them out. -- The value of the rows that meet the requirements doesn't mean anything, in fact all that's needed here is the information of how many rows of data. +- Not all rows meet the filter criteria `name = "TiDB"`. If the conditions are not met, you can not read them out. +- The value of the rows that meet the requirements doesn't mean anything, in fact, all needed here is the information of how many rows of data. ### Distributed SQL operations @@ -151,8 +151,8 @@ The following is a schematic representation of the data returned layer by layer. ### SQL layer architecture -With the above example, hope you have a basic understanding of how SQL statements are handled. In fact, TiDB's SQL layer is much more complex, with many modules and layers. The following diagram lists the important modules and calling relationships: +With the above example, I hope you have a basic understanding of how SQL statements are handled. In fact, TiDB's SQL layer is much more complex, with many modules and layers. The following diagram lists the important modules and calling relationships: ![tidb sql layer](/media/tidb-computing-tidb-sql-layer.png) -The user's SQL request is sent to TiDB Server either directly or via `Load Balancer`. TiDB Server will parse `MySQL Protocol Packet`, getting the content of requests, parsing syntax and semantic analysis of SQL, developing and optimizing query plans. Execute a query plan and fetch and process the data. The data is all stored in the TiKV cluster, so in this process TiDB Server needs to work with the TiKV interacts and gets the data. Finally, TiDB Server needs to return the query results to the user. \ No newline at end of file +The user's SQL request is sent to TiDB Server either directly or via `Load Balancer`. TiDB Server will parse `MySQL Protocol Packet`, getting the content of requests, parsing syntax and semantic analysis of SQL, developing and optimizing query plans, executing a query plan , fetching and processing the data. The data is all stored in the TiKV cluster, so in this process, TiDB Server needs to interact with the TiKV and gets the data. Finally, TiDB Server needs to return the query results to the user. From 93b35459a0c42057281a33155263625c314b39a2 Mon Sep 17 00:00:00 2001 From: baurine <2008.hbl@gmail.com> Date: Sun, 28 Jun 2020 21:39:58 +0800 Subject: [PATCH 04/56] wip --- tidb-computing.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tidb-computing.md b/tidb-computing.md index 36ac11019fc49..ddf9c00021b5c 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -12,7 +12,7 @@ For the computation layer dependent storage schemes, this article only introduce ## Mapping of table data to Key-Value -This section describes the scheme for mapping data to (Key, Value) key-value pairs in TiDB. Data here consists of the following two main aspects. +This section describes the scheme for mapping data to (Key, Value) key-value pairs in TiDB. Data here consists of the following two main aspects: - Data for each row in the table, hereinafter referred to as table data. - Data for all indexes in the table, hereinafter referred to as index data. @@ -55,7 +55,7 @@ Value: null ### Summary of mapping relationships -`tablePrefix`, `recordPrefixSep`, and `indexPrefixSep` in all of the above encoding rules are string constants that are used to distinguish between other Data in Key space, defined as follows: +`tablePrefix`, `recordPrefixSep`, and `indexPrefixSep` in all of the above encoding rules are string constants that are used to distinguish between other data in Key space, defined as follows: ``` tablePrefix = []byte{'t'} @@ -69,7 +69,7 @@ Also note that in the above schemes, regardless of table data or index data key Finally, a simple example is used to understand the Key-Value mapping relationship of TiDB. Suppose the following table exists in TiDB. -``sql +```sql CREATE TABLE User { ID int, Name varchar(20), @@ -138,7 +138,7 @@ For example, `select count(*) from user where name = "TiDB"` such a SQL statemen This solution is intuitive and feasible, but has some obvious problems in a distributed database scenario. - As the data is being scanned, each row is read out of TiKV via a KV operation at least once RPC overhead, which can be very high if there is a lot of data to scan. -- Not all rows meet the filter criteria `name = "TiDB"`. If the conditions are not met, you can not read them out. +- Not all rows meet the filter criteria `name = "TiDB"`. If the conditions are not met, they are unnessary to be read out. - The value of the rows that meet the requirements doesn't mean anything, in fact, all needed here is the information of how many rows of data. ### Distributed SQL operations From 434a216a2ab8eb01c37fbcd7940ac190e27965c6 Mon Sep 17 00:00:00 2001 From: baurine <2008.hbl@gmail.com> Date: Sun, 28 Jun 2020 21:41:24 +0800 Subject: [PATCH 05/56] wip --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index ddf9c00021b5c..0a526096ec2d1 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -131,7 +131,7 @@ For example, `select count(*) from user where name = "TiDB"` such a SQL statemen 3. filter data: for each row of data read, calculate `name = "TiDB"` expression. Returns up this line if true, otherwise discards this line of data. 4. calculate `Count(*)`: for each line that meets the requirements, accumulate to the result of `Count(*)`. -** The entire process is illustrated as follows:** +**The entire process is illustrated as follows:** ![naive sql flow](/media/tidb-computing-native-sql-flow.jpeg) From dfecc77d1ea6f7b0b6dfb59e2fd4db38b7761f2b Mon Sep 17 00:00:00 2001 From: baurine <2008.hbl@gmail.com> Date: Sun, 28 Jun 2020 21:45:26 +0800 Subject: [PATCH 06/56] wip --- tidb-computing-zh.md | 158 ------------------------------------------- 1 file changed, 158 deletions(-) delete mode 100644 tidb-computing-zh.md diff --git a/tidb-computing-zh.md b/tidb-computing-zh.md deleted file mode 100644 index e37a3cd71e9e5..0000000000000 --- a/tidb-computing-zh.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -title: TiDB 数据库的计算 -summary: 了解 TiDB 数据库的计算层。 -category: introduction ---- - -# TiDB 数据库的计算 - -TiDB 在 TiKV 提供的分布式存储能力基础上,构建了兼具优异的交易处理能力与良好的数据分析能力的计算引擎。本文首先从数据映射算法入手介绍 TiDB 如何将库表中的数据映射到 TiKV 中的 (Key, Value) 键值对,然后描述 TiDB 元信息管理方式,最后介绍 TiDB SQL 层的主要架构。 - -对于计算层依赖的存储方案,本文只介绍基于 TiKV 的行存储结构。针对分析型业务的特点,TiDB 推出了作为 TiKV 扩展的列存储方案 [TiFlash](/tiflash/tiflash-overview.md)。 - -## 表数据与 Key-Value 的映射关系 - -本小节介绍 TiDB 中数据到 (Key, Value) 键值对的映射方案。这里的数据主要包括以下两个方面: - -- 表中每一行的数据,以下简称表数据 -- 表中所有索引的数据,以下简称索引数据 - -### 表数据与 Key-Value 的映射关系 - -在关系型数据库中,一个表可能有很多列。要将一行中各列数据映射成一个 (Key, Value) 键值对,需要考虑如何构造 Key。首先,OLTP 场景下有大量针对单行或者多行的增、删、改、查等操作,要求数据库具备快速读取一行数据的能力。因此,对应的 Key 最好有一个唯一 ID(显式或隐式的 ID),以方便快速定位。其次,很多 OLAP 型查询需要进行全表扫描。如果能够将一个表中所有行的 Key 编码到一个区间内,就可以通过范围查询高效完成全表扫描的任务。 - -基于上述考虑,TiDB 中的表数据与 Key-Value 的映射关系作了如下设计: - -- 为了保证同一个表的数据放在一起,方便查找,TiDB 会为每个表分配一个表 ID,用 `TableID` 表示。表 ID 是一个整数,在整个集群内唯一。 -- TiDB 会为表中每行数据分配一个行 ID,用 `RowID` 表示。行 ID 也是一个整数,在表内唯一。对于行 ID,TiDB 做了一个小优化,如果某个表有整数型的主键,TiDB 会使用主键的值当做这一行数据的行 ID。 - -每行数据按照如下规则编码成 (Key, Value) 键值对: - -``` -Key: tablePrefix{TableID}_recordPrefixSep{RowID} -Value: [col1, col2, col3, col4] -``` - -其中 `tablePrefix` 和 `recordPrefixSep` 都是特定的字符串常量,用于在 Key 空间内区分其他数据。其具体值在后面的小结中给出。 - -### 索引数据和 Key-Value 的映射关系 - -TiDB 同时支持主键和二级索引(包括唯一索引和非唯一索引)。与表数据映射方案类似,TiDB 为表中每个索引分配了一个索引 ID,用 `IndexID` 表示。 - -对于主键和唯一索引,需要根据键值快速定位到对应的 RowID,因此,按照如下规则编码成 (Key, Value) 键值对: - -``` -Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue -Value: RowID -``` - -对于不需要满足唯一性约束的普通二级索引,一个键值可能对应多行,需要根据键值范围查询对应的 RowID。因此,按照如下规则编码成 (Key, Value) 键值对: - -``` -Key: tablePrefix{TableID}_indexPrefixSep{IndexID}_indexedColumnsValue_{RowID} -Value: null -``` - -### 映射关系小结 - -上述所有编码规则中的 `tablePrefix`、`recordPrefixSep` 和 `indexPrefixSep` 都是字符串常量,用于在 Key 空间内区分其他数据,定义如下: - -``` -tablePrefix = []byte{'t'} -recordPrefixSep = []byte{'r'} -indexPrefixSep = []byte{'i'} -``` - -另外请注意,上述方案中,无论是表数据还是索引数据的 Key 编码方案,一个表内所有的行都有相同的 Key 前缀,一个索引的所有数据也都有相同的前缀。这样具有相同的前缀的数据,在 TiKV 的 Key 空间内,是排列在一起的。因此只要小心地设计后缀部分的编码方案,保证编码前和编码后的比较关系不变,就可以将表数据或者索引数据有序地保存在 TiKV 中。采用这种编码后,一个表的所有行数据会按照 `RowID` 顺序地排列在 TiKV 的 Key 空间中,某一个索引的数据也会按照索引数据的具体的值(编码方案中的 `indexedColumnsValue`)顺序地排列在 Key 空间内。 - -### Key-Value 映射关系示例 - -最后通过一个简单的例子,来理解 TiDB 的 Key-Value 映射关系。假设 TiDB 中有如下这个表: - -```sql -CREATE TABLE User { - ID int, - Name varchar(20), - Role varchar(20), - Age int, - PRIMARY KEY (ID), - KEY idxAge (Age) -}; -``` - -假设该表中有 3 行数据: - -``` -1, "TiDB", "SQL Layer", 10 -2, "TiKV", "KV Engine", 20 -3, "PD", "Manager", 30 -``` - -首先每行数据都会映射为一个 (Key, Value) 键值对,同时该表有一个 `int` 类型的主键,所以 `RowID` 的值即为该主键的值。假设该表的 `TableID` 为 10,则其存储在 TiKV 上的表数据为: - -``` -t10_r1 --> ["TiDB", "SQL Layer", 10] -t10_r2 --> ["TiKV", "KV Engine", 20] -t10_r3 --> ["PD", "Manager", 30] -``` - -除了主键外,该表还有一个非唯一的普通二级索引 `idxAge`,假设这个索引的 `IndexID` 为 1,则其存储在 TiKV 上的索引数据为: - -``` -t10_i1_10_1 --> null -t10_i1_20_2 --> null -t10_i1_30_3 --> null -``` - -以上例子展示了 TiDB 中关系模型到 Key-Value 模型的映射规则,以及选择该方案背后的考量。 - -## 元信息管理 - -TiDB 中每个 `Database` 和 `Table` 都有元信息,也就是其定义以及各项属性。这些信息也需要持久化,TiDB 将这些信息也存储在了 TiKV 中。 - -每个 `Database`/`Table` 都被分配了一个唯一的 ID,这个 ID 作为唯一标识,并且在编码为 Key-Value 时,这个 ID 都会编码到 Key 中,再加上 `m_` 前缀。这样可以构造出一个 Key,Value 中存储的是序列化后的元信息。 - -除此之外,TiDB 还用一个专门的 (Key, Value) 键值对存储当前所有表结构信息的最新版本号。这个键值对是全局的,每次 DDL 操作的状态改变时其版本号都会加 1。目前,TiDB 把这个键值对持久化存储在 PD Server 中,其 Key 是 "/tidb/ddl/global_schema_version",Value 是类型为 int64 的版本号值。TiDB 参考了 Google F1 的 Online Schema 变更算法,有一个后台线程在不断地检查 PD Server 中存储的表结构信息的版本号是否发生变化,并且保证在一定时间内一定能够获取版本的变化。 - -## SQL 层简介 - -TiDB 的 SQL 层,即 TiDB Server,负责将 SQL 翻译成 Key-Value 操作,将其转发给共用的分布式 Key-Value 存储层 TiKV,然后组装 TiKV 返回的结果,最终将查询结果返回给客户端。 - -这一层的节点都是无状态的,节点本身并不存储数据,节点之间完全对等。 - -### SQL 运算 - -最简单的方案就是通过上一节所述的[表数据与 Key-Value 的映射关系](#表数据与-key-value-的映射关系)方案,将 SQL 查询映射为对 KV 的查询,再通过 KV 接口获取对应的数据,最后执行各种计算。 - -比如 `select count(*) from user where name = "TiDB"` 这样一个 SQL 语句,它需要读取表中所有的数据,然后检查 `name` 字段是否是 `TiDB`,如果是的话,则返回这一行。具体流程如下: - -1. 构造出 Key Range:一个表中所有的 `RowID` 都在 `[0, MaxInt64)` 这个范围内,使用 `0` 和 `MaxInt64` 根据行数据的 `Key` 编码规则,就能构造出一个 `[StartKey, EndKey)`的左闭右开区间。 -2. 扫描 Key Range:根据上面构造出的 Key Range,读取 TiKV 中的数据。 -3. 过滤数据:对于读到的每一行数据,计算 `name = "TiDB"` 这个表达式,如果为真,则向上返回这一行,否则丢弃这一行数据。 -4. 计算 `Count(*)`:对符合要求的每一行,累计到 `Count(*)` 的结果上面。 - -**整个流程示意图如下:** - -![naive sql flow](/media/tidb-computing-native-sql-flow.jpeg) - -这个方案是直观且可行的,但是在分布式数据库的场景下有一些显而易见的问题: - -- 在扫描数据的时候,每一行都要通过 KV 操作从 TiKV 中读取出来,至少有一次 RPC 开销,如果需要扫描的数据很多,那么这个开销会非常大。 -- 并不是所有的行都满足过滤条件 `name = "TiDB"`,如果不满足条件,其实可以不读取出来。 -- 符合要求的行的值并没有什么意义,实际上这里只需要有几行数据这个信息就行。 - -### 分布式 SQL 运算 - -为了解决上述问题,计算应该需要尽量靠近存储节点,以避免大量的 RPC 调用。首先,SQL 中的谓词条件 `name = "TiDB"` 应被下推到存储节点进行计算,这样只需要返回有效的行,避免无意义的网络传输。然后,聚合函数 `Count(*)` 也可以被下推到存储节点,进行预聚合,每个节点只需要返回一个 `Count(*)` 的结果即可,再由 SQL 层将各个节点返回的 `Count(*)` 的结果累加求和。 - -以下是数据逐层返回的示意图: - -![dist sql flow](/media/tidb-computing-dist-sql-flow.png) - -### SQL 层架构 - -通过上面的例子,希望大家对 SQL 语句的处理有一个基本的了解。实际上 TiDB 的 SQL 层要复杂得多,模块以及层次非常多,下图列出了重要的模块以及调用关系: - -![tidb sql layer](/media/tidb-computing-tidb-sql-layer.png) - -用户的 SQL 请求会直接或者通过 `Load Balancer` 发送到 TiDB Server,TiDB Server 会解析 `MySQL Protocol Packet`,获取请求内容,对 SQL 进行语法解析和语义分析,制定和优化查询计划,执行查询计划并获取和处理数据。数据全部存储在 TiKV 集群中,所以在这个过程中 TiDB Server 需要和 TiKV 交互,获取数据。最后 TiDB Server 需要将查询结果返回给用户。 From 7248b0cd57c6b61babc1a4055e599451086823f7 Mon Sep 17 00:00:00 2001 From: TomShawn <41534398+TomShawn@users.noreply.github.com> Date: Tue, 7 Jul 2020 16:22:06 +0800 Subject: [PATCH 07/56] Update tidb-computing.md --- tidb-computing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/tidb-computing.md b/tidb-computing.md index 0a526096ec2d1..ca4bc21a43f8b 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -2,6 +2,7 @@ title: TiDB database computation summary: Understand the computation layer of the TiDB database. category: introduction +aliases: ['/docs/dev/tidb-computing/'] --- # Computation of TiDB database From 00dd7ec8c1fdd396ab6a7760861aec258e810a43 Mon Sep 17 00:00:00 2001 From: TomShawn <41534398+TomShawn@users.noreply.github.com> Date: Wed, 8 Jul 2020 16:23:19 +0800 Subject: [PATCH 08/56] Update tidb-computing.md --- tidb-computing.md | 1 - 1 file changed, 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index ca4bc21a43f8b..0a526096ec2d1 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -2,7 +2,6 @@ title: TiDB database computation summary: Understand the computation layer of the TiDB database. category: introduction -aliases: ['/docs/dev/tidb-computing/'] --- # Computation of TiDB database From c7530c721064a9c0164594a72e1fc2248f92384b Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:39:10 +0800 Subject: [PATCH 09/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 0a526096ec2d1..8f25c492351cd 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -1,6 +1,6 @@ --- title: TiDB database computation -summary: Understand the computation layer of the TiDB database. +summary: Understand the computating layer of the TiDB database. category: introduction --- From 2677941990ca55356d2070991a41c17b65f09cb8 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:39:29 +0800 Subject: [PATCH 10/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 8f25c492351cd..6bbdc4141800f 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -6,7 +6,7 @@ category: introduction # Computation of TiDB database -Based on the distributed storage capability provided by TiKV, TiDB builds the computing engine that combines superior transaction processing with good data analysis capabilities. This article starts with a data mapping algorithm to describe how TiDB maps data from database tables to TiKV's (Key, Value) key-value pairs, then a description of how TiDB manages meta-information, and finally a description of the main architecture of the TiDB SQL layer. +Based on the distributed storage capability provided by TiKV, TiDB builds the computing engine that combines great transactional processing capability with good data analysis capability. This document starts by introducing a data mapping algorithm that maps data from TiDB database tables to TiKV's (Key, Value) key-value pairs, then introduces how TiDB manages metadata, and finally illustrates the architecture of the TiDB SQL layer. For the computation layer dependent storage schemes, this article only introduces TiKV based row storage structures. For analytic services, TiDB introduces a column storage scheme as a TiKV extension - [TiFlash](/tiflash/tiflash-overview.md). From ccfcba2df7a49d796f5bc3490eac84c149c767d6 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:39:42 +0800 Subject: [PATCH 11/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 6bbdc4141800f..1546f50d8ef14 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -8,7 +8,7 @@ category: introduction Based on the distributed storage capability provided by TiKV, TiDB builds the computing engine that combines great transactional processing capability with good data analysis capability. This document starts by introducing a data mapping algorithm that maps data from TiDB database tables to TiKV's (Key, Value) key-value pairs, then introduces how TiDB manages metadata, and finally illustrates the architecture of the TiDB SQL layer. -For the computation layer dependent storage schemes, this article only introduces TiKV based row storage structures. For analytic services, TiDB introduces a column storage scheme as a TiKV extension - [TiFlash](/tiflash/tiflash-overview.md). +For the storage solution on which the computing layer is dependent, this document only introduces the row-based storage structure of TiKV. For OLAP services, TiDB introduces a column-based storage solution [TiFlash](/tiflash/tiflash-overview.md) as a TiKV extension. ## Mapping of table data to Key-Value From cdee8790af9f331ff451a76818fa26d2bda72c57 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:39:53 +0800 Subject: [PATCH 12/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 1546f50d8ef14..b7dce7b87cee0 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -10,7 +10,7 @@ Based on the distributed storage capability provided by TiKV, TiDB builds the co For the storage solution on which the computing layer is dependent, this document only introduces the row-based storage structure of TiKV. For OLAP services, TiDB introduces a column-based storage solution [TiFlash](/tiflash/tiflash-overview.md) as a TiKV extension. -## Mapping of table data to Key-Value +## Mapping table data to Key-Value This section describes the scheme for mapping data to (Key, Value) key-value pairs in TiDB. Data here consists of the following two main aspects: From d198973abf9f1e8c6fbc1c393967a8533e08e2d4 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:40:05 +0800 Subject: [PATCH 13/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index b7dce7b87cee0..617414c7a80ce 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -12,7 +12,7 @@ For the storage solution on which the computing layer is dependent, this documen ## Mapping table data to Key-Value -This section describes the scheme for mapping data to (Key, Value) key-value pairs in TiDB. Data here consists of the following two main aspects: +This section describes the scheme for mapping data to (Key, Value) key-value pairs in TiDB. Data to be mapped here consists of the following two types: - Data for each row in the table, hereinafter referred to as table data. - Data for all indexes in the table, hereinafter referred to as index data. From 49c19bdd3a4489f9b402b705b43557ce0d7e0dd7 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:40:23 +0800 Subject: [PATCH 14/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 617414c7a80ce..e046e82626c13 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -14,7 +14,7 @@ For the storage solution on which the computing layer is dependent, this documen This section describes the scheme for mapping data to (Key, Value) key-value pairs in TiDB. Data to be mapped here consists of the following two types: -- Data for each row in the table, hereinafter referred to as table data. +- Data of each row in the table, hereinafter referred to as table data. - Data for all indexes in the table, hereinafter referred to as index data. ### Mapping of table data to Key-Value From 81edc4bf82eab2c830c7c9336d5054038c9200b7 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:40:47 +0800 Subject: [PATCH 15/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index e046e82626c13..bc85582154d26 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -15,7 +15,7 @@ For the storage solution on which the computing layer is dependent, this documen This section describes the scheme for mapping data to (Key, Value) key-value pairs in TiDB. Data to be mapped here consists of the following two types: - Data of each row in the table, hereinafter referred to as table data. -- Data for all indexes in the table, hereinafter referred to as index data. +- Data of all indexes in the table, hereinafter referred to as index data. ### Mapping of table data to Key-Value From 2ffbf2067dabebd34d6bccfd7481fecad651ae28 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:41:15 +0800 Subject: [PATCH 16/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index bc85582154d26..02d10cde57260 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -108,7 +108,7 @@ The above example shows the mapping rule from a relational model to a Key-Value ## Meta-information management -Each `Database` and `Table` in TiDB has meta information, aka its definition and various attributes. This information also needs to be persistent, and TiDB stores this information in TiKV as well. +Each database and table in TiDB has metadata that indicates its definition and various attributes. This information also needs to be persisted, and TiDB stores this information in TiKV as well. Each `Database` / `Table` is assigned an unique ID. As the unique identifier, when encoded as Key-Value, this ID is encoded in the Key with the `m_` prefix. This constructs a Key, and stores the serialized meta information in Value. From 9973287e29a014fc64f535552759211e894a126e Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:41:34 +0800 Subject: [PATCH 17/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 02d10cde57260..a523473698a64 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -19,7 +19,7 @@ This section describes the scheme for mapping data to (Key, Value) key-value pai ### Mapping of table data to Key-Value -In a relational database, a table may have many columns. To map the data from each column in a row to a (Key, Value) key-value pair, you need to consider how to construct the Key. First of all, OLTP scenarios have a large number of operations such as adding, deleting, changing and searching for single or multiple rows, which require the database to read a line of data quickly. Therefore, it is best to have a unique ID (either explicit or implicit) for the corresponding key to facilitate a quick location. Second, many OLAP queries require a full table scan. If you can encode the keys of all rows in a table into a range, the whole table can be efficiently scanned by range queries. +In a relational database, a table might have many columns. To map the data of each column in a row to a (Key, Value) key-value pair, you need to consider how to construct the Key. First of all, in OLTP scenarios, there are many operations such as adding, deleting, changing, and searching for data on a single or multiple rows, which requires the database to read a row of data quickly. Therefore, each key should have a unique ID (either explicit or implicit) to make it quick to locate. Secondly, many OLAP queries require a full table scan. If you can encode the keys of all rows in a table into a range, the whole table can be efficiently scanned by range queries. Based on the above considerations, the mapping of table data to Key-Value in TiDB is designed as follows: From b8f47b8b06a2cc9f7a191bdad2405d52941b4824 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:41:59 +0800 Subject: [PATCH 18/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index a523473698a64..d4ed8a3a6d77d 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -24,7 +24,7 @@ In a relational database, a table might have many columns. To map the data of ea Based on the above considerations, the mapping of table data to Key-Value in TiDB is designed as follows: - To ensure that data from the same table is kept together for easy searching, TiDB assigns a table ID to each table denoted by `TableID`. Table ID is an integer that is unique throughout the cluster. -- TiDB assigns a row ID, represented by `RowID`, to each row of data in the table. The row ID is also an integer, unique within the table. For row ID, TiDB has made a small optimization, if a table has integer type primary key, TiDB will use primary key value as the row ID of this row of data. +- TiDB assigns a row ID, represented by `RowID`, to each row of data in the table. The row ID is also an integer, unique within the table. For row ID, TiDB has made a small optimization: if a table has an integer type primary key, TiDB uses the value of this primary key as the row ID. Each row of data is encoded as a (Key, Value) key-value pair according to the following rule: From 8d4a80917465b1aa803382d986daa1ded4d3bfe6 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:46:26 +0800 Subject: [PATCH 19/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index d4ed8a3a6d77d..35273e2bcbfb5 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -33,7 +33,7 @@ Key: tablePrefix{TableID}_recordPrefixSep{RowID} Value: [col1, col2, col3, col4] ``` -`tablePrefix` and `recordPrefixSep` are both special string constants used to distinguish other data in Key space. The exact value is given in the summary that follows. +`tablePrefix` and `recordPrefixSep` are both special string constants used to distinguish other data in Key space. The exact values of the string constants are introduced in [Summary of mapping relationships](#summary-of-mapping-relationships). ### Mapping of Indexed Data to Key-Value From 025bd4d1584818a646eaf51881dddcbc592d61fc Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:46:39 +0800 Subject: [PATCH 20/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 35273e2bcbfb5..17d1ef277f54e 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -110,7 +110,7 @@ The above example shows the mapping rule from a relational model to a Key-Value Each database and table in TiDB has metadata that indicates its definition and various attributes. This information also needs to be persisted, and TiDB stores this information in TiKV as well. -Each `Database` / `Table` is assigned an unique ID. As the unique identifier, when encoded as Key-Value, this ID is encoded in the Key with the `m_` prefix. This constructs a Key, and stores the serialized meta information in Value. +Each database or table is assigned a unique ID. As the unique identifier, when table data is encoded to Key-Value, this ID is encoded in the Key with the `m_` prefix. This constructs a key-value pair with the serialized metadata stored in it. Besides, TiDB also uses a dedicated (Key, Value) key pair to store the latest version number of the current all tables structure information. This key-value pair is global, and its version number is increased by 1 each time the state of the DDL operation changes. TiDB stores this key-value pair persistently in the PD Server with a key of "/tidb/ddl/global_schema_version", and Value is the version number value of int64 type. Refers to Google F1's Online Schema change algorithm, TiDB keeps a background thread constantly checking whether the version number of the table structure information stored in the PD Server changes, and ensuring to get the changes of version in a certain time. From 997bb3d8d11dcafa78560c7fcdb8b442ff94aaa9 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:46:51 +0800 Subject: [PATCH 21/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 17d1ef277f54e..fcb2ae3b7587c 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -112,7 +112,7 @@ Each database and table in TiDB has metadata that indicates its definition and v Each database or table is assigned a unique ID. As the unique identifier, when table data is encoded to Key-Value, this ID is encoded in the Key with the `m_` prefix. This constructs a key-value pair with the serialized metadata stored in it. -Besides, TiDB also uses a dedicated (Key, Value) key pair to store the latest version number of the current all tables structure information. This key-value pair is global, and its version number is increased by 1 each time the state of the DDL operation changes. TiDB stores this key-value pair persistently in the PD Server with a key of "/tidb/ddl/global_schema_version", and Value is the version number value of int64 type. Refers to Google F1's Online Schema change algorithm, TiDB keeps a background thread constantly checking whether the version number of the table structure information stored in the PD Server changes, and ensuring to get the changes of version in a certain time. +In addition, TiDB also uses a dedicated (Key, Value) key-value pair to store the latest version number of all table structure information. This key-value pair is global, and `its version number is increased by 1 each time the state of the DDL operation changes. TiDB stores this key-value pair persistently in the PD server with the key of `/tidb/ddl/global_schema_version`, and Value is the version number value of the `int64` type. Inspired by Google F1's Online Schema change algorithm, TiDB keeps a background thread that constantly checks whether the version number of the table structure information stored in the PD server changes. This thread also ensures that the changes of version can be obtained within a certain period of time. ## Introduction to the SQL layer From 4efd71fc5d5e6c7af6ec6838c86bf7bfc18b8fe4 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:47:02 +0800 Subject: [PATCH 22/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index fcb2ae3b7587c..f06f6cff435d7 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -1,5 +1,5 @@ --- -title: TiDB database computation +title: Computation of TiDB Database summary: Understand the computating layer of the TiDB database. category: introduction --- From b157b8b09171dff0f3bbdd882661fc8f00a83afe Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:47:13 +0800 Subject: [PATCH 23/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index f06f6cff435d7..cef1349f0e1fe 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -116,7 +116,7 @@ In addition, TiDB also uses a dedicated (Key, Value) key-value pair to store the ## Introduction to the SQL layer -TiDB's SQL layer, TiDB Server, is responsible for translating the SQL into Key-Value operation to the common distributed Key-Value storage layer TiKV, assembling the results returned by TiKV, and returning the query results to the client ultimately. +TiDB's SQL layer, TiDB Server, translates SQL statements into Key-Value operations, forwards the operations to TiKV, the distributed Key-Value storage layer, assembles the results returned by TiKV, and finally returns the query results to the client. The nodes at this layer are stateless, the nodes themselves do not store data, and the nodes are completely equal. From 36f51ec0539b37632c63cea9e954beb27e6f6a46 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:47:23 +0800 Subject: [PATCH 24/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index cef1349f0e1fe..ff234702f3fba 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -118,7 +118,7 @@ In addition, TiDB also uses a dedicated (Key, Value) key-value pair to store the TiDB's SQL layer, TiDB Server, translates SQL statements into Key-Value operations, forwards the operations to TiKV, the distributed Key-Value storage layer, assembles the results returned by TiKV, and finally returns the query results to the client. -The nodes at this layer are stateless, the nodes themselves do not store data, and the nodes are completely equal. +The nodes at this layer are stateless. These nodes themselves do not store data and are completely equivalent. ### SQL algorithm From e0a2c569f1a4bcc8d30e8afb937e843187cbba82 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:47:35 +0800 Subject: [PATCH 25/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index ff234702f3fba..84f083b955795 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -120,7 +120,7 @@ TiDB's SQL layer, TiDB Server, translates SQL statements into Key-Value operatio The nodes at this layer are stateless. These nodes themselves do not store data and are completely equivalent. -### SQL algorithm +### SQL computing The simplest solution is through the [mapping of table data to Key-Value](#mapping-of-table-data-to-key-value) as described in the previous section scheme, mapping SQL queries to KV queries, and then acquires the corresponding data through the KV interface and performs various computations. From e3aaee58dbe6163f12223cf6df26a4dacbe4026f Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:47:48 +0800 Subject: [PATCH 26/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 84f083b955795..bb54927d0a290 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -122,7 +122,7 @@ The nodes at this layer are stateless. These nodes themselves do not store data ### SQL computing -The simplest solution is through the [mapping of table data to Key-Value](#mapping-of-table-data-to-key-value) as described in the previous section scheme, mapping SQL queries to KV queries, and then acquires the corresponding data through the KV interface and performs various computations. +The simplest solution to SQL computing is the [mapping of table data to Key-Value](#mapping-of-table-data-to-key-value) as described in the previous section, which maps SQL queries to KV queries, acquires the corresponding data through the KV interface, and performs various computations. For example, `select count(*) from user where name = "TiDB"` such a SQL statement. It needs to read all the data in the table, then check if the `name` field is `TiDB`, and if so, returns this line. The process is as follows: From cba591aeb9ece5c521a6f62abdcf197a44a6f0d0 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:48:00 +0800 Subject: [PATCH 27/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index bb54927d0a290..13009e2594c71 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -124,7 +124,7 @@ The nodes at this layer are stateless. These nodes themselves do not store data The simplest solution to SQL computing is the [mapping of table data to Key-Value](#mapping-of-table-data-to-key-value) as described in the previous section, which maps SQL queries to KV queries, acquires the corresponding data through the KV interface, and performs various computations. -For example, `select count(*) from user where name = "TiDB"` such a SQL statement. It needs to read all the data in the table, then check if the `name` field is `TiDB`, and if so, returns this line. The process is as follows: +For example, to execute the `select count(*) from user where name = "TiDB"` SQL statement, TiDB needs to read all data in the table, then checks whether the `name` field is `TiDB`, and if so, returns this row. The process is as follows: 1. construct the Key Range: all `RowID` in a table are in `[0, MaxInt64)` range. According to the row data `Key` encoding rule, using `0` and `MaxInt64` can construct a `[StartKey, EndKey)` range that is left-included, right-excluded. 2. scan Key Range: read the data in TiKV according to the key range constructed above. From 7166994199e8fa8aac4cfa4011f3cf8b35324445 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:48:11 +0800 Subject: [PATCH 28/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 13009e2594c71..aecd5bad3d926 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -143,7 +143,7 @@ This solution is intuitive and feasible, but has some obvious problems in a dist ### Distributed SQL operations -To solve the above problem, the computation should need to be as close to the storage node as possible to avoid a large number of RPC calls. First of all, the SQL predicate condition `name = "TiDB"` should be pushed down to the storage node for computation, so that only valid rows are returned, avoiding meaningless network transfers. Then, the aggregation function `Count(*)` can also be pushed down to the storage nodes for pre-aggregation, and each node only has to return a result of `Count(*)`, and the SQL layer will sum up the `Count(*)` result returned by each node. +To solve the problems above, the computation should be as close to the storage node as possible to avoid a large number of RPC callings. First of all, the SQL predicate condition `name = "TiDB"` should be pushed down to the storage node for computation, so that only valid rows are returned, which avoids meaningless network transfers. Then, the aggregation function `Count(*)` can also be pushed down to the storage nodes for pre-aggregation, and each node only has to return a result of `Count(*)`. The SQL layer will sum up the `Count(*)` results returned by each node. The following is a schematic representation of the data returned layer by layer. From 2d0265c29481ca0e4061779cdd19ddf82336bb99 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:48:22 +0800 Subject: [PATCH 29/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index aecd5bad3d926..376dff8bb410c 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -145,7 +145,7 @@ This solution is intuitive and feasible, but has some obvious problems in a dist To solve the problems above, the computation should be as close to the storage node as possible to avoid a large number of RPC callings. First of all, the SQL predicate condition `name = "TiDB"` should be pushed down to the storage node for computation, so that only valid rows are returned, which avoids meaningless network transfers. Then, the aggregation function `Count(*)` can also be pushed down to the storage nodes for pre-aggregation, and each node only has to return a result of `Count(*)`. The SQL layer will sum up the `Count(*)` results returned by each node. -The following is a schematic representation of the data returned layer by layer. +The following image shows how data returns layer by layer: ![dist sql flow](/media/tidb-computing-dist-sql-flow.png) From 14cf3a17a071159d507aa1abc1936953b6d16811 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:48:32 +0800 Subject: [PATCH 30/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 376dff8bb410c..d1c652353fde4 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -149,7 +149,7 @@ The following image shows how data returns layer by layer: ![dist sql flow](/media/tidb-computing-dist-sql-flow.png) -### SQL layer architecture +### Architecture of the SQL layer With the above example, I hope you have a basic understanding of how SQL statements are handled. In fact, TiDB's SQL layer is much more complex, with many modules and layers. The following diagram lists the important modules and calling relationships: From cf0e8cae39a4656bf18b5b2a18dda911fb79229d Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:48:43 +0800 Subject: [PATCH 31/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index d1c652353fde4..51fc553c8a011 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -151,7 +151,7 @@ The following image shows how data returns layer by layer: ### Architecture of the SQL layer -With the above example, I hope you have a basic understanding of how SQL statements are handled. In fact, TiDB's SQL layer is much more complex, with many modules and layers. The following diagram lists the important modules and calling relationships: +The previous sections introduce some functions of the SQL layer and I hope you have a basic understanding of how SQL statements are handled. In fact, TiDB's SQL layer is much more complicated, with many modules and layers. The following diagram lists the important modules and calling relationships: ![tidb sql layer](/media/tidb-computing-tidb-sql-layer.png) From 110ae19c435bec1d60bf46a2a16d0e091cb78700 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:48:54 +0800 Subject: [PATCH 32/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 51fc553c8a011..0fadc2a16d733 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -155,4 +155,4 @@ The previous sections introduce some functions of the SQL layer and I hope you h ![tidb sql layer](/media/tidb-computing-tidb-sql-layer.png) -The user's SQL request is sent to TiDB Server either directly or via `Load Balancer`. TiDB Server will parse `MySQL Protocol Packet`, getting the content of requests, parsing syntax and semantic analysis of SQL, developing and optimizing query plans, executing a query plan , fetching and processing the data. The data is all stored in the TiKV cluster, so in this process, TiDB Server needs to interact with the TiKV and gets the data. Finally, TiDB Server needs to return the query results to the user. +The user's SQL request is sent to TiDB Server either directly or via `Load Balancer`. TiDB Server will parse `MySQL Protocol Packet`, get the content of requests, parse the SQL request syntactically and semantically, develop and optimize query plans, execute a query plan, get and process the data. All data is stored in the TiKV cluster, so in this process, TiDB Server needs to interact with TiKV and gets the data. Finally, TiDB Server needs to return the query results to the user. From 8d51c559a4c6c1985836693ce8ec5ba5266701da Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:49:14 +0800 Subject: [PATCH 33/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 0fadc2a16d733..aa5552c22cbf8 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -126,7 +126,7 @@ The simplest solution to SQL computing is the [mapping of table data to Key-Valu For example, to execute the `select count(*) from user where name = "TiDB"` SQL statement, TiDB needs to read all data in the table, then checks whether the `name` field is `TiDB`, and if so, returns this row. The process is as follows: -1. construct the Key Range: all `RowID` in a table are in `[0, MaxInt64)` range. According to the row data `Key` encoding rule, using `0` and `MaxInt64` can construct a `[StartKey, EndKey)` range that is left-included, right-excluded. +1. Construct the Key Range: all `RowID` in a table are in `[0, MaxInt64)` range. According to the row data `Key` encoding rule, using `0` and `MaxInt64` can construct a `[StartKey, EndKey)` range that is left-inclusive and right-exclusive. 2. scan Key Range: read the data in TiKV according to the key range constructed above. 3. filter data: for each row of data read, calculate `name = "TiDB"` expression. Returns up this line if true, otherwise discards this line of data. 4. calculate `Count(*)`: for each line that meets the requirements, accumulate to the result of `Count(*)`. From 0fd26a65351ed96015ce0cf83746746b25089d68 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:49:29 +0800 Subject: [PATCH 34/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index aa5552c22cbf8..ce1bfece66445 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -127,7 +127,7 @@ The simplest solution to SQL computing is the [mapping of table data to Key-Valu For example, to execute the `select count(*) from user where name = "TiDB"` SQL statement, TiDB needs to read all data in the table, then checks whether the `name` field is `TiDB`, and if so, returns this row. The process is as follows: 1. Construct the Key Range: all `RowID` in a table are in `[0, MaxInt64)` range. According to the row data `Key` encoding rule, using `0` and `MaxInt64` can construct a `[StartKey, EndKey)` range that is left-inclusive and right-exclusive. -2. scan Key Range: read the data in TiKV according to the key range constructed above. +2. Scan Key Range: read the data in TiKV according to the key range constructed above. 3. filter data: for each row of data read, calculate `name = "TiDB"` expression. Returns up this line if true, otherwise discards this line of data. 4. calculate `Count(*)`: for each line that meets the requirements, accumulate to the result of `Count(*)`. From 2d3b7ef4e0539569748081af166e2f841ef047dd Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:49:48 +0800 Subject: [PATCH 35/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index ce1bfece66445..cecf32908d65c 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -128,7 +128,7 @@ For example, to execute the `select count(*) from user where name = "TiDB"` SQL 1. Construct the Key Range: all `RowID` in a table are in `[0, MaxInt64)` range. According to the row data `Key` encoding rule, using `0` and `MaxInt64` can construct a `[StartKey, EndKey)` range that is left-inclusive and right-exclusive. 2. Scan Key Range: read the data in TiKV according to the key range constructed above. -3. filter data: for each row of data read, calculate `name = "TiDB"` expression. Returns up this line if true, otherwise discards this line of data. +3. Filter data: for each row of data read, calculate the `name = "TiDB"` expression. If the result is true, return to this row. If not, skip this row. 4. calculate `Count(*)`: for each line that meets the requirements, accumulate to the result of `Count(*)`. **The entire process is illustrated as follows:** From d53875ec70728f3c84a5401a8f61f468cf579b8a Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:50:04 +0800 Subject: [PATCH 36/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index cecf32908d65c..fbe3c71e109f8 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -129,7 +129,7 @@ For example, to execute the `select count(*) from user where name = "TiDB"` SQL 1. Construct the Key Range: all `RowID` in a table are in `[0, MaxInt64)` range. According to the row data `Key` encoding rule, using `0` and `MaxInt64` can construct a `[StartKey, EndKey)` range that is left-inclusive and right-exclusive. 2. Scan Key Range: read the data in TiKV according to the key range constructed above. 3. Filter data: for each row of data read, calculate the `name = "TiDB"` expression. If the result is true, return to this row. If not, skip this row. -4. calculate `Count(*)`: for each line that meets the requirements, accumulate to the result of `Count(*)`. +4. Calculate `Count(*)`: for each row that meets the requirements, add up to the result of `Count(*)`. **The entire process is illustrated as follows:** From a23e75bcb4312d5897bc951fce81ad6f97ce93d5 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:50:18 +0800 Subject: [PATCH 37/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index fbe3c71e109f8..2e6ca13a22bcf 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -137,7 +137,7 @@ For example, to execute the `select count(*) from user where name = "TiDB"` SQL This solution is intuitive and feasible, but has some obvious problems in a distributed database scenario. -- As the data is being scanned, each row is read out of TiKV via a KV operation at least once RPC overhead, which can be very high if there is a lot of data to scan. +- As the data is being scanned, each row is read out of TiKV via a KV operation with at least one RPC overhead, which can be very high if there is a large amount of data to be scanned. - Not all rows meet the filter criteria `name = "TiDB"`. If the conditions are not met, they are unnessary to be read out. - The value of the rows that meet the requirements doesn't mean anything, in fact, all needed here is the information of how many rows of data. From 09dc3738b2ee6e0fa4b73bcbdcf541b2b942a6a3 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:50:32 +0800 Subject: [PATCH 38/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 2e6ca13a22bcf..7c8e5081c7664 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -138,7 +138,7 @@ For example, to execute the `select count(*) from user where name = "TiDB"` SQL This solution is intuitive and feasible, but has some obvious problems in a distributed database scenario. - As the data is being scanned, each row is read out of TiKV via a KV operation with at least one RPC overhead, which can be very high if there is a large amount of data to be scanned. -- Not all rows meet the filter criteria `name = "TiDB"`. If the conditions are not met, they are unnessary to be read out. +- It is not applicable to all rows. Data that doesn't meet the conditions doesn't need to be read. - The value of the rows that meet the requirements doesn't mean anything, in fact, all needed here is the information of how many rows of data. ### Distributed SQL operations From d1b5cf690223ad97a3e6b47deb18c736cd5e620b Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:50:46 +0800 Subject: [PATCH 39/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 7c8e5081c7664..ed272e12052d6 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -139,7 +139,7 @@ This solution is intuitive and feasible, but has some obvious problems in a dist - As the data is being scanned, each row is read out of TiKV via a KV operation with at least one RPC overhead, which can be very high if there is a large amount of data to be scanned. - It is not applicable to all rows. Data that doesn't meet the conditions doesn't need to be read. -- The value of the rows that meet the requirements doesn't mean anything, in fact, all needed here is the information of how many rows of data. +- The value of the rows that meet the conditions is meaningless. In fact, all needed here is just the number of rows. ### Distributed SQL operations From cf95af0a0ae93036d1fec2f4365ef06c6c752984 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:51:10 +0800 Subject: [PATCH 40/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index ed272e12052d6..2a6d8dc10f0c5 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -4,7 +4,7 @@ summary: Understand the computating layer of the TiDB database. category: introduction --- -# Computation of TiDB database +# Computation of TiDB Database Based on the distributed storage capability provided by TiKV, TiDB builds the computing engine that combines great transactional processing capability with good data analysis capability. This document starts by introducing a data mapping algorithm that maps data from TiDB database tables to TiKV's (Key, Value) key-value pairs, then introduces how TiDB manages metadata, and finally illustrates the architecture of the TiDB SQL layer. From 60151ee7610d57cd707ad64a14e9026080248b9c Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:51:26 +0800 Subject: [PATCH 41/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 2a6d8dc10f0c5..8451b0fada70b 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -37,7 +37,7 @@ Value: [col1, col2, col3, col4] ### Mapping of Indexed Data to Key-Value -TiDB supports both primary keys and secondary indexes (both unique and non-unique indexes). Similar to the table data mapping scheme, TiDB assigns an index ID to each index in the table indicated by `IndexID`. +TiDB supports both primary keys and secondary indexes (both unique and non-unique indexes). Similar to the table data mapping scheme, TiDB assigns an index ID to each index of the table represented by `IndexID`. For primary keys and unique indexes, it is needed to quickly locate the corresponding RowID based on the key value, so it is encoded as follows (Key, Value) Key-value pairs. From 2ad9b247d2ab5b51ceaa94ca474310e16a64f492 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:51:42 +0800 Subject: [PATCH 42/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 8451b0fada70b..a3d18c7d0c6e4 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -39,7 +39,7 @@ Value: [col1, col2, col3, col4] TiDB supports both primary keys and secondary indexes (both unique and non-unique indexes). Similar to the table data mapping scheme, TiDB assigns an index ID to each index of the table represented by `IndexID`. -For primary keys and unique indexes, it is needed to quickly locate the corresponding RowID based on the key value, so it is encoded as follows (Key, Value) Key-value pairs. +For primary keys and unique indexes, it is needed to quickly locate the corresponding `RowID` based on the key-value pair, so such a key-value pair is encoded as follows. ``` Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue From 8fdd93f191ba5690b8a1358734230d429edcb5f5 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:51:56 +0800 Subject: [PATCH 43/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index a3d18c7d0c6e4..031f9516bd64f 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -46,7 +46,7 @@ Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue Value: RowID ``` -For ordinary secondary indexes that do not need to satisfy the uniqueness constraint, a single key may correspond to multiple rows. It needs to query corresponding RowID according to the range of keys. Therefore, it is encoded as a (Key, Value) key-value pair according to the following rule: +For ordinary secondary indexes that do not need to satisfy the uniqueness constraint, a single key might correspond to multiple rows. It needs to query corresponding `RowID` according to the range of keys. Therefore, the key-value pair must be encoded according to the following rule: ``` Key: tablePrefix{TableID}_indexPrefixSep{IndexID}_indexedColumnsValue_{RowID} From 4fdab5dd3f7251cad284ff08b96efc6afd97cace Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:52:10 +0800 Subject: [PATCH 44/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 031f9516bd64f..a400a7c742bf9 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -55,7 +55,7 @@ Value: null ### Summary of mapping relationships -`tablePrefix`, `recordPrefixSep`, and `indexPrefixSep` in all of the above encoding rules are string constants that are used to distinguish between other data in Key space, defined as follows: +`tablePrefix`, `recordPrefixSep`, and `indexPrefixSep` in all of the above encoding rules are string constants that are used to distinguish a KV from other data in the Key space, which are defined as follows: ``` tablePrefix = []byte{'t'} From 7bb923e6abe957e7585ecceb7d764a72413c211c Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:52:26 +0800 Subject: [PATCH 45/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index a400a7c742bf9..5f730c2244805 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -63,7 +63,7 @@ recordPrefixSep = []byte{'r'} indexPrefixSep = []byte{'i'} ``` -Also note that in the above schemes, regardless of table data or index data key encoding scheme, all rows in a table have the same key prefix, and all the data of an index also has the same prefix. Data with the same prefixes are thus arranged together in TiKV's Key space. Therefore, by carefully designing the encoding scheme of the suffix part to ensure that the pre and post-encoding comparisons remain the same, the table data or index data can be stored in the TiKV in an ordered manner. With this encoding, all rows of data in a table are arranged orderly by `RowID` in the TiKV's Key space, and the data for a particular index will also be arranged sequentially in the Key space according to the specific value of the index data (the `indexedColumnsValue`). +Also note that in the above mapping schemes, regardless of table data or index data key encoding scheme, all rows in a table have the same key prefix, and all the data of an index also has the same prefix. Data with the same prefixes are thus arranged together in TiKV's key space. Therefore, by carefully designing the encoding scheme of the suffix part to ensure that the pre-encoding and post-encoding comparisons remain the same, the table data or index data can be stored in the TiKV in an ordered manner. Using this encoding scheme, all row data in a table is arranged orderly by `RowID` in the TiKV's key space, and the data of a particular index is also arranged sequentially in the key space according to the specific value of the index data (`indexedColumnsValue`). ### Example of Key-Value mapping relationship From e7841125dfddb9065ba16cef9242bdd2b9af6817 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:52:41 +0800 Subject: [PATCH 46/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 5f730c2244805..e02ee713f4065 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -67,7 +67,7 @@ Also note that in the above mapping schemes, regardless of table data or index d ### Example of Key-Value mapping relationship -Finally, a simple example is used to understand the Key-Value mapping relationship of TiDB. Suppose the following table exists in TiDB. +This section shows a simple example for you to understand the Key-Value mapping relationship of TiDB. Suppose the following table exists in TiDB. ```sql CREATE TABLE User { From 076444356ebb3b71be722fbbe70b1a23a9674464 Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:52:55 +0800 Subject: [PATCH 47/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index e02ee713f4065..cb05277514476 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -88,7 +88,7 @@ Suppose there are 3 rows of data in the table. 3, "PD", "Manager", 30 ``` -First, each row of data is mapped to a (Key, Value) key-value pair, and the table has an `int` type primary key, so the value of `RowID` is the value of this primary key. Suppose the table has `TableID` of 10, then its table data stored on TiKV is: +Each row of data is mapped to a (Key, Value) key-value pair, and the table has an `int` type primary key, so the value of `RowID` is the value of this primary key. Suppose the table's `TableID` is 10, and then its table data stored on TiKV is: ``` t10_r1 --> ["TiDB", "SQL Layer", 10] From 59e9b560b1af5df7b66b704b43e9f35993987b9b Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:53:11 +0800 Subject: [PATCH 48/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index cb05277514476..28511f65ddefa 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -96,7 +96,7 @@ t10_r2 --> ["TiKV", "KV Engine", 20] t10_r3 --> ["PD", " Manager", 30] ``` -In addition to the primary key, the table has a non-unique ordinary secondary index, `idxAge`. Suppose the `IndexID` is 1, then its index data stored on TiKV is: +In addition to the primary key, the table has a non-unique ordinary secondary index, `idxAge`. Suppose the `IndexID` is `1`, and then its index data stored on TiKV is: ``` t10_i1_10_1 --> null From 2ce0c47bba2f7b08e47c74a11f5865fce48d665b Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:53:31 +0800 Subject: [PATCH 49/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 28511f65ddefa..a34593fb47095 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -104,7 +104,7 @@ t10_i1_20_2 --> null t10_i1_30_3 --> null ``` -The above example shows the mapping rule from a relational model to a Key-Value model in TiDB, and the consideration behind the selection of this scheme. +The above example shows the mapping rule from a relational model to a Key-Value model in TiDB, and the consideration behind this mapping scheme. ## Meta-information management From 3a09bfdbda15bb7d66b594d041d76980d1ebc66b Mon Sep 17 00:00:00 2001 From: Sparkle <1284531+baurine@users.noreply.github.com> Date: Mon, 13 Jul 2020 09:53:47 +0800 Subject: [PATCH 50/56] Update tidb-computing.md Co-authored-by: TomShawn <41534398+TomShawn@users.noreply.github.com> --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index a34593fb47095..7d42e9f6d5fdb 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -106,7 +106,7 @@ t10_i1_30_3 --> null The above example shows the mapping rule from a relational model to a Key-Value model in TiDB, and the consideration behind this mapping scheme. -## Meta-information management +## Metadata management Each database and table in TiDB has metadata that indicates its definition and various attributes. This information also needs to be persisted, and TiDB stores this information in TiKV as well. From dc08906126ebf82df05461cc74e23e072d762bb4 Mon Sep 17 00:00:00 2001 From: TomShawn <41534398+TomShawn@users.noreply.github.com> Date: Mon, 13 Jul 2020 10:45:10 +0800 Subject: [PATCH 51/56] Update tidb-computing.md --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 7d42e9f6d5fdb..7116fc8f023ee 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -112,7 +112,7 @@ Each database and table in TiDB has metadata that indicates its definition and v Each database or table is assigned a unique ID. As the unique identifier, when table data is encoded to Key-Value, this ID is encoded in the Key with the `m_` prefix. This constructs a key-value pair with the serialized metadata stored in it. -In addition, TiDB also uses a dedicated (Key, Value) key-value pair to store the latest version number of all table structure information. This key-value pair is global, and `its version number is increased by 1 each time the state of the DDL operation changes. TiDB stores this key-value pair persistently in the PD server with the key of `/tidb/ddl/global_schema_version`, and Value is the version number value of the `int64` type. Inspired by Google F1's Online Schema change algorithm, TiDB keeps a background thread that constantly checks whether the version number of the table structure information stored in the PD server changes. This thread also ensures that the changes of version can be obtained within a certain period of time. +In addition, TiDB also uses a dedicated (Key, Value) key-value pair to store the latest version number of all table structure information. This key-value pair is global, and its version number is increased by 1 each time the state of the DDL operation changes. TiDB stores this key-value pair persistently in the PD server with the key of `/tidb/ddl/global_schema_version`, and Value is the version number value of the `int64` type. Inspired by Google F1's Online Schema change algorithm, TiDB keeps a background thread that constantly checks whether the version number of the table structure information stored in the PD server changes. This thread also ensures that the changes of version can be obtained within a certain period of time. ## Introduction to the SQL layer From 3be0372e8d964504f4f7877620731f846e9dc872 Mon Sep 17 00:00:00 2001 From: TomShawn <41534398+TomShawn@users.noreply.github.com> Date: Mon, 13 Jul 2020 13:45:20 +0800 Subject: [PATCH 52/56] minor typo fixing --- tidb-computing.md | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/tidb-computing.md b/tidb-computing.md index 7116fc8f023ee..f6b8d244bb979 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -1,29 +1,28 @@ --- title: Computation of TiDB Database -summary: Understand the computating layer of the TiDB database. -category: introduction +summary: Understand the computing layer of the TiDB database. --- # Computation of TiDB Database -Based on the distributed storage capability provided by TiKV, TiDB builds the computing engine that combines great transactional processing capability with good data analysis capability. This document starts by introducing a data mapping algorithm that maps data from TiDB database tables to TiKV's (Key, Value) key-value pairs, then introduces how TiDB manages metadata, and finally illustrates the architecture of the TiDB SQL layer. +Based on the distributed storage provided by TiKV, TiDB builds the computing engine that combines great capability of transactional processing with that of data analysis. This document starts by introducing a data mapping algorithm that maps data from TiDB database tables to (Key, Value) key-value pairs in TiKV, then introduces how TiDB manages metadata, and finally illustrates the architecture of the TiDB SQL layer. For the storage solution on which the computing layer is dependent, this document only introduces the row-based storage structure of TiKV. For OLAP services, TiDB introduces a column-based storage solution [TiFlash](/tiflash/tiflash-overview.md) as a TiKV extension. ## Mapping table data to Key-Value -This section describes the scheme for mapping data to (Key, Value) key-value pairs in TiDB. Data to be mapped here consists of the following two types: +This section describes the scheme for mapping data to (Key, Value) key-value pairs in TiDB. Data to be mapped here includes the following two types: - Data of each row in the table, hereinafter referred to as table data. - Data of all indexes in the table, hereinafter referred to as index data. ### Mapping of table data to Key-Value -In a relational database, a table might have many columns. To map the data of each column in a row to a (Key, Value) key-value pair, you need to consider how to construct the Key. First of all, in OLTP scenarios, there are many operations such as adding, deleting, changing, and searching for data on a single or multiple rows, which requires the database to read a row of data quickly. Therefore, each key should have a unique ID (either explicit or implicit) to make it quick to locate. Secondly, many OLAP queries require a full table scan. If you can encode the keys of all rows in a table into a range, the whole table can be efficiently scanned by range queries. +In a relational database, a table might have many columns. To map the data of each column in a row to a (Key, Value) key-value pair, you need to consider how to construct the Key. First of all, in OLTP scenarios, there are many operations such as adding, deleting, changing, and searching for data on a single or multiple rows, which needs the database to read a row of data quickly. Therefore, each key should have a unique ID (either explicit or implicit) to make it quick to locate. Then, many OLAP queries require a full table scan. If you can encode the keys of all rows in a table into a range, the whole table can be efficiently scanned by range queries. -Based on the above considerations, the mapping of table data to Key-Value in TiDB is designed as follows: +Based on the considerations above, the mapping of table data to Key-Value in TiDB is designed as follows: -- To ensure that data from the same table is kept together for easy searching, TiDB assigns a table ID to each table denoted by `TableID`. Table ID is an integer that is unique throughout the cluster. +- To ensure that data from the same table is kept together for easy searching, TiDB assigns a table ID to each table represented by `TableID`. Table ID is an integer that is unique throughout the cluster. - TiDB assigns a row ID, represented by `RowID`, to each row of data in the table. The row ID is also an integer, unique within the table. For row ID, TiDB has made a small optimization: if a table has an integer type primary key, TiDB uses the value of this primary key as the row ID. Each row of data is encoded as a (Key, Value) key-value pair according to the following rule: @@ -35,7 +34,7 @@ Value: [col1, col2, col3, col4] `tablePrefix` and `recordPrefixSep` are both special string constants used to distinguish other data in Key space. The exact values of the string constants are introduced in [Summary of mapping relationships](#summary-of-mapping-relationships). -### Mapping of Indexed Data to Key-Value +### Mapping of indexed data to Key-Value TiDB supports both primary keys and secondary indexes (both unique and non-unique indexes). Similar to the table data mapping scheme, TiDB assigns an index ID to each index of the table represented by `IndexID`. @@ -63,7 +62,7 @@ recordPrefixSep = []byte{'r'} indexPrefixSep = []byte{'i'} ``` -Also note that in the above mapping schemes, regardless of table data or index data key encoding scheme, all rows in a table have the same key prefix, and all the data of an index also has the same prefix. Data with the same prefixes are thus arranged together in TiKV's key space. Therefore, by carefully designing the encoding scheme of the suffix part to ensure that the pre-encoding and post-encoding comparisons remain the same, the table data or index data can be stored in the TiKV in an ordered manner. Using this encoding scheme, all row data in a table is arranged orderly by `RowID` in the TiKV's key space, and the data of a particular index is also arranged sequentially in the key space according to the specific value of the index data (`indexedColumnsValue`). +Also note that in the above encoding schemes, no matter table data or index data key encoding scheme, all rows in a table have the same key prefix, and all data of an index also has the same prefix. Data with the same prefixes are thus arranged together in TiKV's key space. Therefore, by carefully designing the encoding scheme of the suffix part to ensure that the pre-encoding and post-encoding comparisons remain the same, the table data or index data can be stored in TiKV in an ordered manner. Using this encoding scheme, all row data in a table is arranged orderly by `RowID` in the TiKV's key space, and the data of a particular index is also arranged sequentially in the key space according to the specific value of the index data (`indexedColumnsValue`). ### Example of Key-Value mapping relationship @@ -88,7 +87,7 @@ Suppose there are 3 rows of data in the table. 3, "PD", "Manager", 30 ``` -Each row of data is mapped to a (Key, Value) key-value pair, and the table has an `int` type primary key, so the value of `RowID` is the value of this primary key. Suppose the table's `TableID` is 10, and then its table data stored on TiKV is: +Each row of data is mapped to a (Key, Value) key-value pair, and the table has an `int` type primary key, so the value of `RowID` is the value of this primary key. Suppose the table's `TableID` is `10`, and then its table data stored on TiKV is: ``` t10_r1 --> ["TiDB", "SQL Layer", 10] @@ -112,9 +111,9 @@ Each database and table in TiDB has metadata that indicates its definition and v Each database or table is assigned a unique ID. As the unique identifier, when table data is encoded to Key-Value, this ID is encoded in the Key with the `m_` prefix. This constructs a key-value pair with the serialized metadata stored in it. -In addition, TiDB also uses a dedicated (Key, Value) key-value pair to store the latest version number of all table structure information. This key-value pair is global, and its version number is increased by 1 each time the state of the DDL operation changes. TiDB stores this key-value pair persistently in the PD server with the key of `/tidb/ddl/global_schema_version`, and Value is the version number value of the `int64` type. Inspired by Google F1's Online Schema change algorithm, TiDB keeps a background thread that constantly checks whether the version number of the table structure information stored in the PD server changes. This thread also ensures that the changes of version can be obtained within a certain period of time. +In addition, TiDB also uses a dedicated (Key, Value) key-value pair to store the latest version number of structure information of all tables. This key-value pair is global, and its version number is increased by `1` each time the state of the DDL operation changes. TiDB stores this key-value pair persistently in the PD server with the key of `/tidb/ddl/global_schema_version`, and Value is the version number value of the `int64` type. Inspired by Google F1's Online Schema change algorithm, TiDB keeps a background thread that constantly checks whether the version number of the table structure information stored in the PD server changes. This thread also ensures that the changes of version can be obtained within a certain period of time. -## Introduction to the SQL layer +## SQL layer overview TiDB's SQL layer, TiDB Server, translates SQL statements into Key-Value operations, forwards the operations to TiKV, the distributed Key-Value storage layer, assembles the results returned by TiKV, and finally returns the query results to the client. @@ -128,18 +127,18 @@ For example, to execute the `select count(*) from user where name = "TiDB"` SQL 1. Construct the Key Range: all `RowID` in a table are in `[0, MaxInt64)` range. According to the row data `Key` encoding rule, using `0` and `MaxInt64` can construct a `[StartKey, EndKey)` range that is left-inclusive and right-exclusive. 2. Scan Key Range: read the data in TiKV according to the key range constructed above. -3. Filter data: for each row of data read, calculate the `name = "TiDB"` expression. If the result is true, return to this row. If not, skip this row. +3. Filter data: for each row of data read, calculate the `name = "TiDB"` expression. If the result is `true`, return to this row. If not, skip this row. 4. Calculate `Count(*)`: for each row that meets the requirements, add up to the result of `Count(*)`. **The entire process is illustrated as follows:** ![naive sql flow](/media/tidb-computing-native-sql-flow.jpeg) -This solution is intuitive and feasible, but has some obvious problems in a distributed database scenario. +This solution is intuitive and feasible, but has some obvious problems in a distributed database scenario: -- As the data is being scanned, each row is read out of TiKV via a KV operation with at least one RPC overhead, which can be very high if there is a large amount of data to be scanned. -- It is not applicable to all rows. Data that doesn't meet the conditions doesn't need to be read. -- The value of the rows that meet the conditions is meaningless. In fact, all needed here is just the number of rows. +- As the data is being scanned, each row is read from TiKV via a KV operation with at least one RPC overhead, which can be very high if there is a large amount of data to be scanned. +- It is not applicable to all rows. Data that does not meet the conditions does not need to be read. +- The value of the rows that meets the conditions is meaningless. In fact, all needed here is just the number of rows. ### Distributed SQL operations @@ -149,10 +148,10 @@ The following image shows how data returns layer by layer: ![dist sql flow](/media/tidb-computing-dist-sql-flow.png) -### Architecture of the SQL layer +### Architecture of SQL layer The previous sections introduce some functions of the SQL layer and I hope you have a basic understanding of how SQL statements are handled. In fact, TiDB's SQL layer is much more complicated, with many modules and layers. The following diagram lists the important modules and calling relationships: ![tidb sql layer](/media/tidb-computing-tidb-sql-layer.png) -The user's SQL request is sent to TiDB Server either directly or via `Load Balancer`. TiDB Server will parse `MySQL Protocol Packet`, get the content of requests, parse the SQL request syntactically and semantically, develop and optimize query plans, execute a query plan, get and process the data. All data is stored in the TiKV cluster, so in this process, TiDB Server needs to interact with TiKV and gets the data. Finally, TiDB Server needs to return the query results to the user. +The user's SQL request is sent to TiDB Server either directly or via `Load Balancer`. TiDB Server will parse `MySQL Protocol Packet`, get the content of requests, parse the SQL request syntactically and semantically, develop and optimize query plans, execute a query plan, get and process the data. All data is stored in the TiKV cluster, so in this process, TiDB Server needs to interact with TiKV and get the data. Finally, TiDB Server needs to return the query results to the user. From 3badc8e0a4a8ca18fb32103e98f623503cea4bce Mon Sep 17 00:00:00 2001 From: TomShawn <41534398+TomShawn@users.noreply.github.com> Date: Mon, 13 Jul 2020 16:48:30 +0800 Subject: [PATCH 53/56] Update TOC.md --- TOC.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TOC.md b/TOC.md index ad1f7d71200b6..47ece8c307f15 100644 --- a/TOC.md +++ b/TOC.md @@ -180,7 +180,8 @@ + [Quick Start](/get-started-with-tispark.md) + [User Guide](/tispark-overview.md) + Reference - + [Architecture](/architecture.md) + + Architecture + + [TiDB Computing](/tidb-computing.md) + Key Monitoring Metrics + [Overview](/grafana-overview-dashboard.md) + [TiDB](/grafana-tidb-dashboard.md) From da1aaed5c82e2edeff78089807923bc90fd36270 Mon Sep 17 00:00:00 2001 From: TomShawn <41534398+TomShawn@users.noreply.github.com> Date: Mon, 13 Jul 2020 16:50:07 +0800 Subject: [PATCH 54/56] Update tidb-computing.md --- tidb-computing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tidb-computing.md b/tidb-computing.md index f6b8d244bb979..9ac97933a64e0 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -1,9 +1,9 @@ --- -title: Computation of TiDB Database +title: TiDB Computing summary: Understand the computing layer of the TiDB database. --- -# Computation of TiDB Database +# TiDB Computing Based on the distributed storage provided by TiKV, TiDB builds the computing engine that combines great capability of transactional processing with that of data analysis. This document starts by introducing a data mapping algorithm that maps data from TiDB database tables to (Key, Value) key-value pairs in TiKV, then introduces how TiDB manages metadata, and finally illustrates the architecture of the TiDB SQL layer. From d9524aa299c0af8e1bf9f2043e495067cd804ad5 Mon Sep 17 00:00:00 2001 From: TomShawn <41534398+TomShawn@users.noreply.github.com> Date: Fri, 17 Jul 2020 12:29:56 +0800 Subject: [PATCH 55/56] Update TOC.md --- TOC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TOC.md b/TOC.md index e7419baaeeba1..0165f228eb9b1 100644 --- a/TOC.md +++ b/TOC.md @@ -191,7 +191,7 @@ + Reference + Cluster Architecture + [Overview](/architecture.md) - + [TiDB Computing](/tidb-computing.md) + + [Computing](/tidb-computing.md) + Key Monitoring Metrics + [Overview](/grafana-overview-dashboard.md) + [TiDB](/grafana-tidb-dashboard.md) From 3e6ef48b9bbeaa34a600dbe696831f9c077ea0ab Mon Sep 17 00:00:00 2001 From: TomShawn <41534398+TomShawn@users.noreply.github.com> Date: Mon, 20 Jul 2020 17:58:10 +0800 Subject: [PATCH 56/56] Update tidb-computing.md Co-authored-by: Feng Liyuan --- tidb-computing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tidb-computing.md b/tidb-computing.md index 9ac97933a64e0..516621b39c82f 100644 --- a/tidb-computing.md +++ b/tidb-computing.md @@ -125,7 +125,7 @@ The simplest solution to SQL computing is the [mapping of table data to Key-Valu For example, to execute the `select count(*) from user where name = "TiDB"` SQL statement, TiDB needs to read all data in the table, then checks whether the `name` field is `TiDB`, and if so, returns this row. The process is as follows: -1. Construct the Key Range: all `RowID` in a table are in `[0, MaxInt64)` range. According to the row data `Key` encoding rule, using `0` and `MaxInt64` can construct a `[StartKey, EndKey)` range that is left-inclusive and right-exclusive. +1. Construct the Key Range: all `RowID` in a table are in `[0, MaxInt64)` range. According to the row data `Key` encoding rule, using `0` and `MaxInt64` can construct a `[StartKey, EndKey)` range that is left-closed and right-open. 2. Scan Key Range: read the data in TiKV according to the key range constructed above. 3. Filter data: for each row of data read, calculate the `name = "TiDB"` expression. If the result is `true`, return to this row. If not, skip this row. 4. Calculate `Count(*)`: for each row that meets the requirements, add up to the result of `Count(*)`.