2.4 分布层
CockroachDB结构为集群提供了统一的view。
**概要
--monolithic sortedmap结构
--使用monolithic sorted map(庞大的sorted map)
--与其他层的交互
**技术细节和组件
--gRpc
--BatchRequst
--DistSender
--元range KV 结构
--表数据KV结构
--range描述符
--range分裂
**与其他层的交互
--分布层和事务层的交互
--分布层和复制层的交互
2.4.1 概要
为了保证集群中的所有数据可以被所有节点访问,cockroach数据以KV键值对形式存储庞大的 sorted map中,这些key空间描述了集群中所有数据及其位置,将数据分为ranges。Ranges是key空间临近的chunks,每个key总是被包含在一个range中。
Cockroach实现sorted map 通过:
--简单的查找:节点包涵某一部分数据,query可以快速的定位他们想要的数据
--高效的扫描:定义数据的顺序,通过扫描可以发现数据具体的range。
**Monolithicsorted map 结构
Monolithic sortedmap结构由两个基本的组件组成
--系统数据,包括元ranges 描述了集群中数据的位置(其他的集群范围和本地数据元素)
--用户数据,存储集群的表数据
元ranges
集群所有range的位置,存储在key空间的两个级别的索引中,即元ranges,第一个级别(meta1)指向第二级别,第二级别(meta2)指向集群中的数据。每个节点被告知在哪里定位meta1 range(即range 描述符,具体细节如下)。Range不会被拆分。
元range结构默认寻址4EB(1EB=1024PB,1PB=1024TB)的数据,可以寻址2^(18+ 18) = 2^36 ranges,每个range寻址 2^26 B,综上, 2^(36+26) B = 2^62 B = 4EB,对于更大的range大小,我们可以在未来扩展它的能力。
元数据与其他正常的range处理相似,像集群中的KV数据的其他元素
一样,被访问和复制。
Meta2 range缓存每个节点的已取过的值,用于优化未来的访问。无论何时一个节发现meta2缓存是无效的,通过在meta2 range上定期的读更新缓存。
表数据
在集群中节点存储的元数据是KV数据,这个数据被分成64MB的相邻的连续key空间,即ranges。这个大小足够小使节点之间可以快速移动, 也足够大存储有意义的可能被同时访问的连续数据,是一个合适的值。这些ranges在集群中清洗,保证集群的耐受性。
这些range被复制(在复制层),每个复本的地址存储在meta2 range中。
**使用monolithic sorted map(庞大的sorted map)
当节点接受到请求,通过比较请求中元ranges的keys与自己meta2range,查找将请求路由到哪个节点。
这些meta range大量的被缓存,所以很容易获得,不需要向实际meta2 ranges节点发送RPC。
节点将这些KV操作发送给meta2 range的租户。不过有可能发生,当数据移动,节点不再告知请求节点,数据现在存储的位置。这种情况下,将回到meta2 range获取最新信息,再重试。
**与其他层进行交互
与分布层相关联的其他层:
--在相同节点接受来自事务层的请求
--识别哪个节点可以接受请求,将请求发送到正确的复制层节点。
2.4.2 技术细节和组件
**gRPC
gRPC是与其他节点交流的一个软件节点,分布层是与其他节点交流的第一层,Cockroach在这里实现了gRPC。
gRpc需要输入输出使用potocol buffers(protobufs)格式化,为租用Grpc,cockroachDB实现了protocol-buffer-based API(基于缓冲协议的API)定义为
api.proto
.
更多信息,查看gRpc的官方文档。
gRpc是一个客户端直接调用不同机器的服务端的方法,就像他是个本地客户端,使创建分布式应用和服务更加容易,在很多RPC(remote procedure call,远程过程调用)系统中,gRpc基于定义服务思想,识别方法,该方法可以使用参数被远程调用,并返回类型值。在服务器端,服务器实现这个接口,运行gRpc服务处理客户端调用。在客户端端,客户端具有一个存根提供与服务端同样的方法
GRpc客户端和服务端在不同的环境中彼此可以运行和交流,可以使用java创建一个GRpc的服务端,客户端使用go,python,ruby语言。可以获取最近的google api获得最新的实现。
potocol buffers
https://developers.google.com/protocol-buffers/docs/overview
potocol buffers CSDN(非官方文档)
http://blog.csdn.net/manchew/article/details/39494901
**batchRequest
所有的KV操作输入protobuf,叫做batchRequest.Batch的目标是被batchRequest头识别,同时是一个请求的事务记录的指针,在其他方面,一个节点使用batchRequest,调用protobuf,获得batchResponse。
**DistSender
网关或者合作节点的DistSender接受到来自自己的TxnCoordSender的batchRequest。DistSender应答,解析batchRequests,并将通过meta2 range将一个新的batchRequests请求集路由到实际包含数据的节点。使用缓存发送请求给租户,也会准备好去尝试其他的副本,按照“邻近”顺序。缓存的副本是租户,租户在副本集list里向前移动,并按顺序向所有副本发送RPC。
请求收到非租户失败,直到副本的最后一个已知租户返回一个失败指针。
网关节点使用明显更新的租约重试请求,不会到达客户端。所有的节点开始依赖于这些命令,DistSender收集准备结果返回给客户端。
**元range KV结构
像集群中的其他数据一样,metarange使用KV键值对构建他的结构,meta range都具有相同的结构
metaX/successorKey ->LeaseholderAddress, [list of other nodes containing data]
元素 | 描述 |
metaX | Mata range级别,我们简单使用meta1或者meta2,但是实际在cockroach分别为是\x02和\x03 |
successorKey | 第一个比扫描到key更大的key,使cochroachDB的扫描是高效的,简单的扫描keys直到发现一个值更大的key,获取相关数据的位置。对于keyspace的末尾,successorKey即为maxKey |
LeaseholderAddress | 副本主要负责读写,叫做LeaseholderAddress,复制层更多信息,请查看Leases |
例如
meta2/M-> node1:26257, node2:26257, node3:26257
在这种情况下,node1的副本是租户,node2和node3同样包含副本。
在这种情况下,node1的副本是租户,node2和node3同样包含副本。
例如:
想象我们有一个按照字母顺序排序的列,我们使用去查询,metarange数据类似:
1)Meta1 包含存储meta2副本的节点地址
# Points to meta2 range for keys [A-M)
meta1/M -> node1:26257, node2:26257, node3:26257
# Points to meta2 range for keys [M-Z]
meta1/maxKey -> node4:26257, node5:26257, node6:26257
2) meta2包含集群中每个range副本的位置,第一个是Leaseholder.
# Contains [A-G)
meta2/G -> node1:26257, node2:26257, node3:26257
# Contains [G-M)
meta2/M -> node1:26257, node2:26257, node3:26257
#Contains [M-Z)
meta2/Z -> node4:26257, node5:26257, node6:26257
#Contains [Z-maxKey)
meta2/maxKey-> node4:26257, node5:26257, node6:26257
**表的数据的KV结构
KV数据,表中数据使用如下结构:
/<tableId>/<index id>/<indexed column values> -><non-indexed/STORING column values>
表本身存储1的index_id作为主键列,表中剩下的列为存储/覆盖列。
**Range描述
CockroachDB的每个range包含元数据,即range描述符。一个range描述符由如下组成:
--一个时序的rangeID
--range包含的key空间(key集)。例如,在表数据的KV结构中,第一个和最后一个<index column values> ,这个决定了meta2 range的key。
--节点的地址包含range的副本,它的leaseholder(负责读写)的第一位置。这决定了meta2 range key的值。
因为range描述比较meta2 range的KV数据,每个节点的meta2缓存也存储range描述。
Range描述更新:range的raft成员更改(具体的细节在复制层讨论)—leaseholder改变—range分裂
所有的range描述更新发生在本地range,然后扩散到meta2 range。
**range分裂
默认,cockroachDB保存ranges或副本在64MiB中,一旦range达到限制将分裂成2个32MB的ranges(由连续的key空间组成)
在range分裂,节点创建一个新的raft group与被分裂的range相同的成员。事实上,两个range意味着事务升级到meta2,使用新的表空间边界,使用range描述的节点地址。
2.4.3 与其他层的交互
**分布层与事务层
分布层的DistSender接受到它自己节点的TxnCoordSender发送的BatchRequests
封装在事务层
**分布层与复制层
分布式层路由BatchRequests到包含数据range的节点,最终路由到raft group leader 或者leaseholder,在复制层处理