logo资料库

深入学习hbase原理资料整理.docx

第1页 / 共19页
第2页 / 共19页
第3页 / 共19页
第4页 / 共19页
第5页 / 共19页
第6页 / 共19页
第7页 / 共19页
第8页 / 共19页
资料共19页,剩余部分请下载后查看
Hbase HLog源代码阅读笔记
HRegionServer
HBase存储格式
HFile
HLogFile
HBase源代码阅读与理解
Hbase HLog 源代码阅读笔记 catalog region, and if not then check if that table supports append(HRegionInfo info, byte [] tableName, WALEdit edits, HLog 当客户端往 RegionServer 上提交了一个更新操作后,会调用 HLog 的 append 方 法往 WAL 上写一个节点,入口方法就是 append 1.append public void final long now) throws IOException { if (edits.isEmpty()) return; if (this.closed) { throw new IOException("Cannot append; log is closed"); } synchronized (this.updateLock) { long seqNum = obtainSeqNum(); byte [] hriKey = info.getEncodedNameAsBytes(); this.lastSeqWritten.putIfAbsent(hriKey, seqNum);// 存 的 是 一 个 最 老 的 sqeNum,这是代表,比该值等于或大于的数据都是没有持久化的 HLogKey logKey = makeKey(hriKey, tableName, seqNum, now); doWrite(info, logKey, edits);//写数据,关键方法 this.numEntries.incrementAndGet(); } // Sync if // deferred log flushing if (info.isMetaRegion() || !info.getTableDesc().isDeferredLogFlush()) { this.sync();//如果是 Meta 表或是表不允许延迟同步,则立即同步 } } 2.doWrite protected void doWrite(HRegionInfo info, HLogKey logKey, WALEdit logEdit) throws IOException { if (!this.enabled) { return; } if (!this.listeners.isEmpty()) { for (WALObserver i: this.listeners) { i.visitLogEntryBeforeWrite(info, logKey, logEdit);//观察者模式,以便调 起其他需要通知的方法 } } try { long now = System.currentTimeMillis(); this.writer.append(new HLog.Entry(logKey, logEdit));//重要方法是这句 long took = System.currentTimeMillis() - now;
writeTime += took; writeOps++; if (took > 1000) { long len = 0; for(KeyValue kv : logEdit.getKeyValues()) { len += kv.getLength(); } LOG.warn(String.format( "%s took %d ms appending an edit to hlog; editcount=%d, len~=%s", Thread.currentThread().getName(), took, this.numEntries.get(), StringUtils.humanReadableInt(len)));//记录用时,如果超一秒则警告 } } catch (IOException e) { LOG.fatal("Could not append. Requesting close of hlog", e); requestLogRoll();//如果写出错日志会被截断 throw e; } } SequenceFileLogWriter 3.append public void append(HLog.Entry entry) throws IOException { this.writer.append(entry.getKey(), entry.getEdit()); } SequenceFile.Writer 4.append 最终是调用 hadoop 的 SequenceFile.Writer.append 将数据持久化的。 当 Region 的 memstore flush 之后,会往 HLog 里写一条日志,标明哪个表的哪 个分区在哪个 sequenceId 这里持久化过一遍 1.completeCacheFlush public void completeCacheFlush(final byte [] encodedRegionName, final byte [] tableName, final long logSeqId, final boolean isMetaRegion) throws IOException { try { if (this.closed) { return; } synchronized (updateLock) { long now = System.currentTimeMillis(); WALEdit edit = completeCacheFlushLogEdit();//这一句表名是 Flush 这种操 作的日志 HLogKey key = makeKey(encodedRegionName, tableName, logSeqId, System.currentTimeMillis());//这一句表明该日志记录下了表名、分区名、当
前的日志 SequenceId this.writer.append(new Entry(key, edit));//这一句写入日志文件 writeTime += System.currentTimeMillis() - now; writeOps++; this.numEntries.incrementAndGet(); Long seq = this.lastSeqWritten.get(encodedRegionName); if (seq != null && logSeqId >= seq.longValue()) { this.lastSeqWritten.remove(encodedRegionName);// 每 个 Region 最 后 更 新 SequenceId 被删除,表明该 Region 没有数据需要持久化。 } } // sync txn to this.sync();//这种 flush 操作很重要,一定要同步到 hdfs 的其他节点上 system file } finally { this.cacheFlushLock.unlock(); } } HRegionServer HRegionServer 主要负责响应用户 I/O 请求,向 HDFS 文件系统中读写数据,是 HBase 中最核心的模块。 HRegionServer 内部管理了一系列 HRegion 对象,每个 HRegion 对应了 Table 中 的一个 Region,HRegion 中由多个 HStore 组成。每个 HStore 对应了 Table 中的 一个 Column Family 的存储,可以看出每个 Column Family 其实就是一个集中的
存储单元,因此最好将具备共同 IO 特性的 column 放在一个 Column Family 中, 这样最高效。 HStore 存储是 HBase 存储的核心了,其中由两部分组成,一部分是 MemStore, 一部分是 StoreFiles。MemStore 是 Sorted Memory Buffer,用户写入的数据首 先会放入 MemStore,当 MemStore 满了以后会 Flush 成一个 StoreFile(底层实 现是 HFile),当 StoreFile 文件数量增长到一定阈值,会触发 Compact 合并操 作,将多个 StoreFiles 合并成一个 StoreFile,合并过程中会进行版本合并和 数据删除,因此可以看出 HBase 其实只有增加数据,所有的更新和删除操作都是 在后续的 compact 过程中进行的,这使得用户的写操作只要进入内存中就可以立 即返回,保证了 HBase I/O 的高性能。当 StoreFiles Compact 后,会逐步形成 越来越大的 StoreFile,当单个 StoreFile 大小超过一定阈值后,会触发 Split 操作,同时把当前 Region Split 成 2 个 Region,父 Region 会下线,新 Split 出的 2 个孩子 Region 会被 HMaster 分配到相应的 HRegionServer 上,使得原先 1 个 Region 的压力得以分流到 2 个 Region 上。下图描述了 Compaction 和 Split 的过程: 在理解了上述 HStore 的基本原理后,还必须了解一下 HLog 的功能,因为上述的 HStore 在系统正常工作的前提下是没有问题的,但是在分布式系统环境中,无 法避免系统出错或者宕机,因此一旦 HRegionServer 意外退出,MemStore 中的 内存数据将会丢失,这就需要引入 HLog 了。每个 HRegionServer 中都有一个 HLog 对象,HLog 是一个实现 Write Ahead Log 的类,在每次用户操作写入 MemStore 的同时,也会写一份数据到 HLog 文件中(HLog 文件格式见后续),HLog 文件定 期会滚动出新的,并删除旧的文件(已持久化到 StoreFile 中的数据)。当 HRegionServer 意外终止后,HMaster 会通过 Zookeeper 感知到,HMaster 首先 会处理遗留的 HLog 文件,将其中不同 Region 的 Log 数据进行拆分,分别放到 相应 region 的目录下,然后再将失效的 region 重新分配,领取 到这些 region 的 HRegionServer 在 Load Region 的过程中,会发现有历史 HLog 需要处理,因 此会 Replay HLog 中的数据到 MemStore 中,然后 flush 到 StoreFiles,完成数 据恢复。 HBase 存储格式 HBase 中的所有数据文件都存储在 Hadoop HDFS 文件系统上,主要包括上述提出 的两种文件类型:
HFile, HBase 中 KeyValue 数据的存储格式,HFile 是 Hadoop 1. 的二进制格式文件,实际上 StoreFile 就是对 HFile 做了轻量级包装,即 StoreFile 底层就是 HFile 2. 物理上是 Hadoop 的 Sequence File HLog File,HBase 中 WAL(Write Ahead Log) 的存储格式, HFile 下图是 HFile 的存储格式: 首先 HFile 文件是不定长的,长度固定的只有其中的两块:Trailer 和 FileInfo。 正如图中所示的,Trailer 中有指针指向其他数据块的起始点。File Info 中记 录了文件的一些 Meta 信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY 等。Data Index 和 Meta Index 块记录了每个 Data 块和 Meta 块的起始点。 Data Block 是 HBase I/O 的基本单元,为了提高效率,HRegionServer 中有基于 LRU 的 Block Cache 机制。每个 Data 块的大小可以在创建一个 Table 的时候通 过参数指定,大号的 Block 有利于顺序 Scan,小号 Block 利于随机查询。每个 Data 块除了开头的 Magic 以外就是一个个 KeyValue 对拼接而成, Magic 内容就 是一些随机数字,目的是防止数据损坏。后面会详细介绍每个 KeyValue 对的内 部构造。 HFile 里面的每个 KeyValue 对就是一个简单的 byte 数组。但是这个 byte 数组 里面包含了很多项,并且有固定的结构。我们来看看里面的具体结构: 开始是两个固定长度的数值,分别表示 Key 的长度和 Value 的长度。紧接着是 Key,开始是固定长度的数值,表示 RowKey 的长度,紧接着是 RowKey,然后是 固定长度的数值,表示 Family 的长度,然后是 Family,接着是 Qualifier,然 后是两个固定长度的数值,表示 Time Stamp 和 Key Type(Put/Delete)。Value 部分没有这么复杂的结构,就是纯粹的二进制数据了。
HLogFile 上图中示意了 HLog 文件的结构,其实 HLog 文件就是一个普通的 Hadoop Sequence File,Sequence File 的 Key 是 HLogKey 对象,HLogKey 中记录了写入数据的归 属信息,除了 table 和 region 名字外,同时还包括 sequence number 和 timestamp,timestamp 是“写入时间”,sequence number 的起始值为 0,或者 是最近一次存入文件系统中 sequence number。 HLog Sequece File 的 Value 是 HBase 的 KeyValue 对象,即对应 HFile 中的 KeyValue HMaster:实现 master 的功能 A.负责分配 region 到 regionserver,检测新增或失败的 regionserver,与 regionserver 交互,regionserver 间的负载均衡等; B.处理 shcema 的变更; C.实现 ZooKeeper 的 Watcher 接口,与 zookeeper 集群交互 2.1 master 启动过程初始化,构造 HMaster 实例 --minServers=:指定最少的 RegionServers 数,默认为 10 根据参数 hbase.cluster.distributed 分 local 和 distribute 模式,这里主要考 虑 distribute 模式下: (1)设置并检查文件系统路径。(checkRootDir 方法) a.如果文件系统处于安全模型,则一直等待直到退出安全模式 b.若 root 路径不存在则会创建该目录,并将当前 hbase 的文件
格式版本号写入 hbase.version 中,并再下一次启动是会检查是否与当前 hbase 版本支持的文件格式版本一致。 c.检查 root Region 是否存在,即-ROOT-目录是否存在(root region 也是当做一个 table 来统一处理的)。如果不存在,则创建 root region 和第 1 个 meta region。root region:regionId=0,tableName=-ROOT-,仅有一个 info 的 Column Family;meta region:regionId=1,tableName=.META.,有一个 info 和 historian 两个 Column Family d.将 meta 做为 root region 的 user region 加入其中(有点拗 口)。 务 (2)获取 master 的地址,并创建一个 HBaseServer 的实例提供 RPC 服 (3)创建一个连接 (4)读取 classpath 下配置的 hbase.zookeeper.property.clientPort 和 hbase.zookeeper.quorum,连接 zookeeper 集群:创建 ZooKeeper 实例,并增 加 Watcher(HMaster)设置 /hbase,/hbase/root-region-server,/hbase/rs,/hbase/master,/hbase/maste r/shutdown 等 hbase 存储在 zookeeper 中信息的路径,将 master 地址写入 /hbase/master(若该 Znode 存在,则等待直到该节点被删除。 (5)建立 RegionServerOperation 队列 (6)启动 ServerManager 管理 region servers 的信息 (7)启动 RegionManager 分配 region 到 region servers,并管理 root,meta 等状态 2.2 master 执行过程,启动线程,执行 Thread.run() (1)将 master 加入集群: 从 zookeeper 的 hbase/root-region-server 读取 root region 所在的 regionserver;读取所有 regionserver 地址,若没有 regionserver 则可 能是一个新启动的集群,调用 HLog.splitLog(this.rootdir, logDir, oldLogDir, this.fs, getConfiguration()); (2)启动服务线程: 启动 RegionManager 的 root 和 meta region 的扫描线程,初始 扫描后,应知道所有 region 的分配信息;而 region 每次分裂后也应通知 master, 并分配 region 给新的 regionserver;但 master 可能丢失该 split 信息,因此周 期性的扫描 root 和 meta region 以检测丢失的 split 信息及 regionserver 的死 亡信息;启动一个 Jetty Server,处理 http 请求;启动 RPC 服务。
HBase 源代码阅读与理解 (2011-09-23 10:27:03) 标签: 分类: 云计算 一、脚本 start-hbase.sh,hbase-daemon.sh,hbase-daemons.sh,zookeepers.sh,regions ervers.sh,hbase,hbase-config.sh 1.1 hbase:hbase 命令行入口,最终控制 master,regionserver,zookeeper 等启 动或关闭 1.1.1 hbase shell:执行 jruby 脚本 org.jruby.Main ${HBASE_HOME}/bin/hirb.rb,是 hbase 命令行接口 1.1.2 hbase master:执行类 org.apache.hadoop.hbase.master.HMaster,启 动或关闭 HMaster 1.1.3 hbase regionserver:执行类 org.apache.hadoop.hbase.regionserver.HRegionServer,启动或关闭 RegionServer 1.1.4 hbase thrift:执行 org.apache.hadoop.hbase.thrift.ThriftServer, 启动或关闭 thrift 服务 注:Thrift 是 facebook 发起的轻量级跨语言的服务框架,现在在 apache 基金 会下。 支持很多语言。Thrift 有一个与具体编程语言无关 IDL 语言,用来描述服务接 口,以及数据交换的格式,存储在.thrift 文件中。 然后使用自带的编译器将 thrift 文件编译成 cpp、python 等语言的框架代码。 1.1.5 hbase avro org.apache.hadoop.hbase.avro.AvroServer Avro 是一个数据序列化系统。 Avro 提供了: * 丰富的数据结构。 * 一个简约的、快速的、二进制数据格式。 * 一个容器文件,用于存储持久数据。 * 远程过程调用(RPC)。 * 和其他动态语言的简单集成。存取数据文件或者使用 RPC 协议的时 候不需要生成代码。代码生成只是作为静态类型语言的一个可选的优化项。 1.1.6 migrate:执行 org.apache.hadoop.hbase.util.Migrate,貌似系统迁移 的功能 1.1.7 zookeeper :执行 org.apache.hadoop.hbase.zookeeper.HQuorumPeer, 启动或关闭 zookeeper 服务 1.2 start-hbase.sh 启动 hbase。
分享到:
收藏