大数据存储组件TiDB原理+实战篇2

本文涉及的产品
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
云原生大数据计算服务 MaxCompute,5000CU*H 100GB 3个月
简介: 大数据存储组件TiDB原理+实战篇

4.1.TiDB-SQL操作

(1)创建、查看和删除数据库


要创建一个名为 samp_db 的数据库,可使用以下语句:

CREATE DATABASE IF NOT EXISTS samp_db;

使用 SHOW DATABASES 语句查看数据库:

SHOW DATABASES;

使用 DROP DATABASE 语句删除数据库,例如:

DROP DATABASE samp_db;

再次查看数据库:

SHOW DATABASES;

(2)创建、查看和删除表

先创建一个库


CREATE DATABASE IF NOT EXISTS samp_db;

USE samp_db;

使用 SHOW TABLES 语句查看数据库中的所有表。例如:


SHOW TABLES FROM samp_db;

使用 CREATE TABLE 语句创建表。


如果表已存在,添加 IF NOT EXISTS 可防止发生错误:


CREATE TABLE IF NOT EXISTS person (


number INT(11),


name VARCHAR(255),


birthday DATE


);


使用 SHOW CREATE 语句查看建表语句。例如


SHOW CREATE table person;

使用 SHOW FULL COLUMNS 语句查看表的列。 例如:


SHOW FULL COLUMNS FROM person;

使用 DROP TABLE 语句删除表。例如:


DROP TABLE person或者DROP TABLE IF EXISTS person;

(3)创建、查看和删除索引

先创建一张表:

CREATE TABLE IF NOT EXISTS person (


number INT(11),


name VARCHAR(255),


birthday DATE


);


对于值不唯一的列,可使用 CREATE INDEX 或 ALTER TABLE 语句。例如:


CREATE INDEX person_num ON person (number) 或者 ALTER TABLE person ADD INDEX person_num (number);

使用 SHOW INDEX 语句查看表内所有索引:


SHOW INDEX from person;

使用 ALTER TABLE 或 DROP INDEX 语句来删除索引。与 CREATE INDEX 语句类似,DROP INDEX 也可以嵌入 ALTER TABLE 语句。例如:


DROP INDEX person_num ON person;

ALTER TABLE person DROP INDEX person_num;

对于值唯一的列,可以创建唯一索引。例如:


CREATE UNIQUE INDEX person_num ON person (number) 或者 ALTER TABLE person ADD UNIQUE person_num (number);

(4)增删改查数据


使用 INSERT 语句向表内插入数据。例如:

INSERT INTO person VALUES(“1”,“tom”,“20170912”);

使用 SELECT 语句检索表内数据。例如:

SELECT * FROM person;

使用 UPDATE 语句修改表内数据。例如:

UPDATE person SET birthday=‘20200202’ WHERE name=‘tom’;

SELECT * FROM person;

使用 DELETE 语句删除表内数据:

DELETE FROM person WHERE number=1;

SELECT * FROM person;

(5)创建、授权和删除用户


使用 CREATE USER 语句创建一个用户 tiuser,密码为 123456:

CREATE USER ‘tiuser’@‘localhost’ IDENTIFIED BY ‘123456’;

授权用户 tiuser 可检索数据库 samp_db 内的表:

GRANT SELECT ON samp_db.* TO ‘tiuser’@‘localhost’;

查询用户 tiuser 的权限:

SHOW GRANTS for tiuser@localhost;

删除用户 tiuser:

DROP USER ‘tiuser’@‘localhost’;

查看所有权限

SHOW GRANTS;

4.2.TiDB-读取历史数据

(1)功能说明


TiDB 实现了通过标准 SQL 接口读取历史数据功能,无需特殊的 client 或者 driver。当数据被更新、删除后,依然可以通过 SQL 接口将更新/删除前的数据读取出来。


另外即使在更新数据之后,表结构发生了变化,TiDB 依旧能用旧的表结构将数据读取出来。


(2)操作流程


为支持读取历史版本数据, 引入了一个新的 system variable: tidb_snapshot ,这个变量是 Session 范围有效,可以通过标准的 Set 语句修改其值。其值为文本,能够存储 TSO 和日期时间。TSO 即是全局授时的时间戳,是从 PD 端获取的; 日期时间的格式可以为: “2020-10-08 16:45:26.999”,一般来说可以只写到秒,比如”2020-10-08 16:45:26”。 当这个变量被设置时,TiDB 会用这个时间戳建立 Snapshot(没有开销,只是创建数据结构),随后所有的 Select 操作都会在这个 Snapshot 上读取数据。


注意:


TiDB 的事务是通过 PD 进行全局授时,所以存储的数据版本也是以 PD 所授时间戳作为版本号。在生成 Snapshot 时,是以 tidb_snapshot 变量的值作为版本号,如果 TiDB Server 所在机器和 PD Server 所在机器的本地时间相差较大,需要以 PD 的时间为准。


当读取历史版本操作结束后,可以结束当前 Session 或者是通过 Set 语句将 tidb_snapshot 变量的值设为 “",即可读取最新版本的数据。


(3)历史数据保留策略


TiDB 使用 MVCC 管理版本,当更新/删除数据时,不会做真正的数据删除,只会添加一个新版本数据,所以可以保留历史数据。历史数据不会全部保留,超过一定时间的历史数据会被彻底删除,以减小空间占用以及避免历史版本过多引入的性能开销。


TiDB 使用周期性运行的 GC(Garbage Collection,垃圾回收)来进行清理,关于 GC 的详细介绍参见 TiDB 垃圾回收 (GC)。


这里需要重点关注的是 tikv_gc_life_time 和 tikv_gc_safe_point 这条。tikv_gc_life_time 用于配置历史版本保留时间,可以手动修改;tikv_gc_safe_point 记录了当前的 safePoint,用户可以安全地使用大于 safePoint 的时间戳创建 snapshot 读取历史版本。safePoint 在每次 GC 开始运行时自动更新。


(4)案例演示


查看当前表数据

45ff17c3b4804032b9e5e3b9cd25731f.jpg



记录时间


587659a4cdbe4c909bc86a41b2c33a5e.jpg

修改数据


a0d76fac6b4d44ccaac29c0182e05cae.jpg

设置历史时间戳,读取历史数据

4d5d3fcc97e64b87a9375a8e0e42abd5.jpg


4.3.TiDB整合Spark-TiSpark

(1)准备数据,向集群中插入样本数据


由于docker-compose安装已经帮我们整合好tispark组件,所以我们无须再次部署。


进入集群内部:docker-compose exec tispark-master bash


进入到tispark目录:cd /opt/spark/data/tispark-sample-data


执行脚本:mysql -h tidb -P 4000 -u root < dss.ddl


fc60da2aacfe49a38bc34d9339353b1b.jpg


(2)启动Spark shell


先退出容器内部执行命令:docker-compose exec tispark-master /opt/spark/bin/spark-shell

1391d46210f04212b3de25835c9fb1de.jpg



(3)执行Spark代码

scala> import org.apache.spark.sql.TiContext
...
scala> val ti = new TiContext(spark)
...
scala> ti.tidbMapDatabase("TPCH_001")
...
scala> spark.sql("select count(*) from lineitem").show
+--------+
|count(1)|
+--------+
|   60175|
+--------+
也可以通过 Python 或 R 来访问 Spark:
docker-compose exec tispark-master /opt/spark/bin/pyspark &&
docker-compose exec tispark-master /opt/spark/bin/sparkR
执行另一个复杂一点的 Spark SQL:
scala> spark.sql(
      """select
        |   l_returnflag,
        |   l_linestatus,
        |   sum(l_quantity) as sum_qty,
        |   sum(l_extendedprice) as sum_base_price,
        |   sum(l_extendedprice * (1 - l_discount)) as sum_disc_price,
        |   sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge,
        |   avg(l_quantity) as avg_qty,
        |   avg(l_extendedprice) as avg_price,
        |   avg(l_discount) as avg_disc,
        |   count(*) as count_order
        |from
        |   lineitem
        |where
        |   l_shipdate <= date '1998-12-01' - interval '90' day
        |group by
        |   l_returnflag,
        |   l_linestatus
        |order by
        |   l_returnflag,
        |   l_linestatus
      """.stripMargin).show
+------------+------------+---------+--------------+--------------+
|l_returnflag|l_linestatus|  sum_qty|sum_base_price|sum_disc_price|
+------------+------------+---------+--------------+--------------+
|           A|           F|380456.00|  532348211.65|505822441.4861|
|           N|           F|  8971.00|   12384801.37| 11798257.2080|
|           N|           O|742802.00| 1041502841.45|989737518.6346|
|           R|           F|381449.00|  534594445.35|507996454.4067|
+------------+------------+---------+--------------+--------------+
-----------------+---------+------------+--------+-----------+
       sum_charge|  avg_qty|   avg_price|avg_disc|count_order|
-----------------+---------+------------+--------+-----------+
 526165934.000839|25.575155|35785.709307|0.050081|      14876|
  12282485.056933|25.778736|35588.509684|0.047759|        348|
1029418531.523350|25.454988|35691.129209|0.049931|      29181|
 528524219.358903|25.597168|35874.006533|0.049828|      14902|
-----------------+---------+------------+--------+-----------+

4.4.数据迁移-TiDB Lightning

(1)TiDB Lightning介绍


TiDB Lightning是一个将全量数据高速导入到TIDB集群的工具,目前支持Mydumper或CSV输出格式的数据源。你可以在以下两种场景下使用Lightning:


迅速导入大量新数据

备份恢复所有数据

TiDB Lightning主要包含两个部分:


tidb-lightning(“前端”):主要完成适配工作,通过读取数据源,在下游TiDB集群建表、将数据转换成键、值对(KV对)发送到tikv-importer、检查数据完整性等。

tikv-importer(“后端”):主要完成对数据导入TiKV集群的工作,把tidb-lightning写入的KV对缓存排序、切分并导入到TiKV集群。

22d76f690c55491c8bee4c100a33e9bc.jpg


da8ed74eca4b46be8c1baa4a2b809be1.jpg



(2)下载迁移工具

wget https://download.pingcap.org/tidb-enterprise-tools-latest-linux-amd64.tar.gz
wget https://download.pingcap.org/tidb-toolkit-latest-linux-amd64.tar.gz

(3)准备mysql数据


5.TiDB技术原理

5.1.TiDB存储原理

(1)key-value


作为保存数据的系统,首先要决定的是数据的存储模型,就是数据以什么样的方式保存下来。TiKV的选择是Key-Value模型,并且提供有序遍历方法。简单来说,可以将TiDB看做一个巨大的Map,其中Key和Value都是原始的Byte数组,在这个Map中。Key按照Byte数组总的原始二进制比特位比较顺序排序。


对于TiDB的理解只需要记住两点:


这是一个巨大的Map,也就是存储的是Key-Value pair。

这个Map中的Key-Value pair按照Key的二进制顺序有序,也就是我们可以Seek到某一个Key的位置,然后不断的调用Next方法以递增的顺序获取比这个Key大的Key-Value。

(2)RocksDB


任何持久化的存储引擎,数据终归要保存在磁盘上,TiKV也不例外,但是TiKV没有直接选择向磁盘上写数据,而是把数据保存在RocksDB中,具体的数据落地由RocksDB负责。这个选择的原因是开发一个单机存储引擎工作量很大,高性能的单机存储引擎。这里可以简单的认为RocksDB是一个单机的Key-Value Map。


底层LSM树将对数据的修改增量保存在内存中,达到指定大小后批量把数据flush到磁盘中。


(3)Raft


RocksDB保证单机的高性能,但是怎么保证数据的可靠性呢,Raft是一个一致性协议,提供几个重要功能:


Leader选举

成员变更

日志复制

TiKV利用Raft来做数据复制,每个数据变更都会落地为一条Raft日志,通过Raft的日志复制功能,将数据安全可靠的同步到Group的多数节点上。


688acca69b834385b6f5ea78bf01d6e2.jpg



RocksDB解决数据快速的存储在磁盘上,通过Raft,我们可以将数据复制到多台机器上,以防止单机失效。数据的写入是通过Raft这一层的接口写入,而不是直接写RocksDB。通过实现Raft,我们拥有一个分


(4)Region


TiKV相当于一个巨大的有序的KV Map,为了实现存储的水平扩展,我们需要将数据分散在多台机器上。Region分散有两种方案:一种是按照Key做Hash,根据Hash值选择对应的存储节点,另一种是分Range,某一段连续的Key都保存在一个存储节点上。TiKV选择了第二种方式,将整个key-value空间分成很多段,每一段是一些列连续的key,我们将每一段叫做一个Region,并且我们会尽量保持每个Region都可以用到StartKey到EndKey这样一个左闭右开的区间来描述。


2a63d5669a044c6d93432d663174d2e4.jpg


将数据划分成Region后,我们将会做两件事:


以Region为单位,将数据分散在集群中所有节点上,并且尽量保证每个节点上服务的Region数量差不多。

以Region为单位做Raft的复制和成员管理。

先看第一点,数据按照Key切分成很多Region,每个Region的数据只会保存在一个节点上面。我们的系统会有一个组件将Region尽可能均匀的散布在及群众所有节点上,这样一方面实现了存储容量的水平扩展,另一方面也实现了负载均衡。通过任意一个key就能查到这个key在哪个Region中,以及这个Region目前在哪个节点上。


第二点,TiKV是以Region为单位做数据的复制,也就是一个Region的数据会保存多个副本,我们将一个副本叫做一个Replica。Replica之间是通过Raft来保持数据的一致,一个Region的多个Replica会保存在不同的节点上,构成一个Raft Group。其中一个Replica回座位这个Group的Leader,其他的Replica作为Follower。所有的读和写都是通过Leader进行,再由Leader复制给Follower。


5e1e7522e175478ba38fcefd4b491445.jpg


我们以Region为单位做数据的分散和复制,就有了一个分布式的具备一定容灾能力的KeyValue系统,不用担心数据存不下,或者磁盘故障丢失数据的问题。

(5)MVCC


很多数据库都会实现多版本控制(MVCC),TiKV也不例外。假设这样的场景,两个Client同事去修改一个Key,如果没有MVCC,就需要对数据上锁,在分布式场景下,可能会带来性能以及死锁的问题。TiKV的MVCC实现是通过在Key后面添加Version来实现。

38f549a77b894643896526febb00bb2e.jpg



注意,对于同一个Key的多个版本,我们把版本号较大的放在前面,版本号小的放在后面,这样当用户通过一个Key+Version来获取value的时候,可以将Key和Version构造出MVCC的Key,也就是Key-Version。然后可以直接Seek(Key-Version),定位到第一个大于等于这个Key-Version的位置。


(6)事务


TiKV的事务采用的是Percolator模型,并且做了大量的优化。TiKV的事务采用乐观锁,事务的执行过程中,不会检测写写冲突,只有在提交过程中,才会做冲突检测,冲突的双方中比较早完成提交的会写入成功,另一方面尝试重新执行整个事务。当业务的写入冲突不严重的情况下,这种模型性能会很好,比如随机更新表中某一行的数据,并且表很大。但是如果业务的写入冲突严重,性能就会很差,举一个极端的例子,多个客户端修改少量行,导致冲突严重,造成大量的无效重试。


5.2.TiDB计算原理

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


我们将关系模型简单的理解为Table和SQL语句,那么问题变为如何在KV结构上保存Table以及如何在KV结构上运行SQL语句。


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


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


表的元数据

Table中的Row

索引数据

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


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


查询的方式分为两种:点查和范围查询。


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


TiDB对每一个表分配一个TableID,每一个索引都会分配一个IndexID,每一行分配一个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}_indexedColumsValue

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

假设表中有 3 行数据:


1, “TiDB”, “SQL Layer”, 10

2, “TiKV”, “KV Engine”, 20

3, “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)原信息管理


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 架构

8f5b706404ae420d8a0bb53f98126385.jpg



TiKV Cluster主要作用是作为KV引擎存储数据,这里主要介绍SQL层,也就是TiDB Servers这一层,这一层的节点都是无状态的节点,本身并不存储数据,节点之间完全对等。TiDB Server这一层最重要的工作是处理用户请求,执行SQL运算逻辑。


(3)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 的很好,原因是显而易见的:


在扫描数据的时候,每一行都要通过 KV 操作同 TiKV 中读取出来,至少有一次 RPC 开销,如果需要扫描的数据很多,那么这个开销会非常大


并不是所有的行都有用,如果不满足条件,其实可以不读取出来


符合要求的行的值并没有什么意义,实际上这里只需要有几行数据这个信息就行


(4)分布式 SQL 运算


如何避免上述缺陷也是显而易见的,首先我们需要将计算尽量靠近存储节点,以避免大量的 RPC 调用。其次,我们需要将 Filter 也下推到存储节点进行计算,这样只需要返回有效的行,避免无意义的网络传输。最后,我们可以将聚合函数、GroupBy 也下推到存储节点,进行预聚合,每个节点只需要返回一个 Count 值即可,再由 tidb-server 将 Count 值 Sum 起来。


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

bc8bf628d97a4d36a09c56121c537803.jpg



(5)SQL 层架构


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

886822506ee14f0abf0ec962906392d1.jpg



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


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


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


5.3.TiDB调度原理

(1)调度的基本操作


调度无非是下面三件事:


增加一个 Replica

删除一个 Replica

将 Leader 角色在一个 Raft Group 的不同 Replica 之间 transfer

刚好 Raft 协议能够满足这三种需求,通过 AddReplica、RemoveReplica、TransferLeader 这三个命令,可以支撑上述三种基本操作。


(2)信息收集


调度依赖于整个集群信息的收集,简单来说,我们需要知道每个 TiKV 节点的状态以及每个 Region 的状态。TiKV 集群会向 PD 汇报两类消息:


每个 TiKV 节点会定期向 PD 汇报节点的整体信息


TiKV 节点(Store)与 PD 之间存在心跳包,一方面 PD 通过心跳包检测每个 Store 是否存活,以及是否有新加入的 Store;另一方面,心跳包中也会携带这个 Store 的状态信息,主要包括:


总磁盘容量

可用磁盘容量

承载的 Region 数量

数据写入速度

发送/接受的 Snapshot 数量(Replica 之间可能会通过 Snapshot 同步数据)

是否过载

标签信息(标签是具备层级关系的一系列 Tag)

每个 Raft Group 的 Leader 会定期向 PD 汇报信息


每个 Raft Group 的 Leader 和 PD 之间存在心跳包,用于汇报这个 Region 的状态,主要包括下面几点信息:


Leader 的位置

Followers 的位置

掉线 Replica 的个数

数据写入/读取的速度

PD 不断的通过这两类心跳消息收集整个集群的信息,再以这些信息作为决策的依据。除此之外,PD 还可以通过管理接口接受额外的信息,用来做更准确的决策。比如当某个 Store 的心跳包中断的时候,PD 并不能判断这个节点是临时失效还是永久失效,只能经过一段时间的等待(默认是 30 分钟),如果一直没有心跳包,就认为是 Store 已经下线,再决定需要将这个 Store 上面的 Region 都调度走。但是有的时候,是运维人员主动将某台机器下线,这个时候,可以通过 PD 的管理接口通知 PD 该 Store 不可用,PD 就可以马上判断需要将这个 Store 上面的 Region 都调度走。


(3)调度的策略


PD 收集了这些信息后,还需要一些策略来制定具体的调度计划。


1.一个 Region的 Replica数量正确


当 PD 通过某个 Region Leader 的心跳包发现这个 Region 的 Replica 数量不满足要求时,需要通过 Add/Remove Replica 操作调整 Replica 数量。出现这种情况的可能原因是:


某个节点掉线,上面的数据全部丢失,导致一些 Region 的 Replica 数量不足

某个掉线节点又恢复服务,自动接入集群,这样之前已经补足了 Replica 的 Region 的 Replica 数量多过,需要删除某个 Replica

管理员调整了副本策略,修改了 max-replicas 的配置

2.一个 Raft Group中的多个 Replica不在同一个位置


注意第二点,『一个 Raft Group 中的多个 Replica 不在同一个位置』,这里用的是『同一个位置』而不是『同一个节点』。在一般情况下,PD 只会保证多个 Replica 不落在一个节点上,以避免单个节点失效导致多个 Replica 丢失。在实际部署中,还可能出现下面这些需求:


多个节点部署在同一台物理机器上

TiKV 节点分布在多个机架上,希望单个机架掉电时,也能保证系统可用性

TiKV 节点分布在多个 IDC 中,希望单个机房掉电时,也能保证系统可用

这些需求本质上都是某一个节点具备共同的位置属性,构成一个最小的容错单元,我们希望这个单元内部不会存在一个 Region 的多个 Replica。这个时候,可以给节点配置 lables 并且通过在 PD 上配置 location-labels 来指明哪些 lable 是位置标识,需要在 Replica 分配的时候尽量保证不会有一个 Region 的多个 Replica 所在结点有相同的位置标识。


3.副本在 Store 之间的分布均匀分配


前面说过,每个副本中存储的数据容量上限是固定的,所以我们维持每个节点上面,副本数量的均衡,会使得总体的负载更均衡。


4.Leader数量在 Store之间均匀分配


Raft 协议要读取和写入都通过 Leader 进行,所以计算的负载主要在 Leader 上面,PD 会尽可能将 Leader 在节点间分散开。


5.访问热点数量在 Store之间均匀分配


每个 Store 以及 Region Leader 在上报信息时携带了当前访问负载的信息,比如 Key 的读取/写入速度。PD 会检测出访问热点,且将其在节点之间分散开。


6.各个 Store的存储空间占用大致相等


每个 Store 启动的时候都会指定一个 Capacity 参数,表明这个 Store 的存储空间上限,PD 在做调度的时候,会考虑节点的存储空间剩余量。


7.控制调度速度,避免影响在线服务

调度操作需要耗费 CPU、内存、磁盘 IO 以及网络带宽,我们需要避免对线上服务造成太大影响。PD 会对当前正在进行的操作数量进行控制,默认的速度控制是比较保守的,如果希望加快调度(比如已经停服务升级,增加新节点,希望尽快调度),那么可以通过 pd-ctl 手动加快调度速度。


8.支持手动下线节点


当通过 pd-ctl 手动下线节点后,PD 会在一定的速率控制下,将节点上的数据调度走。当调度完成后,就会将这个节点置为下线状态。


(4)调度的实现


了解了上面这些信息后,接下来我们看一下整个调度的流程。


PD 不断的通过 Store 或者 Leader 的心跳包收集信息,获得整个集群的详细数据,并且根据这些信息以及调度策略生成调度操作序列,每次收到 Region Leader 发来的心跳包时,PD 都会检查是否有对这个 Region 待进行的操作,通过心跳包的回复消息,将需要进行的操作返回给 Region Leader,并在后面的心跳包中监测执行结果。注意这里的操作只是给 Region Leader 的建议,并不保证一定能得到执行,具体是否会执行以及什么时候执行,由 Region Leader 自己根据当前自身状态来定。

相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps&nbsp;
相关文章
|
1月前
|
数据采集 大数据
大数据实战项目之电商数仓(二)
大数据实战项目之电商数仓(二)
|
6天前
|
存储 Java 分布式数据库
使用Spring Boot和HBase实现大数据存储
使用Spring Boot和HBase实现大数据存储
|
27天前
|
存储 分布式计算 OLAP
Apache Paimon统一大数据湖存储底座
Apache Paimon,始于Flink Table Store,发展为独立的Apache顶级项目,专注流式数据湖存储。它提供统一存储底座,支持流、批、OLAP,优化了CDC入湖、流式链路构建和极速OLAP查询。Paimon社区快速增长,集成Flink、Spark等计算引擎,阿里巴巴在内部广泛应用,旨在打造统一湖存储,打通Serverless Flink、MaxCompute等,欢迎大家扫码参与体验阿里云上的 Flink+Paimon 的流批一体服务。
13616 0
Apache Paimon统一大数据湖存储底座
|
12天前
|
存储 弹性计算 大数据
阿里云ECS以其强大的弹性计算与存储能力,为大数据处理提供了灵活、高效、成本优化的解决方案
阿里云ECS在大数据处理中发挥关键作用,提供多样化实例规格适应不同需求,如大数据型实例适合离线计算。ECS与OSS集成实现大规模存储,通过Auto Scaling动态调整资源,确保高效运算。案例显示,使用ECS处理TB级数据,速度提升3倍,成本降低40%,展现其在弹性、效率和成本优化方面的优势。结合阿里云生态系统,ECS助力企业数据驱动创新。
28 1
|
21天前
|
存储 SQL 分布式计算
MaxCompute产品使用问题之如何查看项目空间耗用的存储大小
MaxCompute作为一款全面的大数据处理平台,广泛应用于各类大数据分析、数据挖掘、BI及机器学习场景。掌握其核心功能、熟练操作流程、遵循最佳实践,可以帮助用户高效、安全地管理和利用海量数据。以下是一个关于MaxCompute产品使用的合集,涵盖了其核心功能、应用场景、操作流程以及最佳实践等内容。
|
21天前
|
存储 分布式计算 大数据
Hadoop 生态圈中的组件如何协同工作来实现大数据处理的全流程
Hadoop 生态圈中的组件如何协同工作来实现大数据处理的全流程
|
1月前
|
存储 分布式计算 DataWorks
MaxCompute产品使用合集之要存储用户的下单所有产品,然后查询时要进行产品分组的,一般这种字段要使用ARRAY还是MAP
MaxCompute作为一款全面的大数据处理平台,广泛应用于各类大数据分析、数据挖掘、BI及机器学习场景。掌握其核心功能、熟练操作流程、遵循最佳实践,可以帮助用户高效、安全地管理和利用海量数据。以下是一个关于MaxCompute产品使用的合集,涵盖了其核心功能、应用场景、操作流程以及最佳实践等内容。
|
1月前
|
存储 分布式计算 大数据
MaxCompute产品使用合集之是否支持创建OSS外部表为分区表,并访问OSS上以分区方式存储的数据
MaxCompute作为一款全面的大数据处理平台,广泛应用于各类大数据分析、数据挖掘、BI及机器学习场景。掌握其核心功能、熟练操作流程、遵循最佳实践,可以帮助用户高效、安全地管理和利用海量数据。以下是一个关于MaxCompute产品使用的合集,涵盖了其核心功能、应用场景、操作流程以及最佳实践等内容。
|
1月前
|
存储 大数据 分布式数据库
使用Apache HBase进行大数据存储:技术解析与实践
【6月更文挑战第7天】Apache HBase,一个基于HDFS的列式存储NoSQL数据库,提供高可靠、高性能的大数据存储。其特点是列式存储、可扩展至PB级数据、低延迟读写及多版本控制。适用场景包括大规模数据存储、实时分析、日志存储和推荐系统。实践包括集群环境搭建、数据模型设计、导入、查询及性能优化。HBase在大数据存储领域扮演关键角色,未来有望在更多领域发挥作用。
|
21天前
|
存储 机器学习/深度学习 分布式计算
MaxCompute产品使用问题之如何使用分层存储
MaxCompute作为一款全面的大数据处理平台,广泛应用于各类大数据分析、数据挖掘、BI及机器学习场景。掌握其核心功能、熟练操作流程、遵循最佳实践,可以帮助用户高效、安全地管理和利用海量数据。以下是一个关于MaxCompute产品使用的合集,涵盖了其核心功能、应用场景、操作流程以及最佳实践等内容。