CochroachDB架构——分布式事务的生命周期

因为CockroachDB是一个分布式事务数据库,查询采用的路径与很多其他数据库架构有显著不同。为了帮助您熟悉CockroachDB内部,该指南会覆盖路径介绍的内容。
如果您已经阅读了CockroachDB架构文档,该指南作为概念化该数据库工作机制的另一个方式。这次,我们不是专注CockroachDB架构的各个层,而是将专注于查询通过系统(然后再回来)采用的线性路径。
为了最大化该指南的价值,我们推荐以架构文档的概览开始并继续阅读如下所有部分。该指南简要描述每个组件功能和其他合适文档的链接,但假设读者首先对架构有基本了解。

一.概览
该指南通过系统中的物理角色进行组织,并接着将每个角色的组件按照相关顺序进行拆解。
这里是物理角色的简要概览,按照执行查询时其涉及到的顺序:
1.SQL客户端向集群发送一个查询。
2.负载均衡将该请求路由到集群中CockroachDB节点,其将扮演一个网关角色。
3.网关为一个CockroachDB节点,其处理SQL请求并对客户端响应。
4.租约持有者为负责服务查询中读并协调键值特定范围写的CockroachDB节点。
5.Raft领导者为负责维护CockroachDB副本间共识的CockroachDB节点。
一旦事务完成,查询将按照几乎相反顺序穿过这些角色。我们说"几乎",是因为单个查询中也许涉及多个租约持有者和Raft领导者,响应期间与负载均衡器机会没有交互。

二.SQL客户端/Postgres线协议(Postgres Wire Protocol)
为了开始,一个SQL客户端(例如:一个app)对CockroachDB集群执行了某种业务逻辑,像插入一条客户记录。
该请求通过用PostgreSQL驱动建立的连接发送给CockroachDB集群。

三.负载均衡和路由
现代架构要求将集群分布到多台机器来改善吞吐、延迟和上线时间。这意味着查询通过负载均衡器路由,其选择连接到最好的CockroachDB节点。因为所有的CockroachDB节点完全对称的访问数据,这意味着负载均衡器能将客户端连接到集群中的任何节点和访问任何数据,同时保证强一致性。
您的架构也许还有另外的路由层来强制遵从法规,像确保遵从GDPR。
一旦路由和负载均衡器决定连接的最好节点,客户端将和网关节点建立连接。

四.网关
网关节点处理来自客户端的连接,接收和响应请求。
1.SQL解析和计划
网关节点首先解析客户端的SQL语句,以确保其对CockroachDB的SQL特征有效,并用该信息产生一个逻辑SQL计划。
考虑到CockroachDB是分布式数据库,考虑集群拓扑也很重要,因此,逻辑计划接着转换成物理计划——这意味着有时将操作推到包含数据的物理机器上。
2.SQL执行器
尽管CockroachDB为客户端提供了一个SQL接口,但实际上数据库是基于键值store构建。为了协调这点,SQL解析结束时产生的物理计划传递给SQL执行器,其通过TxnCoordSender执行键值操作来执行该计划。例如:SQL执行器将INSERT语句转换成Put()操作。
3.TxnCoordSender
TxnCoordSender为在数据库上执行键值操作提供API。
在其后端,TxnCoordSender为一个事务执行大量的解释和跟踪,其中包括:
1)解释事务中涉及的所有键值。除了其他方法外,其还被用于管理事务的状态。
2)将所有的键值操作打包成一个BatchRequest,其被转发到该节点的DistSender。
4.DistSender
网关节点的DistSender从TxnCoordSender接收BatchRequests。其通过获取每个操作,并发现哪个机器应该接收该请求以获取作为范围租约持有者的已知范围,来拆解最初的BatchRequest。范围当前租约持有者的地址很容易从本地缓冲和集群元范围获取。
这些被拆解的BatchRequests被重新组装成新的包含范围租约持有者地址的BatchRequests。
所有写操作也将租约持有者的地址传回TxnCoordSender,因此,其必要时能跟踪和清理写操作。
DistSender为每个范围并行发送第一个BatchRequest。其一旦接收到租约持有者节点评估者(细节如下)的临时确认,其讲发送该范围的下一个BatchRequest。
DistSender接着等待接收其所有写操作的确认,还有其所有读操作的数值。但是,该等待未必被阻塞,DistSender还会执行进行中事务的操作。

五.租约持有者节点
网关节点的DistSender试图将其BatchRequests发送到作为范围租约持有者的副本,其为服务一个范围所有读的单一副本,并协调所有写。租约持有者在CockroachDB架构中扮演重要角色,因此,确信您应该对其熟悉。
1.请求响应
因为租约持有者副本能在节点间移动,所有节点必须能返回任何键值的请求,返回指示下列场景之一的响应:
1)不再是租约持有者
如果一个节点不再是租约持有者,但还包含范围的一个副本,其拒绝请求但包括范围租约持有者的最后已知的地址。
接收到该响应后,DistSender将用新地址更新BatchRequest的头部,并接着将BatchRequest重新发送给新识别的租约持有者。
2)不再拥有/从未拥有范围
如果一个节点没有请求范围的一个副本,其拒绝该请求而没有提供进一步信息。
这种情况下,DistSender必须用集群的元范围查找当前租约持有者。
3)成功
一旦包含范围租约持有者的节点接收到BatchRequest,将开始对其进行处理,并继续检查时间戳缓冲。
2.时间戳缓冲
时间戳缓冲跟踪特定范围上读操作的最高时间戳(即,最近的)。
BatchRequest中每个写操作对比时间戳缓冲检查自己的时间戳,以确保写操作有一个较高的时间戳;这确保历史从不会被重写,且相信读总是服务最近的数据。其为CockroachDB用来确保串行化的重要机制之一。如果一个写操作检查失败了,其必须以一个比时间戳缓冲中数值更高的时间戳重新开始。
3.闩管理器
BatchRequest中的操作通过租约持有者的闩管理器进行串行化。
这通过为每行上的每个写操作分配一个闩来实现。该闩后进来的、已被授权的任何读或写必须等待该写完成,闩被释放时后续的操作可以继续。
4.批量评估
批评估器确保写操作是有效的。我们的架构使得这点相当简单。首先,评估器能简单检查租约持有者的数据,以确保写是有效的;因为其已对该范围的写进行了协调,其必须拥有该范围数据的最新版本。其次,因为闩管理器,每个写操作都保证对该范围进行无竞争访问(即,与其他写操作没有冲突)。
如果评估器认为写操作有效,租约持有者将向网关节点的DistSender发送一个临时确认;这使DistSender开始发送该范围的后续BatchRequests。
重要的是,该特性完全是为了事务优化而构建的(称为事务流水线化)。如果操作经过了评估器但没有结束提交,这是没问题的。
5.从RocksDB读
所有操作(包括写)通过从本地RocksDB实例读以检查操作键值的写意向开始。我们在值得一读的CockroachDB架构事务层中对写意向进行了更多的讨论,但一个简单说明是,这些是临时的、未被提交的写,其表示一些其他并发事务计划往该键值写入一个数值。
我们西面详述的是CockroachDB事务模型的一个简化版本。更多细节,请参考事务架构文档。
1)解析写意向
如果一个操作遇到某个键值的写意向,其试图通过检查写意向事务状态来"解析"该写意向。如果该写意向的事务记录是:
a)COMMITTED:该操作将该写意向转换为普通键值对,并接着继续工作好像其已经读取了数值而非写意向。
b)ABORTED:该操作放弃该写意向并从RocksDB读取下一个最近的数值。
c)PENDING:新事务试图通过向前移动事务时间戳来"推动"该写意向事务(即,该事务时间戳的前面);但是,这仅在该写意向事务已变为不活动时才能成功。
i)如果推动成功,该操作继续。
ii)如果推动失败(其为大概率),该事务将进入该节点的TxnWaitQueue。一旦阻塞事务完成(即,提交或中止),传入事务才能继续。
d)MISSING:解析器查阅该写意向的时间戳。
i)如果在事务活动阈值内创建,其作为PENDING状态对该事务进行处理,另外跟踪该范围时间戳缓冲的推动,一旦事务记录被创建其将通知该事务其时间戳以推动。
ii)如果写意向比事务活动阈值旧,解析将显示ABORTED行为。
注意事务记录也许会丢失,因为我们一直避免事务提交前写入记录。更多信息,请参考事务层:事务记录。
CockroachDB事务模型相关的更多信息,请参考架构文档。
2)写操作
如果读不遇到写意向和键值操作意味着服务一个读,其能用从租约持有者的RocksDB实例读取的数值。因为租约持有者必须是完成任何写的Raft共识组的一部分,这意味着其必须有范围数据的最近版本。
租约持有者将所有的读相应汇聚到一个BatchReponse中,其将返回到网关节点的DistSender。
如前所述,每个读操作也会更新时间戳缓冲。
6.写操作
确保键值没有写意向后,BatchRequest的键值操作被转换成Raft操作,且将其数值转换成写意向。
租约持有者接着将这些Raft操作提供给Raft组领导者。租约持有者和Raft领导者几乎总是同一节点,但也有它们移到不同节点的情况。但是,当两个角色不在同一物理机上时,CockroachDB接下来将试图将它们重新放到同一节点。

六.Raft领导者(Raft Leader)
CockroachDB将Raft用作共识协议。如果您对其不熟悉,我们建议了解有关CockroachDB如何利用Raft的细节,以及详细了解该协议的工作原理。
就执行事务而言,Raft领导者从租约持有者接收提出的Raft命令。每个Raft命令是用于表示存储于RocksDB底层键值对原子状态改变的一个写。
1.共识
对Raft领导者接收的每个命令,其将像该Raft组的其他成员提供一个投票。
一旦该命令达成共识(即,包括自己在内的大多数节点确认该Raft命令),其将被提交到Raft领导者的Raft日志并写到RocksDB。同时,Raft领导者也向其他节点发送一个命令以将该命令包含到它们的Raft日志中。
一旦该领导者提交了Raft日志项,其被认为已提交。此时,该数值认为完成写,并且,如果另一个操作进来,并对RocksDB该键值上执行一个读,其将会遇到该数值。
注意,该写操作创建一个写意向;直到网关节点将事务记录的状态设置为COMMITTED,这些写才会被完全提交。

七.返回之路
既然我们已经完成了从SQL客户端到RocksDB的所有操作,我们也能快速完美的讲述返回路上发生的一切(即,当发生一个到客户端的响应)。
1.一旦租约持有者将一个写应用到其Raft日志,其将向网关节点的DistSender发送一个提交确认,其一直在等待该信号(已经从租约持有者的评估器接收了临时确认)。
2.网关节点的DistSender对BatchRequest中所有写操作的提交确认进行汇聚,还有将被返回客户端的读操作的任何数值。
3.一旦所有操作成功完成(即,读已经返回数值和写意向已经提交),DistSender试着在事务记录中记录事务的成功(其为跟踪事务状态提供了持久机制),其可能会导致一些情况发生:
1)它对第一个写发生的范围的时间戳缓冲进行检查,以查看其时间戳是否已被前推。如果是,事务执行一个读刷新以查看其需要的任何数值是否已被改变。如果刷新成功,事务能在被前推的时间戳提交。如果读刷新失败,事务必须重启。
2)如果是否为ABORTED状态,DistSender发送一个响应,以指示该响应最终返回SQL接口。
通过这些检查后,事务记录首次以COMMITTED装啊提写入,或如果在PENDING状态,其被移到COMMITTED。仅在这点,事务才被认为已提交。
4.DistSender将任何返回客户端的数值传送到TxnCoorDSender(例如:读或影响的行数),其返回来用该数值响应SQL接口。TxnCoordSender也通过向DistSender发送一个请求来将其为该事务创建的所有写意向转换成完全提交的数值来开始异步意向清理。但是,该过程主要是优化;如果任何操作遇到一个写意向,其将检查写意向的事务记录。如果事务记录为COMMITTED,操作能执行同样的清理,并将写意向转换成完全提交的数值。
5.SQL接口接着响应客户端,并准备继续接受新的连接。

八.个人观点

1)事务是关系库最重要的特征,CockroachDB作为一款分布式关系特征库,也是一样,通过该指南的学习,能大致了解和掌握CockroachDB事务实现机制,同时,我们也能清楚的直到,其具备完备的分布式事务机制,这点与其他类似产品差别较大。

 

你可能感兴趣的:(Newsql,transaction,事务,cock,roach,机制)