TiDB(8):技术内幕之计算

news2024/11/16 9:47:40

1 关系模型到 Key-Value 模型的映射

在这我们将关系模型简单理解为 Table 和 SQL 语句,那么问题变为如何在 KV 结构上保存 Table 以及如何在 KV 结构上运行 SQL 语句。 假设我们有这样一个表的定义:

CREATE TABLE User {
    ID int,
    Name varchar(20),
    Role varchar(20),
    Age int,
    PRIMARY KEY (ID),
    Key idxAge (age)
};

SQL 和 KV 结构之间存在巨大的区别,那么如何能够方便高效地进行映射,就成为一个很重要的问题。一个好的映射方案必须有利于对数据操作的需求。那么我们先看一下对数据的操作有哪些需求,分别有哪些特点。

对于一个 Table 来说,需要存储的数据包括三部分:

  1. 表的元信息
  2. Table 中的 Row
  3. 索引数据

表的元信息我们暂时不讨论,后面介绍。

对于 Row,可以选择行存或者列存,这两种各有优缺点。TiDB 面向的首要目标是 OLTP 业务,这类业务需要支持快速地读取、保存、修改、删除一行数据,所以采用行存是比较合适的。

对于 Index,TiDB 不止需要支持 Primary Index,还需要支持 Secondary Index。Index 的作用的辅助查询,提升查询性能,以及保证某些 Constraint。

查询的时候有两种模式,一种是点查,比如通过 Primary Key 或者 Unique Key 的等值条件进行查询,如 select name from user where id=1; ,这种需要通过索引快速定位到某一行数据;另一种是 Range 查询,如 select name from user where age > 30 and age < 35;,这个时候需要通过idxAge索引查询 age 在 30 和 35 之间的那些数据。Index 还分为 Unique Index 和 非 Unique Index,这两种都需要支持。

分析完需要存储的数据的特点,我们再看看对这些数据的操作需求,主要考虑 Insert/Update/Delete/Select 这四种语句。

对于 Insert 语句,需要将 Row 写入 KV,并且建立好索引数据。

对于 Update 语句,需要将 Row 更新的同时,更新索引数据(如果有必要)。

对于 Delete 语句,需要在删除 Row 的同时,将索引也删除。

上面三个语句处理起来都很简单。对于 Select 语句,情况会复杂一些。首先我们需要能够简单快速地读取一行数据,所以每个 Row 需要有一个 ID (显示或隐式的 ID)。其次可能会读取连续多行数据,比如 Select * from user;。最后还有通过索引读取数据的需求,对索引的使用可能是点查或者是范围查询。

大致的需求已经分析完了,现在让我们看看手里有什么可以用的:一个全局有序的分布式 Key-Value 引擎。全局有序这一点重要,可以帮助我们解决不少问题。比如对于快速获取一行数据,假设我们能够构造出某一个或者某几个 Key,定位到这一行,我们就能利用 TiKV 提供的 Seek 方法快速定位到这一行数据所在位置。再比如对于扫描全表的需求,如果能够映射为一个 Key 的 Range,从 StartKey 扫描到 EndKey,那么就可以简单的通过这种方式获得全表数据。操作 Index 数据也是类似的思路。接下来让我们看看 TiDB 是如何做的。

TiDB 对每个表分配一个 TableID,每一个索引都会分配一个 IndexID,每一行分配一个 RowID(如果表有整数型的 Primary Key,那么会用 Primary Key 的值当做 RowID),其中 TableID 在整个集群内唯一,IndexID/RowID 在表内唯一,这些 ID 都是 int64 类型。

每行数据按照如下规则进行编码成 Key-Value pair:

Key: tablePrefix{tableID}_recordPrefixSep{rowID}

Value: [col1, col2, col3, col4]

其中 Key 的 tablePrefix/recordPrefixSep 都是特定的字符串常量,用于在 KV 空间内区分其他数据。

对于 Index 数据,会按照如下规则编码成 Key-Value pair:

Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue

Value: rowID

Index 数据还需要考虑 Unique Index 和非 Unique Index 两种情况,对于 Unique Index,可以按照上述编码规则。但是对于非 Unique Index,通过这种编码并不能构造出唯一的 Key,因为同一个 Index 的 tablePrefix{tableID}_indexPrefixSep{indexID} 都一样,可能有多行数据的 ColumnsValue 是一样的,所以对于非 Unique Index 的编码做了一点调整:

Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue_rowID

Value: null

这样能够对索引中的每行数据构造出唯一的 Key。

注意上述编码规则中的 Key 里面的各种 xxPrefix 都是字符串常量,作用都是区分命名空间,以免不同类型的数据之间相互冲突,定义如下:

var(
    tablePrefix     = []byte{'t'}
    recordPrefixSep = []byte("_r")
    indexPrefixSep  = []byte("_i")
)

另外请大家注意,上述方案中,无论是 Row 还是 Index 的 Key 编码方案,一个 Table 内部所有的 Row 都有相同的前缀,一个 Index 的数据也都有相同的前缀。这样具体相同的前缀的数据,在 TiKV 的 Key 空间内,是排列在一起。

同时只要我们小心地设计后缀部分的编码方案,保证编码前和编码后的比较关系不变,那么就可以将 Row 或者 Index 数据有序地保存在 TiKV 中。这种保证编码前和编码后的比较关系不变 的方案我们称为 Memcomparable,对于任何类型的值,两个对象编码前的原始类型比较结果,和编码成 byte 数组后(注意,TiKV 中的 Key 和 Value 都是原始的 byte 数组)的比较结果保持一致。采用这种编码后,一个表的所有 Row 数据就会按照 RowID 的顺序排列在 TiKV 的 Key 空间中,某一个 Index 的数据也会按照 Index 的 ColumnValue 顺序排列在 Key 空间内。

现在我们结合开始提到的需求以及 TiDB 的映射方案来看一下,这个方案是否能满足需求。

首先我们通过这个映射方案,将 Row 和 Index 数据都转换为 Key-Value 数据,且每一行、每一条索引数据都是有唯一的 Key。

其次,这种映射方案对于点查、范围查询都很友好,我们可以很容易地构造出某行、某条索引所对应的 Key,或者是某一块相邻的行、相邻的索引值所对应的 Key 范围。

最后,在保证表中的一些 Constraint 的时候,可以通过构造并检查某个 Key 是否存在来判断是否能够满足相应的 Constraint。

至此我们已经聊完了如何将 Table 映射到 KV 上面,这里再举个简单的例子,便于大家理解,还是以上面的表结构为例。假设表中有 3 行数据:

  • "TiDB", "SQL Layer", 10
  • "TiKV", "KV Engine", 20
  • "PD", "Manager", 30

那么首先每行数据都会映射为一个 Key-Value pair,注意这个表有一个 Int 类型的 Primary Key,所以 RowID 的值即为这个 Primary Key 的值。假设这个表的 Table ID 为 10,其 Row 的数据为:

t10_r1 --> ["TiDB", "SQL Layer", 10]

t10_r2 --> ["TiKV", "KV Engine", 20]

t10_r3 --> ["PD", "Manager", 30]

除了 Primary Key 之外,这个表还有一个 Index,假设这个 Index 的 ID 为 1,则其数据为:

t10_i1_10_1 --> null

t10_i1_20_2 --> null

t10_i1_30_3 --> null

2 元信息管理

上节介绍了表中的数据和索引是如何映射为 KV,本节介绍一下元信息的存储。Database/Table 都有元信息,也就是其定义以及各项属性,这些信息也需要持久化,我们也将这些信息存储在 TiKV 中。每个 Database/Table 都被分配了一个唯一的 ID,这个 ID 作为唯一标识,并且在编码为 Key-Value 时,这个 ID 都会编码到 Key 中,再加上 m_ 前缀。这样可以构造出一个 Key,Value 中存储的是序列化后的元信息。

除此之外,还有一个专门的 Key-Value 存储当前 Schema 信息的版本。TiDB 使用 Google F1 的 Online Schema 变更算法,有一个后台线程在不断的检查 TiKV 上面存储的 Schema 版本是否发生变化,并且保证在一定时间内一定能够获取版本的变化(如果确实发生了变化)。

3 SQL on KV 架构

TiDB 的整体架构如下图所示

TiKV Cluster 主要作用是作为 KV 引擎存储数据,前面已经介绍过了细节,这里不再敷述。这里主要介绍 SQL 层,也就是 TiDB Servers 这一层,这一层的节点都是无状态的节点,本身并不存储数据,节点之间完全对等。TiDB Server 这一层最重要的工作是处理用户请求,执行 SQL 运算逻辑,接下来我们做一些简单的介绍。

4 SQL 运算

理解了 SQL 到 KV 的映射方案之后,我们可以理解关系数据是如何保存的,接下来我们要理解如何使用这些数据来满足用户的查询需求,也就是一个查询语句是如何操作底层存储的数据。

能想到的最简单的方案就是通过上一节所述的映射方案,将 SQL 查询映射为对 KV 的查询,再通过 KV 接口获取对应的数据,最后执行各种计算。

比如 Select count(*) from user where name="TiDB"; 这样一个语句,我们需要读取表中所有的数据,然后检查 Name 字段是否是 TiDB,如果是的话,则返回这一行。这样一个操作流程转换为 KV 操作流程:

  • 构造出 Key Range:一个表中所有的 RowID 都在 [0, MaxInt64) 这个范围内,那么我们用 0 和 MaxInt64 根据 Row 的 Key 编码规则,就能构造出一个 [StartKey, EndKey) 的左闭右开区间
  • 扫描 Key Range:根据上面构造出的 Key Range,读取 TiKV 中的数据
  • 过滤数据:对于读到的每一行数据,计算 name="TiDB" 这个表达式,如果为真,则向上返回这一行,否则丢弃这一行数据
  • 计算 Count:对符合要求的每一行,累计到 Count 值上面 这个方案肯定是可以 Work 的,但是并不能 Work 的很好,原因是显而易见的:
  1. 在扫描数据的时候,每一行都要通过 KV 操作同 TiKV 中读取出来,至少有一次 RPC 开销,如果需要扫描的数据很多,那么这个开销会非常大
  2. 并不是所有的行都有用,如果不满足条件,其实可以不读取出来
  3. 符合要求的行的值并没有什么意义,实际上这里只需要有几行数据这个信息就行

5 分布式 SQL 运算

如何避免上述缺陷也是显而易见的,

首先我们需要将计算尽量靠近存储节点,以避免大量的 RPC 调用。

其次,我们需要将 Filter 也下推到存储节点进行计算,这样只需要返回有效的行,避免无意义的网络传输。

最后,我们可以将聚合函数、GroupBy 也下推到存储节点,进行预聚合,每个节点只需要返回一个 Count 值即可,再由 tidb-server 将 Count 值 Sum 起来。

这里有一个数据逐层返回的示意图:

6 SQL 层架构

上面几节简要介绍了 SQL 层的一些功能,希望大家对 SQL 语句的处理有一个基本的了解。实际上 TiDB 的 SQL 层要复杂的多,模块以及层次非常多,下面这个图列出了重要的模块以及调用关系:

用户的 SQL 请求会直接或者通过 Load Balancer 发送到 tidb-server,tidb-server 会解析 MySQL Protocol Packet,获取请求内容,然后做语法解析、查询计划制定和优化、执行查询计划获取和处理数据。

数据全部存储在 TiKV 集群中,所以在这个过程中 tidb-server 需要和 tikv-server 交互,获取数据。

最后 tidb-server 需要将查询结果返回给用户。

7 小结

到这里,我们已经从 SQL 的角度了解了数据是如何存储,如何用于计算。 后面我们将会介绍一些关于 PD 的信息,这部分会比较有意思,里面的很多东西是在使用 TiDB 过程中看不到,但是对整体集群又非常重要。主要会涉及到集群的管理和调度。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/727847.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于springboot+vue的文超市进销存管理系统(源代码+数据库+12000字论文)083

基于springbootvue的文超市进销存管理系统(源代码数据库12000字论文)083 一、系统介绍 (本项目有ssmvue版本) 本系统分为管理员、用户、员工三种角色 用户角色包含以下功能&#xff1a; 登录、注册、购物车、订单提交、商品评论、收藏、充值、收货地址管理、收藏管理、订单…

NXP-无感BLDC代码MCSPTE1AK116_BLDC_6Step代码详解

目录 开发平台 工程目录 Generated_Code Sources Config 电机的参数 BLDC参数 无感模式下的一些参数 Peripherals FTM/PDB/ADC配置参数 actuate_s32k meas_s32k motor_structure state_machine main main()主函数 PORT_IRQHandler() PDB0_IRQHandler() FTM1…

最大正方形 · Maximal Square

链接&#xff1a; 题解&#xff1a;九章算法 - 帮助更多程序员找到好工作&#xff0c;硅谷顶尖IT企业工程师实时在线授课为你传授面试技巧 1.暴力的方法&#xff1a;遍历每一个&#xff08;i&#xff0c;j&#xff09;位置&#xff0c;如果当前点为1&#xff0c;则以当前节点为…

如何编写PlantUml文本绘图时序图

效果如图 代码示例 startumlparticipant "上游" as BEGIN participant "SFTP" as SFTP control "文件系统" as FILE participant "业务系统" as BUSactivate BEGIN BEGIN ->SFTP: 上传文件 activate SFTP autonumber 1.0 FILE -&g…

常用的网址

画图网页&#xff1a; https://www.processon.com/diagrams 二进制转换网页&#xff1a; https://tool.oschina.net/hexconvert/ 在线网络计算器 https://www.sojson.com/convert/subnetmask.html 学习网站掘金&#xff1a; https://juejin.cn 注册外网账号网页&#xff1a…

使用Lambda表达式对List<Map<String,Object>>中key值相同的Map进行分组合并

现有两张表A表和B表&#xff0c;A表存放的是各省市的认证次数&#xff0c;B表存放的是各省市的申领次数&#xff0c;重点关注dq,cs这两个字段&#xff0c;其他的字段可忽略 A表&#xff08;省市认证次数表&#xff09; B表&#xff08;省市申领次数表&#xff09; 项目中有以下…

辅助性能优化——长安链性能分析工具原理及用法

如何提升区块链系统性能是很多开发者都会关注的事&#xff0c;但是有些对区块链并非十分熟悉的开发者可能会感到没有头绪。长安链提供了性能分析工具帮助开发者梳理系统耗时&#xff0c;优化系统性能。下面对长安链性能分析工具原理及使用进行介绍。 一、 概述 time_counter.s…

Windows兼容性设置图文教程,Windows兼容模式怎么设置?服务器兼容是什么意思?服务器兼容性怎么改?

兼容性&#xff08;compatibility&#xff09;是指硬件之间、软件之间或是软硬件组合系统之间的相互协调工作的程度。兼容的概念比较广&#xff0c;相对于硬件来说&#xff0c;几种不同的电脑部件&#xff0c;如CPU、主板、显示卡等&#xff0c;如果在工作时能够相互配合、稳定…

备战秋招004(20230706)

文章目录 前言一、今天学习了什么&#xff1f;二、关于问题的答案1.SE 总结 前言 提示&#xff1a;这里为每天自己的学习内容心情总结&#xff1b; Learn By Doing&#xff0c;Now or Never&#xff0c;Writing is organized thinking. 目前的想法是&#xff0c;根据 Java G…

三种方法将视频转换为AVI格式,与大家分享!

将视频转换为AVI格式是常见的需求&#xff0c;因为AVI格式具有广泛的兼容性和可编辑性。本文将介绍三种常用的方法&#xff0c;包括记灵在线工具、剪映和格式工厂。这些方法简单易行&#xff0c;帮助您将视频文件快速转换为AVI格式&#xff0c;满足不同的需求。 方法一&#x…

EasyCVR接入大量设备级联后出现分组加载异常是什么原因?

EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流。 有用…

接口自动化测试实战之pytest框架+allure讲解

一、前言 本文章主要会讲解Python中pytest框架的讲解&#xff0c;介绍什么是pytest、为何要测试、为何使用以及参考和扩展等等&#xff0c;话不多说&#xff0c;咱们直接进入主题哟。 二、pytest讲解 2.1 什么是pytest&#xff1f; pytest是一款单元测试框架&#xff0c;在…

尚硅谷Docker实战教程-笔记10【高级篇,Docker微服务实战】

尚硅谷大数据技术-教程-学习路线-笔记汇总表【课程资料下载】视频地址&#xff1a;尚硅谷Docker实战教程&#xff08;docker教程天花板&#xff09;_哔哩哔哩_bilibili 尚硅谷Docker实战教程-笔记01【基础篇&#xff0c;Docker理念简介、官网介绍、平台入门图解、平台架构图解】…

浅析住宅小区电动车充电桩的电气设计与平台管理系统

安科瑞电气股份有限公司 上海嘉定201801 摘要&#xff1a;根据目前对于新能源汽车发展规划及政策&#xff0c;以及国内外充电设施的主要类型和技术参数。论述地下车库电动汽车充电桩的供配电系统的设计及设计过程中需要注意的一些问题。 关键词&#xff1a;充电桩&#xff1b…

力扣题库刷题笔记36--有效的数独

1、题目如下&#xff1a; 2、个人Python代码实现如下&#xff1a; 3、个人Python代码思路&#xff1a; 先放一个AI解释的思路&#xff1a; 个人理解&#xff0c;本题思路其实很简单&#xff0c;判断每一行、每一列、每一个3*3的子数独是否存在重复数字&#xff0c;如果存在则返…

不用转化器把pdf转化成Excel,分享两个实用方法!

将PDF文件转换为Excel格式通常是进行数据提取和分析的重要步骤。尽管市面上有许多PDF转Excel的工具&#xff0c;但本文将介绍两种无需使用转换器的实用方法&#xff0c;分别是复制粘贴法和使用记灵在线工具。这些方法简单易行&#xff0c;帮助您快速将PDF中的数据提取到Excel表…

第21章:索引优化与查询优化

一、索引优化与查询优化 1.什么情况下要进行数据库调优 ①索引失效&#xff0c;没有充分利用到索引---索引建立 ②关联查询太多join---SQL优化 ③服务器调优和各个参数的设置---调整my.cnf ④数据过多---分库分表 2.SQL优化的技术 ①物理查询优化&#xff1a;通过索引和…

图论算法:DFS求有向图或无向图两点间所有路径及Dijkstra算法求最短路径

1、目的 1)根据有向图获取指定起终点的所有路径; 2)直接求解两点间最短路径。 2、示例效果 2.1 原始数据 路线起终点整理如下: // 共计12个顶点,19条边。 (起点,终点,1)最后的1代表起点终点是连通的。 起点,终点,1:2 4 1 起点,终点,1:9 10 1 起点,终点,1:…

Java面向对象程序开发——网络编程入门知识

​ 文章目录 软件结构网络通信协议协议分类网络编程三要素TCP通信程序概述Socket类构造方法成员方法 ServerSocket类构造方法成员方法 简单的TCP网络程序客户端向服务器发送数据服务器端 文件上传服务端实现&#xff1a;客户端实现&#xff1a; BIO 、 NIO 与 AIO 软件结构 C…

Unity3D如何在一个项目建多个场景

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 3D工具集&#xff1a; NSDT简石数字孪生 设置多个场景 您可以添加多个场景、编辑查看场景的方式以及更改场景设置。 要创建新场景&#xff0c;请参阅创建、加载和保存场景。 添加场景 有两种方法可以向项目添加新场景&…