crdb五层架构

Overview

crdb内部分为五层架构.

crdb arch

  1. SQL Layer: 负责SQL的语法解析,计划的生成与执行。
  2. Transaction Layer: 负责实现分布式事务,提供SI和SSI两种隔离级别。
  3. Distribution Layer: 实现全局排序的map,负责数据的分布规则。
  4. Replication Layer: 通过raft实现数据副本的强一致性。
  5. Storage Layer: 使用RocksDB作为底层存储,包括raft log以及user data的存储。

SQL Layer

将用户提交的SQL语句转化为底层存储的KV操作。

Overview

开发者使用一个连接串即可进行关系操作。crdb节点是对等关系,可以选择任意一个节点进行连接,此节点程为gateway node. 用户发送的SQL语句在这一层需要转换成对KV的操作,然后传递到Transaction Layer。

Components

Relational Structure

crdb给用户展现的是关系型结构,对象有表、行、列等。同时还支持一些关系特性,如外键约束。

SQL API

crdb实现了大部分的ANSI SQL语法以及事务相关的语法,如BEGIN, END, ISOLATION LEVELS

PostgreSQL Wire Protocol

通信协议兼容PG协议

SQL Parser, Planner, Executor

接收到SQL后,crdb解析sql,生成执行计划,并执行。

Parsing

通过解析yacc文件pkg/sql/parser/sql.y, 生成抽象语法树AST.

Planning

根据AST, 生成树形的执行计划planNodes,每个planNode节点是对KV的一组操作。
可以通过EXPLAIN查看执行计划。

Executing

planNodes的执行会直接和Transaction Layer进行交互,此阶段包括消息的encoding和decoding。

Encoding

虽然SQL语句是可读的字符流,但是KV是按照字节流进行存储,所以在和Transaction Layer交互的时候,需要将字符流转化为字节流,然后传递给Transaction Layer, 而Transaction Layer返回的数据是字节流,在返回给client端的时候,需要转换成用户可识别的字符流。

DistSQL

Distributed SQL.

non-distributedSQL, coordinating node接受所有的数据,单点进行后续的计算处理。

DistSQL将计算推给多个节点进行,coordinating node聚集多个执行节点的结果集。

DistSQL可以降低传递给coordinating node的数据量,同时利用并行计算的能力,可以大大降低执行时间。

Technical Interactions with Other Layers

planNodes中的kv操作传递给Transaction Layer.

Transaction Layer

事务层负责实现分布式事务,保证事务的ACID特性。

Overview

Writes & Reads (Phase 1)

Writing

执行写操作时,不直接将数据落盘:

  1. 先写Transaction Record,写到事务中第一条write语句涉及的range。状态有PENDING, COMMITTED, ABORTED
  2. 所有write操作会写Write Intents, 表示临时的未提交状态,同时数据中包含了指向Transaction Record的值。
Reading

如果读到正常数据,OK,如果读到write intents,需要进入transaction conflict处理流程。

Commits (Phase 2)

检查Transaction Record, 如果发现ABORTED状态,restart事务。

Cleanup (Asynchronous Phase 3)

异步清理状态, Transaction Record状态设为COMMIT后,需要异步清理write intents.

存在专门的coordinating node,做异步清理工作:

  1. 清理write intent指向Transaction Record的指针数据。
  2. 删除write intent, 修改原记录

How CockroachDB Does Distributed, Atomic Transactions

Technical Details & Components

Time & Hybrid Logical Clocks

分布式系统中,顺序和因果非常重要,但是很难解决。虽然raft可以实现全局排序,但是效率非常低下。crdb实现了hybrid-logical clocks (HLC),有物理部分和逻辑部分组成。物理部分由本地时钟决定,逻辑部分用于区分相同本地时钟的事件。

事务执行时,通过HLC获取时间戳。当节点向其它节点发送请求时,会带上本地时间戳,当其它节点收到请求后,会根据收到的时间戳调整本地的HLC,保证下一个时间戳一定大于收到的时间戳。

Max Clock Offset Enforcement

为了协调不同节点之间的时间,可以指定最大的时钟偏差。如果节点的时钟偏差超过设定的值,则此节点会自杀。

时钟偏差过大可能导致事务一致性出现问题

Timestamp Cache

时间戳缓存用于缓存本地读操作对应的时间戳,如果写操作小于cache中对应的最新的读时间戳,则写操作的事务会以新的时间戳进行重试。

Transaction Records

在事务修改第一个kv之前,需要先在此key所在的rang上生成一个transaction记录。

Transaction Record的状态:

  1. PENDING: 初始状态,事务写入还在进行状态
  2. COMMITTED: 提交状态,其它事务可见。
  3. ABORTED: 事务失败或者回滚。

此为switch状态,PENDING===OFF,COMMIT=ON

Write Intents

  1. Switch: Transaction Records设置为off状态,此值不允许并发读写。
  2. Stage: 写入的值不直接修改key,而是新做一个临时key0,紧挨着原key, key0包行指向Transaction record的指针。
  3. Filter: 其它线程读取时,如果碰到staged状态的可以,需要检查switch状态,如果为OFF,则返回原始VALUE,如果为ON,则返回staged value
  4. Flip: 写入结束后,commit阶段,把Transaction Records设置为ON状态。
  5. Unstage: commit结束后,需要清理staged value,把key的value替换为key0的value,删除key0
Resolving Write Intent

当一个操作读到write intent后,根据Transaction record的状态可以有如下处理:

  1. COMMITTED: 删除staged value的指针。
  2. ABORTED: 删除staged value。
  3. PENDING: 需要进行Transaction Conflicts

Isolation Levels

  1. Serializable Snapshot Isolation 需要检查读写冲突
  2. Snapshot Isolation 只检查写写冲突, 存在write skew写倾斜的问题

Transaction Conflicts

两种冲突:

  1. Write/Write : 两个PENDING的事务对同一个key进行write intent.
  2. Write/Read : 读遇到了比自己时间戳小的write intent。

三种处理方式:

  1. 看优先级:如果优先级不一样,则优先级低的事务aborted.
  2. 第二个事务将第一个事务的timestamp提升(TxnA必须是SI,TxnB是读操作)
  3. TxnB进入等待,等待TxnA完成。

Technical Interactions with Other Layers

Transaction & SQL Layer

SQL Layer发送planNodes给Transaction Layer

Transaction & Distribution Layer

TxnCoordSender发送KV请求给Distribution Layer的DistSender.

Distribution Layer

Overview

crdb以全局排序进行数据存储用来:

  1. Simple lookups: 很容易知道数据存住在哪个节点,定位数据很简单。
  2. Efficient scans: 通过指定数据的顺序,高效扫描数据

Monolithic Sorted Map Structure

全局排序map包含两种数据:

  1. System data : meta ranges
  2. User data
Meta Ranges

二级索引:meta1meta2. 首先查询meta1,然后查询meta2,最后定位到具体的key所在的range。 每个节点都有meta1 range所在的位置,此range不会分裂。所有node都会缓存已经查询的meta2的记录。

Table Data

data按照range为最小单位,每个range默认是64MB。range也是副本同步的基本单元。

Using the Monolithic Sorted Map

当节点接收到用户请求时,查询meta range,找到key对应的rang所在的node。通过meta2,找到具体range的Leaseholder, 然后将请求发给Leaseholder. Leaseholder是三个副本中负责接收读写请求的副本。

Technical Details & Components

gRPC

Distribution Layer是第一个与其它node通信的Layer, crdb使用gRPC进行节点间通信。

gRPC的消息通过protobufs进行打包和解包。crdb实现了基于pb的APIapi.proto.

BatchRequest

KV请求打包到一起,批量进行传递,减少RPC的调用。

DistSender

gateway节点的DistSender接受本地TxnCoordSender传递的BatchRequests,DistSender将所有请求按照需要发送到的不同的node进行分类。

Meta Range KV Structure

1
metaX/successorKey -> LeaseholderAddress, [list of other nodes containing data]
  1. meta1 contains the address for the nodes containing the meta2 replicas.
1
2
3
4
5
# 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
  1. meta2 contains addresses for the nodes containing the replicas of each range in the cluster, the first of which is the Leaseholder.
1
2
3
4
5
6
7
8
9
10
11
# 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

Table Data KV Structure

1
/<table Id>/<index id>/<indexed column values> -> <non-indexed/STORING column values>

Range Descriptors

每个range都有元数据,成为Range Descriptors,包含如下:

  1. 顺序递增的RangeID
  2. keyspace: 本range包含的minkey和maxkey
  3. 其它副本所在的node的地址。

range描述信息会存储在meta2中。

Range Splits

当range达到了64MB后,分裂成两个32MB的range。

Technical Interactions with Other Layers

Distribution & Transaction Layer

Distribution Layer的DistSender,接收BatchRequests.

Distribution & Replication Layer

Distribution Layer将BatchRequests发送给Replication Layer的Raft group leader or Leaseholder

Replication Layer

Overview

高可用和数据完全一致是数据库系统的一个挑战,为了解决这个问题,crdb使用了一致性协议保证任何修改必须得到多数节点的同意才能完成。

当某个节点挂了之后,crdb会将数据重新分布,达到最大副本数。当新节点加入集群后,数据rebalance.

Components

Raft

将一个range的所有副本组织一个raft group,每个raft group有一个leader,其它的为followerleader由raft group进行选举,协调raft group中所有节点的写入。

BatchRequest首先传递给Leaseholder,然后KV操作映射为对应的raft command发给raft group leader。(Leaseholder是接受read,write的节点,记录在meta2中;raft leader是raft协议选出的group leader,负责raft节点的协调,最好的情况是Leaseholderraft leader是一个节点)

Raft Logs

当写入请求达到多数支持,被raft group leader提交后,此操作记入raft log。raft log是序列化的操作记录,可以replay。

Snapshots

每个副本都可以进行快照,根据MVCC实现快照。使用快照恢复的时候,配合raft group log,进行增量恢复。

Leases

Raft group中的一个节点作为Leaseholder,接受读请求,同事讲写请求提交给raft group leader.

读请求可以不用管raft.

When serving reads, Leaseholders bypass Raft; for the Leaseholder’s writes to have been committed in the first place, they must have already achieved consensus, so a second consensus on the same data is unnecessary.

Co-location with Raft Leadership

Leaseholder在lease过期之后,会进行renew,或者将leaseholder转让给其它节点,目的是让leaseholder与raft group leader是同一个节点。

Epoch-Based Leases (Table Data)

表数据也有租约,使用epochs进行实现,在一个node加入集群和离开集群成为一个epoch,如果node脱离集群,则epoch进行变化,此node就会失去所有相关的lease。

这避免了跟踪每个range的lease,这里假设所有的lease都不会过期,直到node脱离集群。

Expiration-Based Leases (Meta & System Ranges)

meta range也有租约,不是epoch,而是expiration-based lease。定期重新选leaseholder。

Membership Changes: Rebalance/Repair

集群的节点数出现变化后,raft group的成员也会变化,range副本需要rebalance。

  1. Nodes added: 新节点与其它节点通信,表示自己空闲,集群便转移一部分replica过去。
  2. Nodes going offline: 如果一个raft group中的成员长时间不响应,超过5分钟,则集群开始进行rebalance操作,复制数据到其它节点,以达到约定的副本数。

Rebalancing Replicas

集群检查到节点变化后,会进行replica的迁移。

这是通过在Leaseholder上进行snapshot,然后通过gRPC传递快照到其它节点。快照传递完成后,新的节点加入到raft group中来, 此间会通过raft log同步增量数据。

Interactions with Other Layers

Replication & Distribution Layers

  1. Distribution Layer的DistSender发送kv请求给replication层。
  2. Replication layer 发送BatchResponsesDistSender

Replication & Storage Layers

提交的raft command写到raft logs,最终写入到Storage Layer.

Leaseholder从Storage Layer层的RocksDB读取数据。

Storage Layer

Overview

每个crdb节点至少包含一个store, 数据按照KV存储在RocksDB上。每个store包含三个RocksDB实例:

  • One for the Raft log
  • One for storing temporary Distributed SQL data
  • One for all other data on the node

一个节点中的所有store共享一个block cache.

Components

RocksDB

crdb使用RocksDB的原因:

  1. KV存储,和Distribution Layer直接映射
  2. 原子批量写,快照,事务

RocksDB是Facebook基于Google的LevelDB开发的一个分支,加了一些优化措施。

MVCC

crdb严重依赖MVCC机制,时间戳通过hybrid logical clock (HLC) 进行确定。所有的MVCC数据都存在RockDB。

MVCC在存储层存储,被 Transaction Layer利用。

Time-Travel

SELECT...AS OF SYSTEM TIME 获取指定时间戳的数据, 只要数据没有被GC。

Garbage Collection

crdb定期清理老的MVCC数据来减少磁盘数据的大小。存在一个garbage collection period,如果old value的时间戳距离当前时间的差值大于此值,则可以进行清除。

Interactions with Other Layers

Storage & Replication Layers

Storage Layer通过raft log将数据写入磁盘,同时将数据返回给Replication Layer.

参考链接

  1. cockroachdb doc
  2. How CockroachDB Does Distributed, Atomic Transactions

本文标题:crdb五层架构

文章作者:Louis

发布时间:2017年10月25日 - 13:10

最后更新:2017年10月27日 - 09:10

原始链接:/2017/10/25/cock-architecture/

许可协议: Louis-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。