HDFS之Bookkeeper工作原理分析

来源:互联网 发布:什么手机有4g网络 编辑:程序博客网 时间:2024/06/03 17:14

1.前言

1.1背景

    自从hadoop2版本开始,社区引入了NameNode高可用方案。NameNode主从节点间需要同步操作日志来达到主从节点元数据一致。最初业界均通过NFS来实现日志同步,大家之所以选择NFS,一方面因为可以很方便地实现数据共享,另外一方面因为NFS已经发展20多年,已经相对稳定成熟。

虽然如此,NFS也有缺点不能满足HDFS的在线存储业务:网络单点及其存储节点单点。业界提供了数据共享的一些高可用解决方案,但均不能很好地满足目前HDFS的应用场景。

方案

网络单点

存储单点

备注

Mysql HA

数据有丢失风险

Drbd+heartbeat+NFS

脑裂;数据有丢失风险

Keepalive+NFS

数据有丢失风险

        为了满足共享日志的高可用性,社区引入bookkeeper。bookkeeper由yahoo开发,实现了读写高可用性,使HDFS达到真正的高可用性成为可能。

1.2.术语和定义

术语和定义

解释

Entry

主节点写的每一个日志对象则为一个entry

Ledger

一个ledger由entry集合组成,每一个日志段对应一个ledger,相同日志段追加edits即为向相应的ledger追加entry

Bookkeeper client

在HDFS中即为namenode

Bookie

一个bookkeeper的存储服务,存储了bookkeeper的write ahead日志,及其数据(ledgers)内容

Metadata server

由zookeeper充当bookkeeper的元数据服务器,在zk中存储了ledger相关元数据,edits元数据,及其bookie相关元数据

Ensemble

即为bookie可用的最小的节点数量;该参数应该大于等于quorums

Quorums

法定的bookie数量,即日志写入bk服务端的冗余分数,并且每份副本均成功才算成功,否则通过rr算法,查找下一组quorums重新写日志

ledgerId

标志ledger的编号,该编号依次递增

entryId

标志entry的编号,该编号依次递增,一个txid就会对应一个entryId

entryLogId

Bookie内标志存储entry的log文件编号

startLogSegment

开始一个新的日志段,该日志段状态为接收写入日志的状态

finalizeLogSegment

将文件由正在写入日志的状态转化为不接收写日志的状态

recoverUnfinalizedSegments

主从切换等情况下,恢复没有转换为finalized状态的日志

 

2.设计方案

    bookkeeeper通过读写多个存储节点达到高可用性,同时为了恢复由于异常造成的多节点数据不一致性,引入了数据一致性算法。逻辑图如下:


 Zookeeper作为bookkeeper的元数据服务器,里面存储了哪些bookie服务是可用的,同时也记录了目前系统有哪些ledger,及其ledger相关信息,如该ledger数据存储在哪些机器上,及其该ledger起始,结束entryid等,具体信息如下:

[zk: localhost:2181(CONNECTED) 65] get /ledgers/L0000000107

BookieMetadataFormatVersion    2

quorumSize: 2

ensembleSize: 2

length: 83

lastEntryId: 2

state: CLOSED

segment {

  ensembleMember: "10.28.171.35:3181"(包含entry0,1

  ensembleMember: "10.28.171.38:3181"(包含entry 01

  firstEntryId: 0

}

//中间35bookie服务down掉,42接替35

segment {

  ensembleMember: "10.28.171.42:3181"entry2

  ensembleMember: "10.28.171.38:3181"entry0,1,2

  firstEntryId: 2

}

digestType: HMAC

password: ""

ackQuorumSize: 2

        Bookie节点存储实际的数据,及其数据的读写服务。

2.1.写日志机制

写操作由主节点来完成,当主节点调用setReadyToFlush操作,会调用RPC同时向N(N=quorums)个bookie节点写,flush异步等待响应。


2.1.1.bookkeeper客户端

主节点对bk的操作,其实就是对ledger的操作,在开始向bk服务写数据前,首先需要打开ledger, 打开ledger就会与配置的所有bookie节点建立连接;打开连接后,数据以entry为单位以RR算法选择向N(N=quorums)个bookie节点写entry数据,并且异步地等待结果返回,有任何一个bookie写入失败,则需要重新选择一个bookie写入失败的副本。

 

2.1.2.bookkeeper服务端

当bookie服务端接收到写入数据后,首先会写日志,然后根据同步或者异步算法将数据同步到磁盘上。写入数据过程中,首先会写入log文件,写入的内容包含ledgerid,entryid,EntrySize,LastConfirmed,及其真实数据内容

--------- Lid=478, Eid=0, ByteOffset=30625,EntrySize=65 ---------

Type:           DATA

LastConfirmed:  -1

Data:二进制数据

然后在相应ledger文件中记录下entryid,及其该entry所在的日志文件,偏移量等。

entry 1162  :   (log:7, pos: 465208)

2.2 读日志机制

相比写日志过程,读日志要相对简单一些。同样,读日志过程也支持高可用。

读取日志示意图


当从节点触发读日志的时候,会经历如下几个步骤:

1、  选择日志文件,建立输入流

    从节点触发消化日志后,首先会查询ZK,获取到主节点写入ZK的edits元数据信息(不包含inprocess状态的edits元数据),这个元数据包含日志段的startTxid,lastTxid,ledgerID,同时也会打开相应的ledger,并获取其元数据,如ledger的quorumSize,ensembleSize,lastEntryId等,同时按照txid先后顺序对ledger进行排序,放入输入流集合。需要强调的是,当打开ledger时,会检查其entry副本之间的一致性,如果不一致需恢复。

2、  消化日志

准备好输入流以后,开始消化日志, 依次操作输入流集合的ledgers,读取每个ledger内的entry会经过如下步骤:

1、  通过查询ledger元数据,同时通过RR算法确定该entry存储在哪几个bookies;

2、  尝试从bookies集合的第一个bookie服务读取entry,如果成功,该entry就读取成功,如果失败,转入第3步;

3、  尝试从bookies集合的第二个bookie服务读取entry,如果成功,该entry就读取成功,如果失败,依次类推,如果尝试读取完所有的bookies均失败,则该entry读取失败;

2.3.数据恢复

数据恢复流程


Bookie数据恢复检查通过定时或者人工发起,集群数据修复流程:

1、  通过zk查询到ledger元数据;

2、  通过元数据,查询相关bookie中存储的ledger的entry是否完整;

3、  如果查询到存储在某bookie上的entry不完整,则需要进入数据恢复流程;

4、  首先从bk服务端读取到ledger相关的entry,然后将其写到需要恢复entry的某bookie服务端;

5、  Ledger数据恢复完成后,需要更新ledger的segment相关元数据。

元数据更新对比


3.附言

3.1.Ledger相关

Ledger在bookkeeper中扮演了很重要角色,很有必要说明一下其相关操作及其作用

CreateLedger:创建一个空的ledger,此时会在zk中存储相关元数据;

AddEntry:添加一个记录到ledger中,如果客户端失败或者ledger已经关闭,则不能再追加entry;

openLedger:开始读取数据前,必须先打开ledger,如果某ledger处于未关闭,不能读取相关数据,如果有异常,需先恢复;

readEntries:读取ledger中的entry;

3.2.方案对比

 

QJM

bookkeeper

NFS

发布时间

2012/3

2008

1984

公司

Cloudera

yahoo

Sun

单点

可维护性

相对简单

相对复杂

相对简单

外部系统依赖

Zk依赖较大

NFS服务器,网络

存活检查

QJMclient

Zk

NFS client

稳定性

xx

xx

xx

性能

xx

xx

xx

原创粉丝点击