這篇文檔介紹和說明了cockroachdb的架構(gòu)凑耻,簡單明了犯戏。
作為Spanner的開源實現(xiàn),CockroachDB具有支持標準SQL接口拳话,線性擴展先匪,強一致,高可用等重要特性弃衍⊙椒牵總體架構(gòu)如下圖所示:
CockroachDB架構(gòu)圖
總覽
Node代表一個CockroachDB進程實例,一般情況下一臺物理機部署一個CockroachDB實例,一個CockroachDB實例可以配置多個Store, 單個Store與RocksDB實例一一對應(yīng)岸裙,一般情況下一個Store對應(yīng)一塊物理磁盤猖败。
CockroachDB按照范圍進行數(shù)據(jù)切分,最小數(shù)據(jù)切分單元是Range降允。Range默認的配置大小是64M, 以3副本的方式分布在各個節(jié)點上恩闻,副本間通過Raft協(xié)議進行數(shù)據(jù)同步。
**元數(shù)據(jù)管理 **
CockroachDB通過兩級路由的方式管理元數(shù)據(jù)剧董,類似普通用戶數(shù)據(jù)的管理方式幢尚,元數(shù)據(jù)也是基于Range進行管理。每條路由元數(shù)據(jù)約為256B翅楼,默認情況下,單個元數(shù)據(jù)Range可存儲256K條路由信息(64MB/256B),那么尉剩,CockroachDB集群理論上最大容量為4EB(256K256K64MB)。
第一級元數(shù)據(jù)永遠不會發(fā)生分裂而且第一級元數(shù)據(jù)的路由信息會通過Gossip協(xié)議同步到各個節(jié)點毅臊。由于第一級元數(shù)據(jù)發(fā)生變更的幾率較小理茎,所以各個節(jié)點大部分時間可直接根據(jù)本地的元數(shù)據(jù)信息將請求路由到指定節(jié)點處理。
SQL層
CockroachDB支持標準SQL, 當CockroachDB集群的某個節(jié)點收到SQL請求時管嬉,會經(jīng)過SQL解析皂林、SQL執(zhí)行計劃生成、SQL執(zhí)行等重要步驟蚯撩。
**1. 協(xié)議層 **
CockroachDB兼容PostgreSQL協(xié)議础倍,對于報文的封裝和解析完全按照PostgreSQL的方式進行,所以用戶可以直接使用PostgreSQL的客戶端訪問CockroachDB求厕。
**2. SQL解析 **
CockroachDB對于用戶的SQL語句按照PostgreSQL的語法進行解析著隆,解析完成后生成語法樹(AST)扰楼,調(diào)用示例類似 :
func (p *Parser) Parse(sql string)
(stmts StatementList, err error) {
p.scanner.init(sql)
if p.parserImpl.Parse(&p.scanner) != 0 {
return nil, errors.New(p.scanner.lastError)
}
return p.scanner.stmts, nil
}
**3. SQL執(zhí)行計劃生成 **
CockroachDB 會根據(jù)不同的語法樹生成對應(yīng)的執(zhí)行計劃呀癣。目前執(zhí)行計劃基本是基于規(guī)則的方式來生成的。對于OLAP的SQL Statement, CockroachDB會將邏輯計劃轉(zhuǎn)化為物理執(zhí)行計劃弦赖,即通過分布式任務(wù)的方式進行并行執(zhí)行项栏。
// makePlan implements the Planner interface.
func (p *planner) makePlan(
ctx context.Context,
stmt parser.Statement) (planNode, error) {
plan, err := p.newPlan(ctx, stmt, nil)
if err != nil {
return nil, err
}
if err :=
p.semaCtx.Placeholders.AssertAllAssigned();
err != nil {
return nil, err
}
needed := allColumns(plan)
plan, err = p.optimizePlan(ctx, plan, needed)
if err != nil {
return nil, err
}
if log.V(3) {
log.Infof(ctx, “statement %s compiled to:\n%s”,
stmt, planToString(ctx, plan))
}
return plan, nil
}
4. SQL執(zhí)行當執(zhí)行計劃生成完畢后,CockroachDB會按照約定的方式開始執(zhí)行蹬竖,此時CockroachDB將調(diào)用事務(wù)性的KV接口沼沈。執(zhí)行完成后通過協(xié)議層將執(zhí)行結(jié)果返回給客戶端。
分布式KV層
**SQL到分布式KV映射 **
在CockroachDB中币厕,所有的Table必須包含一個主鍵(若建表時無顯式聲明主鍵列另,則系統(tǒng)默認創(chuàng)建)。每列數(shù)據(jù)構(gòu)成一個Key-Value存儲單元旦装。Key就是每個存儲單元值的地址页衙,如/<TableID>/<IndexID>/<Primary Key>/<Column>。舉個例子,我們創(chuàng)建一張水果價格表:
我們再往表格中插入兩條數(shù)據(jù):
此時店乐,KV層存儲的數(shù)據(jù)結(jié)構(gòu)是:
對于Key-Value存儲單元艰躺,分布式KV存儲層對外提供了操作原語接口,SQL層通過調(diào)用KV操作原語接口實現(xiàn)對KV對象的增刪改查操作眨八。
**分布式事務(wù) **
對于CockroachDB集群腺兴,接收請求的節(jié)點會充當事務(wù)協(xié)調(diào)節(jié)點(Coordinator), 不同于傳統(tǒng)的2PC,CockroachDB通過事務(wù)表來保證事務(wù)的原子性廉侧。CockroachDB在事務(wù)開始時页响,會在事務(wù)表中新增一條事務(wù)記錄,初始狀態(tài)為Pending伏穆,然后協(xié)調(diào)節(jié)點會將請求發(fā)送給參與節(jié)點進行處理拘泞,當所有的參與節(jié)點執(zhí)行完畢后,協(xié)調(diào)節(jié)點會將該事務(wù)的狀態(tài)置為Committed枕扫;若事務(wù)回滾陪腌,則把事務(wù)狀態(tài)標記為Abort。這樣做的優(yōu)點是既消除了兩階段鎖烟瞧,同時大大降低了事務(wù)提交和回滾的開銷诗鸭。
**多副本強一致 **
多副本數(shù)據(jù):Range默認3副本存儲,副本數(shù)可設(shè)置成2N+1参滴;若少于一半的副本丟失强岸,CockroachDB集群會自動在其他可用節(jié)點上補齊丟失的副本數(shù)據(jù)。
故障容災(zāi):根據(jù)不同的容災(zāi)等級砾赔,Range數(shù)據(jù)多副本可配置為跨機器蝌箍、跨數(shù)據(jù)中心、跨地域存儲暴心。Range數(shù)據(jù)分布越分散妓盲,相應(yīng)的讀寫延時也會越大,集群整體性能會有所下降专普。
Raft協(xié)議:對于CockroachDB而言悯衬,單個Range的多個副本通過Raft協(xié)議進行數(shù)據(jù)同步。Raft協(xié)議將所有的請求以Raft Log的形式串行化并由Leader同步給Follower檀夹,當絕大多數(shù)副本寫Raft Log成功后筋粗,該Raft Log會標記為Committed狀態(tài),并Apply到狀態(tài)機(即寫RocksDB)炸渡。對于讀請求娜亿,由于直接使用Raft協(xié)議開銷比較大,為此CockroachDB引入了Leaseholder的概念蚌堵。Leaseholder在有效期內(nèi)可以保證該Range的Raft Group的Leader不會發(fā)生切換买决,從而保證從Leaseholder上即可讀到最新的數(shù)據(jù),一般情況下Leaseholder也同樣是Raft Leader。