TiDB已经在项目中使用,从了解来看,它主要解决的是分布式事务的问题,而我们实际使用场景,却是大数据量下不需要关注分表;
最近实在忙加懒,一直没时间看TiDB的原理。今天看了下PingCAP3篇入门介绍,收获挺多;
首先TiDB的存储使用的是KV,名字叫TiKV,实际数据落地使用的是Facebook开源的RocksDB;
RocksDB只能支持单机存储,TiDB在此之上实现了副本机制,构成集群模式;但多副本模式下,数据一致性成为一个挑战,目前TiDB使用Raft协议保证副本间的数据一致性;具体是多副本中,一个Leader负责所有读/写操作,RocksDB的修改记录,通过Raft log的方式同步到其它副本;
TiDB中引入了Region的概念,一个Region最大64M;这里的Region与Hbase上面实现基本相同,按Key的Range进行划分。
结合上面的副本机制,也就是一个RocksDB分很多Region,每个Region分别有多个副本;
存储的原理基本清楚了,接下来的问题是,数据是以K-V的形式存储,如何支持SQL查询呢?
答案是TiDB实现了一个SQL层,管理SQL语法解析,查询计划等操作;关系数据库支持Insert, Delete, Updata, Select 4 种基本操作,最复杂的是Select操作,一般支持两种模式,一种是简单读取一行,这里面,每条KV记录都有一个RowID,如果有整数型Primary Key,TiDB会使用这个PK作为RowID,同时TiDB维护一个索引,也为KV存储,KEY为Prefix+键值,Value即为这个PK,因此可以单条记录查询。比如一条select * from xxx_tab where age = 18;
执行计划首先从Index prefix_18得到RowID,然后通过RowID查询到数据。第二种支持的是范围查询,TiDB的实现中,K-V中的Key是全局有序,按顺序排列的(相当于一个无限大的sorted map),因此通过上面单条记录查询的方法,定位到StartKey和EndKey很容易实现范围查询。
这点上看,与Hbase的Rowkey设计很类似;
TiDB通过PD(Placement Driver)来实现集群状态管理。
为什么进行集群状态管理?有很多方面考虑,如何管理节点的上下线操作?
如何实现Region Leader的负载均衡?如何保证集群中Region副本的数据正确?
这些都需要一个中心管理单元,这个单元即PD;PD会通过心跳来收集集群中Region,Raft Group(Leader管理)的状态信息,控制整个集群中 Region Leader在服务器上均匀分布,处理节点加入与掉线异常;
这里,PD里面会存储一个Region到Key Range的路由信息,在读取和写入时,通过查询PD,就可以知道自己要读取或查询的Region位置信息。当然这里实现是跟Hbase一致的,Client会缓存一份这个路由信息,避免对PD造成压力,而Client在查询失败时,会去主动拉取一份PD路由表。
另外TiDB中的分布式事务是参考Google Percolator事务模型实现的。通过Raft协议,可以实现对一个Key操作的一致性;但是一个事务操作中,如果涉及到改多个Key,而这多个Key可能在不同Region上面,这就需要分布式事务的保证。
一般简单的分布式事务实现是2PC,但2PC中需要一个协调者存在,Percolator中实现了协调者的高可用;
再简单说一下TiKV与RocksDB的关系,
TiKV 会将数据存储到 RocksDB,任何的数据都最终会转换成一个或者多个K-V 存放到 RocksDB 里面。
每个 TiKV 包含两个 RocksDB 实例,一个用于存储 Raft Log,我们后面称为 Raft RocksDB,而另一个则是存放用户实际的数据,我们称为 KV RocksDB。