Ti-KV

本文深入探讨了TiKV的持久化机制,包括RocksDB的列簇、写入与查询优化。详细阐述了TiKV的分布式事务实现,特别是两阶段提交和MVCC。此外,还介绍了Raft一致性算法在TiKV中的应用,以及读写流程中的ReadIndexRead、LeaseRead和FollowerRead策略。最后提到了Coprocessor在提升查询性能方面的作用。
摘要由CSDN通过智能技术生成

目录

TiKV 持久化

TiKV架构及作用 

RocksDB 

RocksDB:查询

RocksDB:列簇

TiKV 分布式事务

TiKV Raft

Propose

Append

Replicate

Committed

Apply 

Raft Leader选举

TiKV读写与Coprocessor

ReadIndex Read

Lease Read

Follower Read 

Coprocessor 

例题


TiKV 持久化

TiKV架构及作用 

rocksdb:负责数据的落地,即持久化

rocksdb实例包括:

rocksdb raft:负责raft log日志的存取

rocksdb kv:负责键值对和图数据的存取

对与数据的分布式储存,有两种方式HashRange

Hash:按照 Key做Hash,根据Hash值选择对应的存储节点

Range:按照Key分Range,某一段连续的 Key 都保存在一个存储节点上

raft group:raft gruop包括每个region和它的副本

TiKV的读写

rocksdb的读写:针对单个TiKV node的读写,是纵向的

raft共识算法的读写:针对多个TiKV node中的的region副本,是横向的

Raft协议通过Raft共识算法保证数据的分布式储存和数据的一致性,同时仅可能,将数据均衡的分布在集群的所有节点中

Coprocessor协调处理器

提供算子下推能力,使TiKV具有一部分过滤,聚合等SQL计算的能力,且是并行的

RocksDB 

使用RSM Tree存储Key-Value的数据结构 

与MySQL的B+ Tree相比,写入效率更,但查询效率较

RocksDB:写入

写友好型,具有顺序性,且比B+ Tree效率高

 插入,删除先储存在内存,合并后写入磁盘

WAL 预写日志:保证写入的原子性和持久性,即使系统宕机也能进行故障恢复

参数symc_log=true:使写入信息直接写入磁盘WAL文件中,不经过内存

参数write_buffer_size:用于设置MemTable的大小;若超过设置的大小,数据则被转存至immutable,并开辟一个新的Memtable

immutable

设置immutab的目的在于进行磁盘IO时,作为SST文件的中间状态,不阻塞客户端写入;但只要有存在immutable,就开始向磁盘写入当写入操作过多,使immutable达到5个时,则触发rocksdb流控机制(write stall),即限制写入速度

ps:immutable不支持继续写入 

磁盘内部运作机制

Level 0中的内容与immutable的内容相同,是immutable的转存,即immutable的副本

进升Level的条件

当到达4个(默认情况下)SST文件,就进行数据压缩(compaction),将4个SST文件合并为一个,并移至下一个Level(移至下一个Level数据量*10);在压缩过程中,会对Key进行重新排序

ps:写入需要一次内存IO,一次磁盘IO

RocksDB:查询

比B+ Tree慢

Block Cache

存储最近最长读取的数据

查询机制

只读最新,如果在更前Level中查到所需的Key,则不必向下一个Level查找

 若key值为SST文件的Min和Max之间,使用二分法进行查找(若就是Min或Max直接返回)

若不在,则查找下一个SST文件

bloom Filter

用于判断元素是否在集合内

机制:

过滤器判断不在,一定不在
过滤器判断在,不一定在(有误判率)

RocksDB:列簇

列簇用于数据分片

使一个rocksdb有多个内存区域和多套SST文件,但WAL文件是所有列簇是共享

写入时可以指定列簇,不指定则默认在default列簇

TiKV 分布式事务

begin:通过PD获取事务开始时间,即start_ts

提交的两阶段:

prewrite:将内存中的数据和将锁信息写入TiKV node中

commit:通过PD获取事务结束时间(或称提交时间),即commit_ts

               向Write列簇写入提交信息

               向Lock列簇写入解锁信息

其中:

 当一个事务有多行时,只给事务的第一行添加主锁,其他行依附于该锁,即锁的指向

 TiKV Node使用3个列簇存储有关事务的执行信息:

Default列簇:存储修改数据(大于255字节),且只存修改值(方便查询);Key=事务id+事务开始的时间戳

Lock列簇:存储锁信息,且只给事务的第一行添加主锁(即pk),其他行依附于该锁(锁的指向)

Write列簇:存储提交信息和一些小事务(小于255字节);Key=事务id+事务结束的时间戳,Value=事务的开始时间戳

简而言之,短数据存在Write列簇,长数据存在Default列簇

得出结论

prewrite阶段使用Default列簇(修改信息)和Lock列簇(锁信息)

commit阶段使用Lock列簇(解锁信息)和Write列簇(事务提交信息)

ps:解锁不是直接删除Lock列簇中的信息,而是添加一条解锁信息 

根据锁的存在能否被其他会话感知,分为:

乐观事务:在提交时将锁信息写入TiKV node,使其他会话无法感知锁的存在

悲观事务:在提交前将锁信息写入TiKV node,使其他会话可以感知锁的存在

 如上图所示,分布式事务在不同TiKV node中

 @1,锁的指向,即表示该锁执行事务id=1的事务,即主锁的位置

若发生如下情况,事务的某一行在向Write列簇写入提交信息时宕机,也未添加解锁信息(如下图),TiDB如何恢复事务的执行

通过Lock列簇的写锁信息中锁的指向,找到指向的主锁,查找事务信息,继续未执行的步骤

以上图为例,通过查询事务id=1的执行,发现事务已完成,故接下来向Write列簇中添加事务提交信息并向Lock列簇中添加解锁信息、

MVCC 多版本并发控制

MVCC的运作原理:

修改时,生成所修改的region副本(同时向PD获取时间戳)并在其该副本中修改数据;在未提交修改时,则读取的数据来自未修改的版本即原本,使写不阻塞读

这些修改产生的副本,又叫快照(snapshot),即为GC垃圾清理的目标

补充:MVCC只在read commited和repeatable read两个隔离级别下工作,与隔离级别snapshot isolation无关

以上图为例,事务1完成了整个过程,而事务2未完成commit阶段

若此时,对事务2进行读取,结果为<1,Jack><2,Candy><4,Tony>

若此时,对事务2进行修改,则因锁而被阻塞

故在执行分布式事务的整个阶段,只阻塞写,不阻塞读

事务的读取

首先查询Write列簇,是否存在相应事务的提交信息

若查找到相应事务的提交信息(即以被持久化),则在相应的列簇(Default列簇)查询相应的事务信息(通过相应的Key=事务id+事务开始数据)
若Write列簇无相应事务的提交信息,且Lock列簇中存在锁信息,则无法修改该事务

对于MVCC是如何阻塞其他会话写入的的,需要三个指针(三个列簇分布一个)

如图(此时TSO=120),有会话写入事务id=1,在Write列簇中查找到事务id=1的提交信息(TSO=110),当在TSO=115时又添加了一把写锁(W)且为主锁(pk),没有解锁信息,写被阻塞

 当有会话写入事务id=2(此时TSO=120),在Write列簇中查找到事务id=2的提交信息(TSO=110),且Lock列簇中没有关于事务id=2的锁,故写入未被阻塞

当有会话写入事务id=4(此时TSO=120),在Write列簇中查找到事务id=2的提交信息(TSO=110),当在TSO=115时又添加了一把写锁(W)且指向事务id=1的主锁,没有解锁信息,写被阻塞

TiKV Raft

raft group:region及其副本(上图以3副本为例)

Multi raft:由多个raft goup组成

raft log利用region ID+日志的顺序ID来作为唯一标识 

所有客户端的读写流量都通过leader 

Propose

操作被leader收到,leader准备同步并将写入请求转化为raft log的形式(Key=region id+log id) 

Append

Append操作将raft log存入rocksdb实例中(rocksdb raft),进行(在leader中的)持久化

Replicate

leader将raft log复制给follower并在(follower中的)rocksdb中进行持久化(横向复制)

Committed

当大多数(超过一半,但不是所有的)follower将raft log持久化成功(返回append完成)

ps:但此时写入的数据未持久化(未在rocksdb kv中),只有当Apply操作后才写入rocksdb

整个阶段一共有两个Committed;一个是事务的提交,另一个是raft log的横向复制的完成

Apply 

leader将raft log(rocksdb raft中)的操作apply至rocksdb kv中的数据,写入完成

ps:若大多数(超过一半的)follower没有持久化成功(Replicate),则日志不会被Apply

Raft Leader选举

leader周期性向follower发出含有统治信息的心跳(参数heartbeat time interval);若follower长时间收不到统治信息(参数election timeout),某个region将会转换为candidate(候选者),并发起投票重新选取leader

election timeout:控制收不到统治信息时,发起选举的阈值;一般用于初始化时进行选举(集群刚被创建))

heartbeat time interval:控制发出心跳的周期,当收不到心跳的时间且超过election timeout的阈值,发起选举;一般用于集群运行中

 如图,当leader宕机,TiKV node 3长时间收不到心跳并率先达到阈值,自身转化为candidate,进入下一个term并发起选举
向其他region发起请求,进行投票;其他follower接收请求并同意比自己term大的请求(term=2<term=3)

 如图,若碰巧所有Tikv node都转为candidate企图发起选取,election timeout参数将会初始为随机值,减小每个TiKV node达到阈值的记录;若还是有多个candidate重复该过程,整个过程发起了多次选取

election timeout

        raft-election-timeout-ticks:设置 election timeout有多少个相对时间单位(ticks,默认1s)

        raft-base-tick-interval:设置每个相对时间单位(ticks)有多长时间

heartbeat time interval

        raft-heartbeat-ticks:设置 heartbeat time interval有多少个相对时间单位(ticks,默认1s)

        raft-base-tick-interval:设置每个相对时间单位(ticks)有多长时间

假设raft-election-timeout-ticks=5,raft-base-tick-interval=1s,则election timeout=5*1=5s

ps:election timeout ticks不能小于heartbeat ticks

TiKV读写与Coprocessor

首先向PD获取所region的位置(哪一个leader,在哪一个TiKV上)

线程池:

raftstore pool:将数据被转化为raft log并持久化至rocksdb raft;向其他副本横向复制(propose,append,replicate,committed)

apply pool:将raft log应用于rocksdb kv(apply)

ReadIndex Read

本质上属于一种判断操作是否apply至rocksdb的机制,如下图

当某一会话提交操作(TSO=10:00),但其操作未apply至rocksdb;此时ReadIndex=1-95,ApplyIndex=1-92,rocksdb中的数据为1-92

当另一会话企图读取被修改的数据(TSO=10:05),此时raftstore pool将日志commit至1-97,但applypool将操作应用至1-93(该案例假设没有MVCC机制,若有则读取的是之前的数据即1-93);即将ReadIndex=1-97并阻塞commit,使其等待apply操作至1-97

当apply至1-95时,事务的commit完成

当apply至1-97时,即ApplyIndex等于ReadIndex,此时根据raft log的顺序性,事务1-95肯定已被持久化至rocksdb

总结:利用日志的顺序性,通过判断后面的日志是否已apply推断出要读取的日志是否apply

ReadIndex一定大于要读的raft log,当ApplyIndex等于ReadIndex时判断

Lease Read

若某个TiKV发出心跳,则该TiKV在发出心跳的heart time interval内均为leader,但heart time interval之后未发出心跳,此时不会立马发起选举,而要直到election timeout

例如leader发送心跳的周期为10ms,则在心跳发送后10ms内,则该TiKV node在这个时间段内一定为leader

Follower Read 

在数据的读取中,leader及follower都会使用Reader Index机制绑定操作是否已应用于rocksdb;但是follower的往往会比leader更先得出结果(follower的apply速度比leader的快)

当收到读取请求时,follower向leader获取CommitIndex,当follower中的ApplyIndex大于向leader获取的CommitIndex,则说明修改以应用并读取数据

本质上是ReadIndex的变体,ReadIndex读取方式都在leader中执行,而Follower Read中CommitIndex在leader,但ApplyIndex在follower

Leader中:

 Follower中:

Coprocessor 

若不使用Coprocessor ,则数据将汇总在TiDB对网络带宽和CPU负载都有极高要求

 而使用Coprocesser,TiDB的计算将下推至TiKV

当TiDB收到SQL语句,得出物理执行计划,推出可以由TiKV Coprocessor计算的部分

Coprocessor包括三大计算:

  • 执行物理算子
  • 分析数据
  • 提交信息

下推的算子包括:索引扫,表扫,过滤等

例题

1.下列属于TiKV相关功能的是?(选4项)

A.系统参数和元数据信息的持久化 

B.产生TSO

C.分布式事务实现

D.MVCC

E.生成物理执行计划

F.表统计信息的持久化

答案:A,C,D,F

解析:B:TSO由PD产生;E:由TiDB Server生成物理执行计划

注:元数据信息如用户名,密码,表名等

2关于TiKV数据持久化,下列说法不正确的是?

A.RocksDB有2个实例,分别用来持久化raft log和key value数据

B. RocksDB中WAL用来保证写不丢失

C.对于删除操作,只需要在原key value数据上标记已删除即可

D.RocksDB中,除了Level 0层的数据,其他Level都是单一排序持久化的

答案:C

解析:A:rocksdb包括两个实例,持久化raft log的rocksdb raft和持久化key value的rocksdb kv;B:WAL预习日志,直接将写入持久化至磁盘,即使系统宕机也能故障恢复;C:TiDB使用LSM Tree进行操作,LSM Tree是以插入为基础,如更新操作,也是在原有的那一行直接插入数据;D:Level 0的内容等于immutable的内容,而immutable有多个分批写入磁盘,没有进行排序,而其他Level均是有更低的Level压缩合并后按Key值排序而来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值