黄东旭解析 TiDB 的核心优势
529
2024-02-23
本系列学习笔记根据官方课程《TiDB 高级系统管理 [TiDB v5]》整理,感谢官方精心制作的视频课程。相关课程介绍,详见官方课程链接:https://learn.pingcap.com/learner/course/120005
TiDB Server 是 TiDB 数据库的计算节点,主要提供如下功能:
处理客户端的连接(MySQL 协议);
SQL 语句的解析、编译;
关系型数据与 KV 的转化;
SQL 语句的执行
在线 DDL 语句执行;
MVCC(多版本并发控制)垃圾回收;
【注意】
TiDB Server 本身并不存储数据,只是解析 SQL,将实际的数据读取请求转发给底层的存储节点 TiKV(或 TiFlash)。
TiDB 层本身是无状态的,实践中可以启动多个 TiDB 实例,通过负载均衡组件(如 LVS、HAProxy或 F5)对外提供统一的接入地址,客户端的连接可以均匀地分布在多个 TiDB 实例上以达到负载均衡的效果。
TiKV 是 TiDB 数据库的存储节点,主要提供如下功能:
数据持久化。
TiKV 在内部选择了基于 LSM-Tree 结构的 RocksDB 引擎(是由 Facebook 开源的一个非常优秀的单机 KV 存储引擎)作为数据存储引擎。可以简单的认为 RocksDB 是一个单机的持久化 Key-Value Map。
分布式事务支持。
TiKV 的事务采用的是 Google 在 BigTable 中使用的两阶段提交事务模型:Percolator。在每个 TiKV 节点上会单独分配存储锁的空间,称为 CF Lock。
副本的强一致性和高可用性。
采用 Multi-Raft 机制实现了多副本的强一致和高可用。TiKV 中存储的 Region 会自动分裂与合并,实现动态扩展,并借助 PD 节点可使 Region 在 TiKV 节点中灵活调度。
MVCC(多版本并发控制)。
Coprocessor(协同处理器)。
将 SQL 中的一部分计算利用协同处理器,下推到 TiKV 节点中完成(即算子下推)。充分利用 TiKV 节点的计算资源实现并行计算,从而减轻 TiDB Server 节点的计算压力。TiKV 节点将初步计算后的数据返回给 TiDB Server 节点,减少了网络带宽的占用。
PD 是整个 TiDB 集群的控制中枢,是 TiDB 数据库的元信息管理模块。提供如下功能:
TiKV 的元数据存储(即 Region 分布),方便 TiDB 知道数据位于哪个 TiKV 节点;
集群整体拓扑结构的存储;
分配全局 ID 和事务 ID。
生成全局 TSO 时间戳;
收集集群信息进行调度;
提供 TiDB Dashboard 服务。
此外,PD 本身也是由至少 3 个节点构成,拥有高可用的能力。建议部署奇数个 PD 节点。
TiFlash 是 TIDB 数据库的列式存储节点,提供如下功能:
列式存储,提高分析查询效率;
支持强一致性和实时性
业务隔离,即 OLTP 与 OLAP 隔离
智能选择
TiFlash 节点以 Leaner 角色加入到 Multi-Raft 体系中,从而实现了对 TiKV 数据的准实时更新和强一致性。
Protocol Layer、Parse、Compile
Protocol Layer 提供 MySQL 兼容的协议,处理客户端的连接;将接收到的 SQL 语句交由 Parse 模块进行解析后,并交由 Compile 模块进行编译,并生成执行计划。
Executor、DistSQL
Executor、DistSQL 负责 SQL 执行计划的执行;对于可直接返回结果的 SQL 语句(点查),由Executor 和 KV 直接执行;而复杂的 SQL 语句由 Executor 交给 DistSQL 来执行。
Transaction
Transaction 则负责与事务相关的任务。
PD Client、TiKV Client
PD Client 负责与 PD 实例的交互;TiKV Client 则负责与 TiKV 实例的交互。
Schema load、Worker、Start job
schema load、worker、start job 则负责在线 ddl。
GC
GC 负责 MVCC 的垃圾回收
如图1.3所示。首先,通过 lex(词法分析)将 SQL 语句解析成多个 token;再根据 token,通过yacc(语法分析)生成抽象语法树(AST),以便于 compile 模块进行优化。
1.2.2.2. SQL Compile 编译Compile 接收到 AST 后,如图1.4所示。首先根据元数据进行合法性验证(如对象是否存在),然后进行逻辑优化(即 SQL 语句优化,如等价改写),再结合统计信息进行物理优化(如是否走索引等),最后生成执行计划。
聚簇表(Clustered Table)中,行数据的物理存储顺序与主键的存储顺序一致,访问主键时可直接获取到行记录。表的主键即为 KV 映射中 Key 的一部分。如图1.5所示,以 “编号” 列为主键的聚簇表为例,简单介绍关系性数据与 KV 的转化:
【知识补充】
聚簇表的特点:
表中行数据的物理存储顺序与主键的存储顺序一致;
通过主键访问行数据时,可以直接获取行数据,即只需 1 次 I/O;
创建方法:“CREATE TABLE t(a BIGINT PRIMARY KEY CLUSTERED , b VARCHAR(255);”
聚簇表(Clustered Table)在 TiDB 文档中又称聚簇索引表(index-organized tables),在 *** 中称为索引组织表(IOT,Index-Organized Tables)。
首先,提取聚簇表主键列“编号”为 KEY 值;
为了使 KEY 值在 TiDB 数据库中全局唯一,由 TiDB 自动为 KEY 追加“TableID”2前缀;
TableID与主键列共同构成 KEY 值,而表行的其它字段则成为该 KEY 值对应的 VALUE 值。如 KEY 值“T24_r1”对应的 VALUE 值为“Tom,1982-09-28,1390811212,78”。
聚簇表的 KV 映射规则:
Key:tablePrefix{TableID}_recordPrefixSep{Col1}。即假设 Col1 为 Cluster Index,Key 为 TableID 与索引键组合而成。
Value:[col2,col3,col4]。即 Value 由行数据中除了主键列的其它列组成。
1.2.3.2. 非聚簇表3的 KV 转化非聚簇表(Non-clustered Table)中,行数据的物理存储顺序与主键(主键本质上是唯一索引)的存储顺序无关。在对非聚簇表做 KV 转化时,TiDB 会隐式为每行数据生成 RowID(是一个 Int64 整型)。TableID 与 RowID 组合成 KV 键值对的 KEY 值。具体的 KV 转化流程(如图1.6所示)如下:
首先,TiDB 隐式为非聚簇表的每行生成一个 int64 类型的 RowID,并将该 RowID 作为行数据的 KEY 值;若表中含有整型列的主键,则 TiDB 默认将主键列作为每行的 RowID;
为了使 KEY 值在 TiDB 数据库中全局唯一,由 TiDB 自动为 KEY 追加“TableID”前缀;
TableID与 RowID共同构成 KEY 值,而表行的所有字段都成为该 KEY 值对应的 VALUE 值。如 KEY 值 ‘’T24_xx1’’ 对应的 VALUE 值为 “1,Tom,1982-09-28,1390811212,78”。
非聚簇表的 KV 映射规则:
Key:tablePrefix{TableID}_recordPrefixSep{_Tidb_RowID}。即 Key 由 TableID 与 RowID 组合而成。
Value:[col1,col2,col3,col4]。即 Value 为真实的行数据。
【知识补充】
TableID:TableID 在 TiDB 数据库中全局唯一,可理解为 *** 的 ObjectID非聚簇表的特点:
表中行数据的物理存储顺序与主键(主键本质上是唯一索引)的存储顺序无关;
通过主键访问非聚簇表的行数据时,无法直接获取行数据,需要 2 次 I/O(第一次扫主键索引,获取数据行的 RowID;第二次通过 RowID 回表获取行数据);
创建方法:CREATE TABLE t(a BIGINT PRIMARY KEY NONCLUSTERED ,b VARCHAR(255));
1.2.3.3. Region 分裂Region 是 TiDB 数据存储管理的基本单位。多组连续的“KV 键值对”构成一个 Region,存储在TiKV 实例的 RocksDB 中。
每个 Region 默认大小为 96MB。当一个 Region 达到 144MB 时,会自动分裂成两个 Region(如下图所示);
每个 Region 按左闭右开的区间划分数据存储范围,如 [a d)、[d g);
如图1.8所示,SQL 读写相关模块的协作流程如下:
Executor 拿到执行计划后,根据 SQL 语句的类型,分别交给不同模块执行;
当 SQL 语句为简单的点查(PointGet,根据主键或索引,只查 1 行或 0 行),直接交给 KV 执行;
当 SQL 语句为复杂的查询(如多表连结、嵌套查询等),则交给 DistSQL 来执行;DistSQL 模块将多表查询拆分成多个单表查询的操作。
若 SQL 语句包含事务,则 Executor 将启动 Transaction 模块。Transaction 模块负责两阶段提交(PreWrite、Commit)和锁管理等。
Transaction 模块通过 PD Client 向 PD 节点申请全局事务 ID、TSO(全局时间戳)类型的事务开始时间戳 start_ts 和提交时间戳 commit_ts,再调用 KV 模块或 DistSQL 模块。
最终的数据读写,通过调用 TiKV Client 模块,来完成与 TiKV 节点的读写交互。
如图1.9所示,在线 DDL 相关模块的协作流程如下:
start job 模块负责接受 ddl 请求,并将其存入 TiKV 节点的 job queue 队列中;
workers 模块负责读取 TiKV 节点的 job queue 队列,按序执行队列中的 ddl 操作,并将执行完毕的 ddl 存入 history queue 队列中;
schema load 模块负责加载 schema 元数据。
多个 TiDB Server 中的 start job 可同时接受多个 ddl 请求。但是,只有角色为 Owner 的 TiDBServer 中的 workers 模块可以执行 ddl。
同一时刻,只有一个 TiDB Server 角色为 Owner;Owner 角色定期在多个 TIDB Server 节点中轮换(重选举)。
成为 Owner 的 TiDB Server 节点,首先会通过 schema load 模块来加载 schema 元数据。
TiDB 的事务的实现采用了 MVCC(多版本并发控制)机制,当新写入的数据覆盖旧数据时,旧数据不会被替换掉,而是与新写入的数据同时保留,并以时间戳来区分版本,GC 的任务便是周期性地清理不再需要的旧数据。
1.2.6.1. GC LeaderTiDB 集群会从众多 TiDB Server 实例中选举一个作为 GC Leader,而 GC 的工作由 GC Leader 中的 GC Worker 模块来处理4。其他 TiDB Server 上的 GC Worker 是不工作的。
【知识补充】
GC Worker 模块:维护 GC 状态并向所有的 TiKV Region leader 发送 GC 命令。
【注意】
选举 GC Leader 的方式为 GC Worker 每分钟 Tick 时,如果发现没有 Leader或 Leader 失效,就把自己写进去,成为 GC Leader。
1.2.6.2. GC 流程每一轮 GC 分为以下三个步骤(串行执行):
Resolve Locks:该阶段会对所有 Region 扫描 Safepoint 5 之前的锁,并清理这些锁。
Delete Ranges:该阶段快速地删除由于 DROP TABLE/DROP INDEX 等操作产生的整区间(Region)的废弃数据。
Do GC:该阶段每个 TiKV 节点将会各自扫描该节点上的数据,并对每一个 key 删除其不再需要的旧版本。
【知识补充】
Safepoint:每次 GC 时,首先 TiDB 会计算一个称为 Safepoint 的时间戳,接下来 TiDB 会在保证 Safepoint 之后的快照全部拥有正确数据的前提下,删除更早的过期数据。
【注意】
如果一轮 GC 运行时间太久,上轮 GC 还在前两个阶段,下轮 GC 又开始了,下一轮 GC 会忽略,GC Leader 会报“there’s already a gc job running,skipped”:
默认配置下,GC 每 10 分钟触发一次,每次 GC 会保留最近 10 分钟内的数据(即默认 GC LifeTime 为 10 分钟)。
【注意】
为了使持续时间较长的事务能在超过 GC Life Time 之后仍然可以正常运行,Safepoint 不会大于正在执行中的事务的开始时间 (start_ts)。
1.2.6.3. GC 实现细节1.2.6.3.1. Resolve Locks(清理锁)TiDB 的事务是基于 Google Percolator 模型实现的,事务的提交是一个两阶段提交的过程。第一阶段完成时,所有涉及的 key 都会上锁,其中一个锁会被选为 Primary,其余的锁 ( Secondary ) 则会存储一个指向 Primary 的指针;第二阶段会将 Primary 锁所在的 key 加上一个 Write 记录,并去除锁。如果因为某些原因(如发生故障等),这些 Secondary 锁没有完成替换、残留了下来,那么也可以根据锁中的信息找到 Primary,并根据 Primary 是否提交来判断整个事务是否提交。但是,如果 Primary的信息在 GC 中被删除了,而该事务又存在未成功提交的 Secondary 锁,那么就永远无法得知该锁是否可以提交。这样,数据的正确性就无法保证。Resolve Locks 这一步的任务即对 Safepoint 之前的锁进行清理。即如果一个锁对应的 Primary 已经提交,那么该锁也应该被提交;反之,则应该回滚。而如果 Primary 仍然是上锁的状态(没有提交也没有回滚),则应当将该事务视为超时失败而回滚。Resolve Locks 的执行方式是由 GC leader 对所有的 Region 发送请求扫描过期的锁,并对扫到的锁查询 Primary 的状态,再发送请求对其进行提交或回滚。从 3.0 版本开始,Resolve Locks 实现了并行,把所有 Region 分配给各个线程,所有线程并行的向各个 Region 的 Leader 发送请求:
并发线程数若 tikv_gc_auto_concurrency = 1,则每个 TiKV 自动一个线程。若 tikv_gc_auto_concurrency = 0,则由 tikv_gc_concurrency 决定总线程数,但每个 tikv 最多一个线程。
实际清锁的操作,是调用了 RocksDB 的 Delete ,RocksDB 的内部实现原理是写一个删除标记,需要等 RocksDB 执行 Compaction 回收空间,通常这步骤涉及的数据非常少。
1.2.6.3.2. Delete Ranges(删除区间)在执行 Drop/Truncate Table ,Drop Index 等操作时,会有大量连续的数据被删除。如果对每个key 都进行删除操作、再对每个 key 进行 GC 的话,那么执行效率和空间回收速度都可能非常的低下。事实上,这种时候 TiDB 并不会对每个 key 进行删除操作,而是将这些待删除的区间及删除操作的时间戳记录下来。Delete Ranges 会将这些时间戳在 Safepoint 之前的区间进行快速的物理删除,而普通DML 的多版本不在这个阶段回收。
TiKV 默认使用 RocksDB 的 UnsafeDestroyRange 接口
Drop/Truncate Table ,Drop Index 会先把 Ranges 写进 TiDB 系统表(mysql.gc_delete_range),TiDB 的 GC worker 定期查看是否过了 Safepoint,然后拿出这些 Ranges,并发的给 TiKV 去删除 sst文件,并发数和 concurrency 无关,而是直接发给各个 TiKV。删除是直接删除,不需要等 compact 。完成 Delete Ranges 后,会记录在 TiDB 系统表 mysql.gc_delete_range_done,表中的内容过 24 小时后会清除:
mysql> select * from mysql.gc_delete_range_done ; +1.2.6.3.3. Do GC(进行 GC 清理)此步操作主要是清理因 DML 操作而产生的过期版本数据。为了保证 Safepoint 之后的任何时间戳都具有一致的快照,这一步删除 Safepoint 之前提交的数据,但是会对每个 key 保留 Safepoint 前的最后一次写入(除非最后一次写入是删除)。
在进行这一步时,TiDB 只需将 Safepoint 发送给 PD,即可结束整轮 GC。TiKV 会每分钟自行检测是否 Safepoint 发生了更新,然后会对当前节点上所有 Region Leader 进行 GC。与此同时,GC Leader 可以继续触发下一轮 GC。详细流程如下:
调用 RocksDB 的 Delete 接口,打一个删除标记。
每一轮 GC 都会扫所有的 Region,但会根据 sst 上的元信息初步判断是否有较多的历史数据,进而来判断是否可以跳过。如果增量数据比较大,表示要打标记的老版本较多,会大幅增加耗时。
GC 打完标记后,不会立即释放空间,最终通过 RocksDB Compaction 来真正回收空间。
如果这时 TiKV 进程挂掉了,重启后,需要等下一轮 GC 开始继续。
并行度。3.0 开始,默认设置 tikv_gc_mode = distributed,无需 TiDB 通过对 TiKV 发送请求的方式来驱动,而是 TiDB 只需在每个 GC 周期发送 safepoint 到 PD 就可以结束整轮 GC,每台 TiKV 会自行去 PD 获取 safepoint 后分布式处理。
由于通常 Do GC 比较慢,下一轮 interval 到来时,上一轮 GC 还没有跑完。3.0 开始,如果没有执行完,下一轮 GC 将新的 safepoint 更新到 PD 后,TiKV 每隔 1 分钟到 PD 获取新的 safepoint,获取后会使用新的 safepoint 将剩余的 Region 完成扫描,并尽量回头完成 100% 的 Region (TiKV 会将执行到的 Region 位置在内存中,并按 Region 顺序扫描所有 Region )。
1.2.6.4. GC 相关的配置项mysql> select VARIABLE_NAME, VARIABLE_VALUE from mysql.tidb; +--------------------------+--------------------------------------------------------------------------------------------------------+ | VARIABLE_NAME | VARIABLE_VALUE | +--------------------------+--------------------------------------------------------------------------------------------------------+ | bootstrapped | True | | tidb_server_version | 91 | | system_tz | America/New_York | | new_collation_enabled | False | | tikv_gc_leader_uuid | 609230f72ec0002 | | tikv_gc_leader_desc | host:localhost.localdomain, pid:31283, start at 2022-07-29 15:02:51.839012244 +0800 CST m=+0.337013955 | | tikv_gc_leader_lease | 20220824-13:18:51 +0800 | | tikv_gc_enable | true | | tikv_gc_run_interval | 10m0s | | tikv_gc_life_time | 10m0s | | tikv_gc_last_run_time | 20220824-13:16:51 +0800 | | tikv_gc_safe_point | 20220824-13:06:51 +0800 | | tikv_gc_auto_concurrency | true | | tikv_gc_scan_lock_mode | legacy | | tikv_gc_mode | distributed | +--------------------------+--------------------------------------------------------------------------------------------------------+ 15 rows in set (0.01 sec)其中,tikv_gc_run_interval,tikv_gc_life_time,tikv_gc_auto_concurrency 这三条记录可以手动配置。其余带有 tikv_gc 前缀的记录为当前运行状态的记录,TiDB 会自动更新这些记录,请勿手动修改。
TiDB Server 缓存组成: SQL 结果、线程缓存、元数据、统计信息。
TiDB Server 缓存相关的参数:
mem-quota-query
单条 SQL 语句可以占用的最大内存阈值,单位为字节。该值作为系统变量 tidb_mem_quota_query的初始值。超过该值的请求会被 oom-action 定义的行为所处理。
tidb_mem_quota_query
这个变量用来设置一条查询语句的内存使用阈值。如果一条查询语句执行过程中使用的内存空间超过该阈值,会触发 TiDB 启动配置文件中 OOMAc-tion 项所指定的行为。该变量的初始值由配置项 mem-quota-query 配置。
oom-action
当 TiDB 中单条 SQL 的内存使用超出 mem-quota-query 限制且不能再利用临时磁盘时的行为。目前合法的选项为”log”、”cancel”(默认值)。设置为”log” 时,仅输出日志。设置为”cancel” 时,取消执行该 SQL 操作,并输出日志。其他详细的配置参数,详见“第六章 TiDB 数据库系统优化”。
Placement Driver (简称 PD) 是 TiDB 集群数据库的总控节点,负责整个集群的调度,负责全局 ID的生成,以及全局时间戳 TSO 的生成等。PD 还保存着整个集群 TiKV 的元信息,负责给 client 提供路由功能。
作为总控节点,PD 集群由多个(通常为 3 个)PD 实例构成,其中的 Leader 实例对外提供服务。PD 通过集成 etcd ,实现 PD 高可用和元数据存储,自动支持 auto failover,无需担心单点故障问题。同时,PD 通过 Raft 协议,保证了多个 PD 实例 etcd 数据的强一致性,不用担心数据丢失的问题。
在架构层面,PD 所有的数据都是通过 TiKV 主动上报获知。同时,PD 对整个 TiKV 集群的调度等操作,也只会在 TiKV 发送 Heartbeat 命令的结果里面返回相关的命令,让 TiKV 自行处理,而不是主动给 TiKV 发送命令。可以认为 PD 是一个无状态的服务。
PD(Placement Driver)主要功能
存储集群的元信息(metadata),即某个 Key 存储在哪个 TiKV 实例的哪个 Region;
分配全局 ID(如 tableid、indexid 等)和事务 ID
生成全局时间戳 TSO,如事务的开始时间与结束时间
收集集群信息进行调度,即收集各个 TiKV 实例中 Region 的分布情况,并对其进行调度。
提供 label 功能,实现相关的高可用,即通过 label,使 Region 可实现更合理的高可用隔离性。
提供 TiDB Dashboard 服务
PD 存储了整个集群 TiKV 的元数据(即 Region 的分布),因此可为 TiDB Server 的 Executor 提供Region 的路由功能(如图1.29所示):
TiDB Server 实例的 Executor 执行器将执行计划的请求发送给 TiKV Client 模块;
TiKV Client 模块通过 PD Client 模块,到 PD Server 中获取 Region 的元数据信息;
PD Server 将 Region 的元数据信息,通过 TiDB Server 的 PD Client 模块返回给 TiKV Client 模块;
TiKV Client 模块根据接收到的 Region 元数据信息,到 TiKV 实例中获取数据。
为了减少与 PD Server 的网络交互,TiKV Client 将从 PD Server 获取的元数据信息缓存到本地Region Cache 中,以便后续的读取请求可直接从 Region Cache 中获取该 Region 的元数据信息。当TiKV 的 Region 的 Leader 发生切换或分裂,TiKV Client 还是按照 Region Cache 缓冲的元数据进行请求时,会发生二次请求29,这种现象则成为 back off。导致 back off 的主要原因是 Region Cache 的信息过旧;back off 事件越多,读写的延迟就越高。
TiDB 中的时间服务(TSO)由 PD 提供,使用的是中心式的混合逻辑时钟。其使用 64 位(int64)表示一个时间戳,其中低 18 位代表逻辑时钟(Logical Time)部分,剩余部分代表物理时钟(Physical Time)部分,其结构如下图1.30所示。物理时钟是当前 Unix Time 物理时钟的毫秒时间。由于逻辑时钟部分为 18 位,因此理论上,每毫秒内 PD 最多可以分配 218=262144 个时间戳(TSO)。
下面从“校时”、“授时”、“推进”三部分,对 TSO 进行讲解:
1.3.3.1. 校时当新的 Leader 节点被选出时,其并不知道当前系统的时间已经推进到了哪里,因此首选要对Leader 的时间进行校对。首先,新 Leader 会读取上一个 Leader 保存到 etcd 中的时间,这个时间是上一个 Leader 申请的物理时间(ms)的最大值 Tlast。通过读取 Tlast,便可知道上一个 Leader 分配的时间戳是小于(因为是预分配)Tlast 的。获得 Tlast 后,会将本地物理时间 Tnow 与 Tlast 进行比较,如果 Tnow - Tlast < 1ms,那么当前的物理时间 Tnext = Tlast + 1,否则 Tnext = Tnow。至此,校时完成。
1.3.3.2. 授时TSO 的授时流程如图1.31所示。
TSO 请求者向 TiDB Server 实例的 PD Client 模块发送“TSO 请求”;
PD Client 将“TSO 请求”转发至 PD Leader;
PD 接收到“TSO 请求”后,因为无法立刻分配 TSO。于是,先为 PD Client 返回一个异步对象tsFuture,表示 “我已经收到请求了,这个 tsFuture 你先拿着,待会儿给你分配 TSO,到时候你需用这个 tsFuture 来领取 TSO”;
PD Client 将 PD 分配的 tsFuture 转发给 TSO 请求者,TSO 请求者收到并存储 tsFuture。
PD 为 TSO 请求者分配 TSO(TSO 中会携带 tsFuture 信息),PD Client 将分配的 TSO 转交给TSO 请求者;
TSO 请求者接收到 PD Client 发送过来的 TSO 后,将其中携带的 tsFuture 信息与自己在第 4 步中已收到的 tsFuture 相比对,以确保是分配给自己的 TSO。
为了保证当前 Leader 宕机后,新 Leader 能校时成功,需要在每次授时之后,都要对 Tlast 进行持久化,保存到 etcd 中。若每次授时之后,都持久化,性能会大大降低。PD 采取的优化策略是预申请一个可分配的“时间窗口 Tx”,默认 Tx = 3s。因此,在授时开始之前,PD Leader 首先将 Tlast = Tnext +Tx 存储到 etcd 中;然后,PD Leader 便可在内存中直接分配 [Tnext , Tnext + Tx) 之内的所有时间戳,避免了频繁写入 etcd 带来的性能问题。但是,如果 Leader crash,会浪费一些时间戳。
TSO 中的物理时钟部分便是校时之后的 Tlast,而逻辑时钟部分便随着请求而原子递增。如果逻辑时钟部分超过了最大值(218=262144),则会睡眠 50ms 来等待物理时间被推进,物理时间被推进后,如果有时间戳可以被分配,则会继续分配时间戳。
由于 TSO 请求是跨网络的,所以为减少网络开销,PD 的 TSO 服务支持批量请求时间戳。客户端可以一次申请 N 个时间戳,减少网络开销。
1.3.3.3. 推进授时阶段,只能通过逻辑时钟部分自增来
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。