tidb的tikv
##################
集群cluster:一个集群由多个实例组成。 Node : Node 可以认为是一个实际的物理机器,每个 Node 负责一个或者多个 Store。相当于一台机器,一台机器可以部署多个实例。 Store : Store 使用 RocksDB 进行实际的数据存储,通常一个 Store 对应一块硬盘。相当于一个实例,一个实例有多个region。 Region : Region 是数据移动的最小单元,对应的是 Store 里面一块实际的数据区间。每个 Region会有多个副本(replica),每个副本位于不同的 Store ,而这些副本组成了一个 Raft group。
当我们启动一个 TiKV 服务的时候,首先需要通过 is_cluster_bootstrapped 来判断整个 TiKV 集群是否已经初始化,如果还没有初始化,我们就会在该 TiKV 服务上面创建第一个 region。
定期 Region 向 PD 汇报自己的相关信息,供 PD 做后续的调度。譬如,如果一个 Region 给 PD 上报的 peers 的数量小于预设的副本数,那么 PD 就会给这个 Region 添加一个新的副本 Peer。
定期 store 向 PD 汇报自己的相关信息,供 PD 做后续调度。譬如,Store 会告诉 PD 当前的磁盘大小,以及剩余空间,如果 PD 发现空间不够了,就不会考虑将其他的 Peer 迁移到这个 Store 上面。
当 Region 发现自己需要 split 的时候,就 ask_split 告诉 PD,PD 会生成新分裂 Region 的 ID ,当 Region 分裂成功之后,会 report_split 通知 PD。
当然这两种策略并不是孤立的,可以灵活组合,比如可以建立多级的 Sharding 策略,最上层用 Hash , 每一个 Hash Sharding 中,数据有序的存储。
在做动态扩展的时候,对于 Range 模型的系统会稍微好做一些,简单来说是采用分裂,比如原本我有一个 [1, 100) 的 Range Region,现在我要分裂,逻辑上我只需要简单的将这个 region 选取某个分裂点,如分裂成 [1,50), [50, 100) 即可,然后将这两个 Region 移动到不同的机器上,负载就可以均摊开。
但是对于 Hash 的方案来说,做一次 re-hash 的代价是挺高的,原因也是显而易见,比如现在的系统有三个节点,现在我添加一个新的物理节点,此时我的 hash 模的 n 就会从 3 变成 4,对于已有系统的抖动是很大,尽管可以通过 ketama hash 这样的一致性 hash 算法尽量的降低对已有系统的抖动,但是很难彻底的避免。
在 TiKV 中,我们选择了按 range 的 sharding 策略,每一个 range 分片我们称之为 region,因为我们需要对 scan 的支持,而且存储的数据基本是有关系表结构的,我们希望同一个表的数据尽量的在一起。另外在 TiKV 中每一个 region 采用 Raft 算法在多个物理节点上保证数据的一致性和高可用。
从社区的多个 Raft 实现来看,比如 Etcd / LogCabin / Consul 基本都是单一 raft group 的实现,并不能用于存储海量的数据,所以他们主要的应用场景是配置管理,很难直接用来存储大量的数据,毕竟单个 raft group 的参与节点越多,性能越差,但是如果不能横向的添加物理节点的话,整个系统没有办法 scale。
scale 的办法说来也很简单,采用多 raft group,这就很自然的和上面所说的 sharding 策略结合起来了,也就是每一个分片作为一个 raft group,这是 TiKV 能够存储海量数据的基础。但是管理动态分裂的多 raft group 的复杂程度比单 raft group 要复杂得多,目前 TiKV 是我已知的开源项目中实现 multiple raft group 的仅有的两个项目之一。
tikv的数据分片规则为:范围range
因为tikv需要支撑100TB以上的海量数据,庞大的数据量只有一个raft肯定搞不定,必须是多个raft组成,
多个raft的动态管理就需要对数据进行分片,分片的规则有哈希和范围两种
tikv的分片规则是范围range,这个选择没有商量的余地咯
1)范围分片规则:
优点是相同前缀的key很好地聚合在一起,适用于范围查询操作。将一段连续的key作为一个单元放在一起,这样进行范围查询就很方便。这就需要充分考虑数据的前缀设计,尽量将相关的数据配置相同的前缀,这样就方便且高效范围查询。
缺点是region数据热点问题,解决方案有让follower角色提供读能力。
2)哈希分片规则:
优点是数据均匀分布
缺点是不支持范围查询
通常的数据分片算法就是 Hash 和 Range,TiKV 使用的 Range 来对数据进行数据分片。为什么使用 Range,主要原因是能更好的将相同前缀的 key 聚合在一起,便于 scan 等操作,这个 Hash 是没法支持的,当然,在 split/merge 上面 Range 也比 Hash 好处理很多,很多时候只会涉及到元信息的修改,都不用大范围的挪动数据。
当然,Range 有一个问题在于很有可能某一个 Region 会因为频繁的操作成为性能热点,当然也有一些优化的方式,譬如通过 PD 将这些 Region 调度到更好的机器上面,提供 Follower 分担读压力等。
总之,在 TiKV 里面,我们使用 Range 来对数据进行切分,将其分成一个一个的 Raft Group,每一个 Raft Group,我们使用 Region 来表示。
rocksdb:
Multi-Raft 架构:
1)由regoin组成的raft小组:数据在 TiKV 内部按照一个个名为 Region 的逻辑概念切分,每一个 Region 是一个独立的 Raft 复制小组,默认状态下是 3 个副本, 2)多个raft小组的操作:多个 Region 自动的动态分裂,合并,移动,在整个集群内部尽可能均匀分布。 3)使用 Raft 主要是为了实现高可用(数据冗余),但是对于 Raft 比较熟悉的朋友一定知道标准的 Raft 是一个有 Strong Leader 的,读写流量都会经过 Leader。 4)细心的朋友这个时候可能发现问题了,虽然 TiKV 能够很均匀的将 Region 分散在各个节点上, 但是对于每一个 Region 来说,只有 Leader 副本能够对外提供服务,另外两个 Follower 除了时刻同步数据,准备着 Failover 时候投票切换成 Leader 外,并没有干其他的活。
###############