TiDB数据库

目录

TiDB数据库是什么?

TiDB 架构

TiDB数据库的存储原理

设计思想

基本概念

实现原理

TiDB数据库的管理机制

信息收集

Region 的分裂

负载均衡

TiDB数据库的应用场景

替代 MySQL

替代 NoSQL 数据库

实时数据仓库


TiDB数据库是什么?

TiDB 是一款结合了传统的关系型数据库和 NoSQL 数据库特性的新型分布式数据库。

TiDB 是基于 Google 公司的 Google Spanner / F1 论文设计的开源分布式数据库,而 Spanner/F1 是 Google 公司研发的可扩展的、多版本、全球分布式、可同步复制的数据库。

TiDB 是第一个把数据分布在全球范围内的系统,并且支持外部一致性的分布式事务。因此,TiDB 在设计时也追求无限的水平扩展,具备强一致性和高可用性,支持分布式事务的处理。

同时,TiDE 的目标是为在线交易处理(OnlineTransactional Processing, OLTP)和在线分析处理(Online Analytical Processing, OLAP)场景提供一站式的解决方案,支持MySQL数据库的数据轻松地向 TiDB 迁移,包括分库、分表后的 MySQL 集群也可通过工具进行实时迁移。

TiDB 架构

TiDB 具有无限水平扩展和高可用性的特点,通过简单地增加新节点即可实现计算和存储能力的扩展,轻松地应对高并发、海量数据的应用场景。

TiDB 的整体架构参考 Google Spanner/F1 的设计,也分为 TiDB 和 TiKV 上下两层。

TiDB 对应的是Google F1,是一层无状态的 SQL 层,负责与客户端交互,对客户端体现的是 MySQL 网络协议,且客户端需要通过一个本地负载均衡器将 SQL 请求转发到本地或最近的数据中心中的 TiDB 服务器。

TiDB 服务器负责解析用户的 SQL 语句,生成分布式的查询计划,并翻译成底层 Key-Value 操作发送给 TiKV,而 TiKV 则是真正存储数据的地方,对应的是 Google Spanner,是一个分布式 Key-Value 数据库,支持弹性水平扩展、 自动的灾难恢复和故障转移,以及 ACID 跨行事务。

另外,TiDB 架构采用 PD 集群来管理整个分布式数据库,PD 服务器在 TiKV 节点之间以 Region 作为单位进行调度,将部分数据迁移到新添加的节点上,完成集群调度和负载均衡。

TiDB 的集群架构如下图所示。
 

从上图中可以看出,TiDB 集群架构主要由 TiDB 节点、PD(Placement Driver)节点和 TiKV 节点 3 个组件构成。通常 TiDB 集群架构推荐至少部署 3 个 TiKV 节点、3 个 PD 节点和 2 个 TiDB 节点,随着业务量的增长,可按照需求相应地添加 TiKV 或者 TiDB 节点。

接下来具体介绍每个组件完成的功能。

TiDB Server

TiDB Server 完成与客户端的交互,兼容绝大多数的 MySQL 语法,属于 SQL 层。

在集群当中,首先多个客户端通过负载均衡组件将 SQL 请求转送至不同的 TiDB Server,TiDB Server 负责解析 SQL 请求,获取请求内容;然后进行合法性验证和类型推导;接着进行查询优化,包括逻辑优化和物理优化,优化完成后构建执行器;最后,把数据从 TiKV 中取出来进行计算,将最终结果反馈给客户端。

TiDB处理用户请求的流程如下图所示。
 

TiDB Server 是无状态的,每个 TiDB Server 都是平等的,其本身并不存储数据,只负责计算,并可以进行无限水平扩展。

当集群中单个 TiDB Server 实例失效时,可以重启这个实例或部署一个新的实例来提高集群的可用性。

TiKV Server

TiKV Server 主要负责数据的存储,是一个分布式的提供事务的键值(Key-Value)存储引擎,存储的是键值对(Key-Value pair),并按照 Key 的二进制顺序进行有序存储。

TiKV Server 将整个 Key-Value 空间分成很多段,每一段都是一系列连续的 Key,这一段称为 Region。

TiKV Server 将 Region 作为存储数据的基本单元,每个 Region 负责存储一定大小的数据。每个 TiKV Server 负责多个 Region,并使用 Raft 协议来为每个 Region 做备份,可用于保持数据的一致性和数据容灾, 通过 PD Server 进行负载均衡调度。

PD Server

PD Server 是 TiDB 中的全局中心总控节点,它是以集群的方式部署的,负责整个集群的调度,如数据的迁移及负载均衡等,也负责全局 ID 的生成,以及全局时间戳 TSO 的生成等。

PD 还保存着整个集群 TiKV 的元信息,即某个 Key 存储在哪个 TiKV 节点上,负责为 TiDB Server 提供路由功能。

TiDB数据库的存储原理

由前述可知,TiDB 架构是 SQL 层和 TiKV 存储层分离的,SQL 层完成用户 SQL 请求的解析、验证等工作,并执行 SQL 的查询优化。

TiKV 作为 Key-Value 数据库,可完成实际数据的存储,支持分布式事务,并提供对上层透明的水平扩展。

本节主要介绍 TiDB 的存储原理,包括 TiDB 的设计思想、基本概念及实现原理。

设计思想

TiDB 的设计是分层的,它的逻辑结构如下图所示,最底层选用了当前比较流行的存储引擎 RocksDB。
 

RocksDB 性能很强,但它是单机的,为了保证高可用性,因此使用副本的机制。

上层使用 Raft 协议来保证单机失效后数据不会出现丢失和出错,即用 Raft 协议把数据复制到多台 TiKV 节点上,保证在一台机器失效时还有其他机器的副本可以使用。

在安全可靠的 TiKV 存储的基础上再去实现多版本控制(MVCC),提高分布式场景下数据库的性能以及避免死锁。最后再去构建分布式事务,以上这些功能就构成了存储层 TiKV。然后由 TiDB 层实现 SQL 层,并解析 MySQL 网络协议即可。

基本概念

Key-Value 模型

作为保存数据的系统,首先要决定数据的存储模型,即数据是以何种形式保存下来的。

在 TiKV 中,数据以 Key-Value 模型的形式存储,TiKV 可以比作一个巨大的 Map,里面有序地存储大量的键值对(Key-Value pair),其中 Key 和 Value 均是原始的 Byte 数组,且在 Map 中这些键值对是按照 Key 的二进制顺序排列的,即可以通过 Key 进行顺序查找。

RocksDB

TiKV 是参考 Google 的 Spanner 设计实现的,但是 Spanner 是使用 Goolge 文件系统(GFS)作为它的分布式文件系统来存储真实数据的。

TiKV 不依赖任何分布式文件系统,它将键值对保存在 RocksDB 中,具体向磁盘上写数据则由 RocksDB 完成。

RocksDB 是一个开源的、高性能的单机存储引擎,由 Facebook 团队在做持续优化,可以很容易地调整读写和放大空间,以满足 TiKV 的要求。

Raft

Raft 是一个管理复制日志的一致性算法,提供了与 Paxos 算法相同的容错功能和性能。但是它的算法结构与 Paxos 不同,使用 Raft 算法用户能够更加容易理解,并且更容易构建一个分布式系统。

Raft 协议将一致性算法分成了几个关键模块,主要提供以下功能。

  1. 领导者选举,Raft 算法使用一个随机计时器来选举领导者,日志条目只从领导者发送给其他的服务器。
  2. 成员变更,Raft 算法为了调整集群中的成员关系使用了新的联合一致性的方法,这种方法中的大多数不同配置的机器在转换关系的时候会重叠。这就使得配置改变的时候,集群仍能够继续操作。
  3. 日志复制,领导者必须从客户端接收日志,然后复制到集群中的其他节点,并且强制要求其他节点的日志与自己保持相同。


TiKV 使用 Rah 来实现数据的复制,每条数据的变更都会被记录成一条 Raft 日志。

通过 Raft 的日志复制功能,可将数据安全、可靠地同步到集群的多个节点上,这样,Raft 可以保证在单机失效时,数据不会出现丢失和出错。

MVCC

多版本并发控制(Multi-Version Concurrency Control, MVCC)是一种并发控制方法,在数据库系统当中实现对数据库的并发访问。

在 TiKV 中,如果两个 Client 同时去修改一个键值对,而且没有使用多版本控制,则需要对访问的键值对上锁,保证在同一时刻只有一个 Client 对这个数据进行操作。

在分布式场景下,采用这种上锁机制可能会带来性能以及死锁的问题,因此,TiKV 采用 MVCC 来完成这种多用户的并发访问。

MVCC 的实现是通过在 Key 后面添加 Version 来实现的,如下图所示,多个用户可以同时对数据进行写操作,同时也可以提供旧版本给其他用户读取访问。

这样就可以保证多个用户在并发访问时都可以访问数据,并形成这种带版本号的键值对。当然,如果不删除对这种带版本的键值对,数据库系统的数据会非常庞大,MVCC 提供垃圾收集器对无效版本的键值对进行回收和删除。

实现原理

关系模型与 Key-Value 模型的映射

在关系模型里,数据是使用二维表的逻辑结构进行存储的,每张表由多个元组(即二维表中的行)组成,而每个元组由多个属性组成,即二维表中的列。

例如,定义如下的表结构:

 
  1. CREATE TABLE User {
  2. User ID int,
  3. Name varchar(20),
  4. Email varchar(20),
  5. PRIMARY KEY (User ID)
  6. };

同时,User 表中数据如下表所示。
 

User 表
User IDNameEmail
1Tonytony@pingcap.com
2Timtim@pingcap.com
3Jackjack@pingcap.com

可以看到,关系模型中的 Table 结构与 TiKV 的 Key-Value 结构是有巨大差异的。那么,如何将 Table 结构映射成 Key-Value 结构是 TiDB 的重点工作。

TiDB 为每个表分配一个 TablelD,每个索引分配一个 IndexID,同样每一行也对应一个 RowID,但如果表的主键是整型,则会将主键作为 RowID,例如,User 表的主键为 User ID 且是整型的, 那么,在 TiDB 中 UserID 就作为 RowID 使用。

同时,TablelD 在整个 TiDB 集群中是唯一的, IndexID 和 RowID 在表内唯一。定义好这些 ID 以后,TiDB 将 User 表中的一行数据映射为一个键值对,Key 为 TablelD+RowID 的格式,整行数据为 Value 值。

同样地,索引也需要建立键值对,一条索引可映射为一个键值对,Key 以 TablelD+IndexID 构造前缀,以索引值构造后缀, 即 TablelD+IndexID+IndexColumnsValue 格式,Value 指向行 key。

对于上表中的 User 表,假设 TablelD 为10,RowID 为 User 表的关键字,则 User 表的数据映射成键值对的格式,如下表所示。
 

Table 表的 Key-Value 映射
KeyValue
10_1Tony | tony@pingcap.com
10_2Tim | tim@pingcap.com
10_3Jack | jack@pingcap.com


如果以 Name 属性构建索引,IndexID 为 1,则索引表可以映射为下图所示的格式。
 

从这个例子中可以看到,一个表中的数据或索引会具有相同的前缀,因此,在 TiKV 的 Key-Value 空间内,一个表的数据会出现在相邻的位置,便于 SQL 的查找。

Region 的分散与复制

在 TiKV 中,数据以键值对的形式存储在 RocksDB 中,再由 RocksDB 存储到磁盘中。为了实现存储的水平扩展,需要将数据分散在集群的多个节点上,即把整个巨大、有序的键值对按照某种规则分割成许多段,再将每段分散存储在不同机器上。

在 TiKV中,分割的每一段称为 Region,每个 Region 里是一系列连续的 Key,且每个 Region 中保存的数据不超过一定的大小,默认为 64MB,当存储的数据超过一个 Region 的域值后,将重新产生新的 Region。因为每个 Region 中的 Key 是有序的,所以每个 Region 都可以用 StartKey 到 EndKey 的左闭右开区间来描述。

TiKV 中 Region 的逻辑结构如下图所示。
 

数据按照 Key 拆分成很多 Region,每个 Region 的数据保存在一个节点上面。

整个系统再由 PD Server 负责将 Region 尽可能地均匀分布在集群中所有节点上,这样可以通过增加新的节点来实现存储容量的水平扩展,只要增加新节点,PD Server 就会自动地将其他节点上的 Region 调度过来。

这种调度策略同时实现了数据的负载均衡,不会出现一个节点数据满负荷,而其他节点是空载的情况。

实现了数据的负载均衡以后,就要考虑数据的容灾,即如果每个 Region 只有单独的一份存储在一个节点上,那么,当这个节点宕机时,数据会丢失。

TiKV 以 Region 为单位实现数据的复制,每个 Region 的数据在集群中以多个副本的形式存储在多个节点上。在 TiKV 中,每一个副本叫作 Replica,Replica 之间通过 Raft 来保持数据的一致。

每个 Region 中的多个 Replica 构成一个 Raft Group,如下图所示。
 

每个 Raft Group 由一个 Replica 作为 Group 的 Leader,其他的 Replica 作为 Follower。

例如, 在上图中,Region4 的三个副本分别存储于 TiKV 的 Node1、Node2 和 Node4 上,Node1 上的 Region4 作为Leader,Node2 和 Node4 上的 Region4 作为 Follower。

当对 Region4 中的数据进行读写时,Client 都是与 Node1 的 Region4 即 Leader 进行操作的,操作完成后,再由 Leader 复制给其他的 Follower。

Region 的分散与复制满足了 TiKV 的负载均衡及容灾,用户不用再担心单机故障造成数据丢失的问题,同时也提高了系统的水平扩展性。

SQL 运算

了解了关系模型的表结构与 Key-Value 的映射及 Region 的分散与复制以后,接着来学习如何用 SQL 的查询语句来操作底层存储的数据。

主要分成三个步骤:首先将 SQL 查询映射为对 Key-Value 的查询,然后通过 Key-Value 接口获取对应的数据,最后执行各种计算。

1) 构造 Key,找出 Key 的范围,根据 SQL 语句找到所需要查找的表,而表的 TablelD 在整个集群中是唯一的,RowID 定义的是 int64 类型的,范围也在 [0,MaxInt64) 内。因此,根据 Key 的编码规则 一一TablelD+RowID,可以找到 [StartKey,EndKey) 这样的左闭右开的区间范围。

2) 扫描 Key 的范围,根据上一步构造出的 Key 的区间范围,读取 TiKV 中的数据,这里从 TiKV 获取数据是通过 RPC 的方式获取的。

3) 过滤数据并计算,对于读取到的 Value 值要判断是否满足 SQL 语句的条件,过滤出符合 SQL 要求的 Value 值。同时,SQL 查询语句通常需要返回计算结果,如 count、sum 等聚合函数,因此,这一步也需要对满足条件的 Value 值进行各种计算。

以上 SQL 查询过程看似简单,但试想一下,在数据量比较大的情况下,一张表的数据会按照 Region 的方式存储在多个 TiKV Server 上,这时就需要采用分布式 SQL 运算。

首先,需要将计算尽量靠近存储节点,以避免大量的 RPC 调用;其次,需要将 SQL 语句的过滤条件也下推到存储节点进行计算,只需要返回有效的行,避免一些无意义的数据在网络中传输;最后,可以将 count().sum() 等聚合函数也下推到存储节点,进行预聚合,每个节点只需要返回一个聚合结果即可,最终再由 TiDB Server 来进行合并。

例如,一个 SQL 语句 Select count(*) from user where name="Jack",它的执行过程如下图所示。
 

执行时,先根据 SQL 语句构造出 Key 的区间范围后,获取这个区间的键值对的分布,然后通过 DistSQLAPI 发送 RPC 请求来访问 TiKV Server,每个 TiKV Server 对本节点上存储的数据进行过滤和计算,并返回结果给 DistSQLAPI,最终由 TiDB 对结果进行汇聚。

TiDB数据库的管理机制

在前面《TiDB架构》一节中介绍过,TiDB 由三个组件组成,分别是 TiDB Server、TiKV Server 和 PD Server。

TiKV 将关系模型中的表格数据转换成 Key-Value 的形式存储在磁盘中,TiDB 则负责完成客户端 SQL 请求的解析和执行。这两个组件已经完成了数据的存储和读写查询,基本实现了一个单机的数据库系统。

但是,TiDB 系统是在大数据场景下诞生的,它的设计初衷是处理分布式的数据库,并具有高可用性和无限的水平扩展能力。那么,这种分布式集群里事务的调度和数据的容灾是如何实现的?

这是由另一个组件 PD Server(后面以 PD 来简称这个组件)来完成的。PD 自身是一个集群, 由多个 Server 组成,有一套选举的策略,由一个 Leader 对外提供服务。

当 PD 崩溃时,系统会重新选举其他节点作为 Leader 来提供服务,无须担心 PD 节点的失效。

下面介绍 PD 作为全局中心总控节点是如何来管理分布式数据库系统的。

信息收集

既然要管理整个分布式数据库系统,就必须收集足够的信息,例如,节点状态、Region 的副本数及位置信息、Raft Group 的信息等。

PD 可从 TiKV 的心跳信息中获取这些相关的信息,这里有两类心跳信息,一类是 TiKV 节点与 PD 之间的心跳包,另一类是 Raft Group 里的 Region Leader 上报的心跳包。

PD 通过 TiKV 节点上报的心跳包来检测每个节点是否存活,以及是否有新的节点加入,PD 对新加入的节点生成全局 ID。同时此心跳包中还包含节点的元数据信息,主要包括:

  • 总磁盘容量;
  • 可用磁盘容量;
  • 承载的Region数量;
  • 数据写入速度;
  • 是否过载;
  • 标签信息。


Region Leader 上报的心跳信息则包括了这个 Region 的元数据信息,主要包括:

  • Region Leader 的位置;
  • Region Followers 的位置;
  • 掉线的副本数;
  • 数据写入/读取的速度。


PD 通过这两种心跳信息获取这个集群的信息,就可以对集群进行调度了。

例如,获取 Region 的位置信息后,TiDB Server 在收到 Client 请求时,解析 SQL 语句,找出 Key 的范围,会向 PD 请求获取 Key 所在 Region 的节点,再与具体 TiKV 节点交互式地读取数据。

Region 的分裂

在 TiKV 中,每个 Raft Group 的 Leader 会定期检查 Region 所占用的空间是否超过某个域值。例如,Region 默认的域值为 64MB,如果一个 Region 超过了 64MB,就需要对 Region 进行拆分。

首先,Leader 会向 PD 发送一个分裂的请求,PD 收到请求信息后,会生产一个新的 RegionlD,并返回给 Leader;

然后,Leader 将此信息写入 raft log 中,TiKV 根据日志信息对 Region 进行分裂,一个 Region 分裂成两个,其中一个继承原 Region 的所有元信息,另一个的元信息则由 PD 生成,如 RegionlD 等;

最后分裂成功后,TiKV 会告诉 PD 这两个 Region 的相关信息,由 PD 来更新 Region 的元信息,包括分裂后的 Region 的位置、副本等。

负载均衡

在 TiKV 中,数据的读取和写入都是通过 Leader 进行的,所有的计算负载都在 Leader 上。如果多个 Raft Group 的 Leader 都在同一个节点上,则要对这些 Leader 进行移动,使其均匀分布在不同的节点上。

同时在实际应用中,通常会出现热点访问的数据,即这种数据被频繁访问,这也会造成当前节点负荷过重。

PD 根据数据的写入和读取速度,检测哪些 Region 是访问热点,然后将这些热点 Region 分散在不同节点上,防止出现一个节点被频繁访问,而其他节点处于空闲状态的情况。

另外,PD 根据节点的总磁盘容量和可用磁盘容量来调度 Region 的存放位置,保证每个节点占用的存储空间大致相等。

同时,根据心跳信息判断当前节点是否失效,如果在一定时间内都没有收到此节点的心跳包,则认为此节点已经下线,这时就需要将此节点上的 Region 都调度到其他节点上。

PD 通过不断地收集节点和 Leader 的心跳信息,获取整个集群的状态,并根据这些信息对集群的操作进行调度。

每次 PD 收到 Region Leader 发来的心跳包,都会检测是否需要对此 Region 进行操作,如 Region 分裂、Region 移动等。

PD 将需要进行的操作通过心跳包的回复信息返回给 Region Leader。这些操作由 Region Leader 根据当前状态来决定是否执行此操作,执行的结果通过后续的心跳包信息发送给 PD,PD 再来更新整个集群的状态。

TiDB数据库的应用场景

TiDB 的应用场景是典型的 OLTP 场景,它的设计目标是 100% 的 OLTP 场景和 80% 的 OLAP 场景,同时还提供 TiSpark 项目以完成更复杂的 OLAP 分析。

TiDB 的应用场景主要分以下几种。

替代 MySQL

传统的 MySQL 数据库在数据量急速增长后,使用分库分表的技术来对数据库进行扩展,在分布式数据库系统中也是使用分片技术,但是这些技术不管在维护成本或开发成本上都很高。

而 TiDB 提供了一个可弹性的横向扩展的分布式数据库,并且具有高可用性,它兼容 MySQL 协议和绝大多数的 MySQL 语法,在通常情况下,用户无须修改代码就可以将 MySQL 无缝迁移到 TiDB。

这种应用场景的实际案例如下。

1) 摩拜单车(Mobike)。摩拜单车从 2017 年年初就开始使用 TiDB,目前已经部署了多个集群用于不同的应用场景,如开关锁日志成功率的统计等。所有集群有近百个节点,存储数十 TB 的数据。摩拜单车将 TiDB 作为核心的数据交易和存储支撑平台,来解决海量数据的在线存储、大规模实时数据分析和处理。

2) 今日头条。TiDB 主要应用在今日头条核心 OLTP 系统一对象存储系统的部分元数据存储,支持头条图片和视频相关业务,如抖音小视频等。TiDB 支撑着今日头条 OLTP 系统里数据流量最大、每秒查询率(Query Per Second, QPS)最高的场景。此场景的集群容量 50多 TB,数据量日增 5 亿行,可见数据量增长的速度非常快。

替代 NoSQL 数据库

NoSQL 数据库拥有弹性的伸缩能力,具有实时并发写入能力,但是 NoSQL 数据库不支持 SQL,也不支持事务的 ACID 特性,NoSQL 无法满足某些强一致性的场景下的需求。

TiDB 具备 SQL 所有的特性,同时满足数据的在线扩展。在线旅行网站“去哪儿”目前使用了几个 TiDB 集群来替代 MySQL 和 HBase,如机票离线集群、金融支付集群等。

集群用来存储支付信息表和订 单信息表,这些信息严格支持事务 ACID 特性,因此可以将原来存储于 MySQL 中的数据同步到 TiDB 中,然后,运营或开发人员可以在 TiDB 上进行 merge 单表查询或 OLAP 分析。

生活服务平台“饿了么”网站的在线交易平台和即时配送平台,随着用户量和订单量的快速增长,数据量也快速增长,产生了对数据存储的强烈需求。

之前这些数据存储于 MySQL、RedisMongoDB、Cassandra 等不同系统中,数据扩容不方便,维护成本也高,因此,“饿了么”网站选择使用 TiDB 来统一存储这些数据,以满足大数据量、高性能、高可靠、高可用、易运维的要求。

实时数据仓库

目前企业大多数的数据分析场景的解决方案都是围绕着 Hadoop 生态系统展开的,包括 HDFS、Hive、Spark 等。但是单纯使用 Hadoop 已经无法满足一些实时的 OLTP 和复杂的 OLAP 需求。

随着 TiDB 的子项目 TiSpark 的发布,可以在拥有关系数据库的事务写入能力同时进行复杂的分析。

在这方面的实际案例也有很多,例如,易果集团使用 TiDB 作为新的实时系统,OLTP 的数据和实时数据可以实时写入 TiDB 中,OLAP 业务通过 TiSpark 进行分析,并且可以通过 TiSpark 将实时数据和离线数据整合起来。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值