本文知识点来源于官网地址https://www.cockroachlabs.com/docs/v22.1/architecture/distribution-layer.html
CockroachDB(以下简称CRDB)将数据存储在key-value对组成的巨大的有序map结构。这个map里面描述了集群中的所有数据,以及数据的位置。数据及数据的位置信息(我们称之为meta数据)均以range为单位存储。所有的key都能在某一个range中找到具体的数据。
通过这个巨大的有序的map,可以实现:
这个巨大的有序的map主要包括两部分内容:
集群中所有range的位置都存储在key空间开头的一个两级索引中,称为meta range。meta range中包含一级meta(meta1)及二级meta(meta2),meta1指向meta2,meta2指向集群中的数据。
可以用以下树型结构来理解两级索引和用户数据的关系:
当数据量不是特别大的时候,此时meta1和meta2存放在一个range当中,meta1只有一条元数据,指向meta2,meta2中每个元数据对应每个用户数据所在的每个range的信息。
每个range的最大容量是512MB。每个range的元数据信息大约占用256Byte空间,因此一个meta range可以容纳1PB左右的用户数据。
如果集群的实际数据确实比较大,如超过1PB,这时候meta range的512MB无法存放所有的range元数据,而meta range又是不可分割的,此时会用新的range来存储新产生的meta2数据。
meta range可以像普通range一样被处理,像集群中KV数据一样被访问和复制。
另外,每个节点都会缓存相关的meta数据,以提升对数据访问的效率。如果缓存中没有相关的meta数据,就通过常规读取来更新缓存数据。
meta range后面的key空间就是集群中KV数据,也是以range为单位进行存储。
每个表及二级索引最开始映射到一个range,range中的每个key-value表示表中的一行数据。当一个range大小达到512MB时,它就分裂为两个range。当数据量持续增大时,后续会继续分裂。数据以range为单位进行复制(通过复制层),并将每个副本的地址存储在meta range中。
当一个节点接收到请求时,它以自底向上的方式查询包含请求的key的range位置。其工作原理如下:
上面描述的过程是递归的,每次递归查找时,要么从缓存中获取一个位置,要么对树上“上一层”的值执行另一个查找。因为meta range是缓存的,通常情况下可以执行查找,而不需要向另一个节点发送RPC。
CRDB每个range都有元数据,称为“range描述符”。range描述符由以下内容组成:
由于range描述符包含meta2 range的key-value数据,所以每个节点的meta2缓存也缓存了range描述符。
range描述符会在以下几种情况下进行更新:
所有range描述符的更新都在range所在节点的本地发生,然后传播到meta2 range。
默认情况下,CRDB尝试保持范围/副本为默认范围大小(当前为512 MiB)。一旦一个范围达到了这个限制,我们就把它分成两个更小的范围(由连续的键空间组成)。
在分割范围期间,节点创建一个新的Raft组,其中包含与分割范围相同的所有成员。现在有两个范围这一事实也意味着存在一个事务,它使用新的键空间边界更新meta2,以及使用范围描述符的节点地址。
为了优化集群的性能,CRDB可以将频繁访问的键分割为更小的范围。与基于负载的再平衡一起,基于负载的拆分在集群中平均分配负载。
启动/禁用基于负载的拆分
可以通过以下参数设置来打开/关闭基于负载的拆分功能,默认为打开。
SET CLUSTER SETTING kv.range_split.by_load_enabled = false;
另外,可以通过以下参数来控制什么时候对range进行拆分,默认为2500。
SET CLUSTER SETTING kv.range_split.load_qps_threshold = 2000;
基于负载的拆分如何工作
当一个范围超出了设置的kv.range_split.load_qps_threshold,该范围适合基于负载的拆分。
在这一点上,开始收集每个关键指标,以确定拆分是否会基于以下启发式来提高集群的性能:
默认情况下,CRDB自动将小范围的数据合并到一起,形成更少、更大的范围(直到默认的范围大小)。这可以提高查询延迟和集群生存能力。
CRDB将您的集群数据划分为许多范围。例如,您的集群可能有一个id在[1000,2000)之间的客户的范围。如果该范围超过默认的范围大小,则该范围将被分割为两个更小的范围。
但是,当您从集群中删除数据时,范围包含的数据可能远远小于默认范围大小。在集群的生命周期中,这可能导致许多小范围。
为了减少小范围的数量,您的集群可以有任何低于一定大小阈值的范围,尝试与它的“右邻居”合并,即从当前范围结束的地方开始的范围。使用上面的例子,这个范围的右邻居可能是id在[2000,3000)之间的客户的范围。
如果小范围和它的邻居的组合大小小于最大范围大小,范围合并为一个单一的范围。在我们的示例中,这将创建一个新的键范围[1000,3000)。
在CRDB中的查询必须联系查询中涉及的每个范围的副本。这对于有很多小范围的集群会产生以下问题:
通过合并小范围,CRDB可以大大减少查询中涉及的范围的数量,从而减少查询延迟。
每当节点联机或脱机时,CRDB自动重新平衡集群中的范围分布。