一、脚本
start-hbase.sh,hbase-daemon.sh,hbase-daemons.sh,zookeepers.sh,regionservers.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。
启动过程为:先调用hbase-daemons.sh启动zookeeper,再调用hbase-daemon.sh启动master,最后调用hbase-daemons.sh启动regionserver
1.3 hbase-daemons.sh:执行regionservers.sh,从${HBASE_CONF_DIR}/regionservers读取 regionserver列表,ssh到每台host上调用其hbase-daemon.sh脚本,在后台启动regionserver;执行 zookeepers.sh,调用org.apache.hadoop.hbase.zookeeper.ZKServerTool从classpath 下读取zoo.cfg中的server列表,ssh到每台host上调用其hbase-daemon.sh脚本,在后台启动zookeeper;
1.4 hbase-daemon.sh:包装hbase master,regionserver,zookeeper等服务从后台启动
二、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/master/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服务。
三、HRegionServer
HRegionServer:管理regions,并向HMaster报告自己状态
3.1 regionserver启动过程
(1)读取一些配置:机器名,端口,客户端重试次数,与master交互间隔,rpc超时
(2)创建Worker线程处理来自master的请求
(3)创建一个HBaseServer的实例提供IPC服务以处理Client的请求
(4)创建HServerInfo实例保持regionserver信息:地址,机器名,监听端口等
(5)创建Flush缓存的线程
(6)创建Compact和Split region的线程
(7)创建归档(roll)HLog的线程
(8)定期检查regions是否需要major compaction线程
(9)regionserver与外部client交互时,会申请一些资源,当外部client失败后需释放这些资源。创建Leases线程来处理这个任务。
(10)创建ZooKeeper实例,以连接zookeeper集群,并增加/hbase/master节点的Watcher
(11)申请一部分heap空间(4个byte数组,每个5M,共20M),保留下来,在发生OOME时再释放以从异常中恢复
(12)启动regionserver并安装shut down hooks
3.2 HRegionServer.run()
(1)向master报告自己的状态:reportForDuty()方法
从zookeeper的/hbase/master节点获取master地址,通过HBaseRPC获得HMasterRegionInterface接口实现的代理;
从MemoryMXBean获取内存使用信息,计算其负载,并保存到HServerInfo实例中; 调 用HMasterRegionInterface.regionServerStartup(),告诉master该regionserver已启动,该 接口返回一个MapWritable,包含hbase.regionserver.address,fs.default.name和 hbase.rootdir等master配置信息,以更新本地配置(HBASE-1215);
(2)启动一些服务线程
在文件系统根目录下.logs建立commit log HLog的实例;后台启动归档(roll)HLog的线程,Flush缓存的线程,Compact和Split region的线程,来自master请求的worker线程,检查regions是否需要major compaction线程
启动Leases线程;
启动Jetty Server,处理http请求
(3)当服务线程正常运行(isAlive()),则在hbase.regionserver.msginterval间隔后与master交换信息:
收集regionserver的metrics,包 括:HStore,HStoreFile,索引,所有region的内存大小,block缓存的命中率等信息,regionserver负载信息;通过 PRC向master发送信息HMsg,并返回master的指令,指令类型有:stop regionserver,停止服务用户regions,重启等。
四、HMater和HRegionServer的RPC连接
启动master时创建一个Server的实例,利用反射机制提供HMaster的方法调用服务;Server继承自抽象类HBaseServer;HBaseServer通过异步io(nio包)提供了非阻塞的网络连接
4.1 RPC Server的创建及启动过程
(1)HBaseServer初始化参数:绑定地址,端口,调用队列大小,最大连接数,最大闲置连接数等
(2)创建Listener(继承自Thread)实例,使用ServerSocketChannel绑定到监听 端口,并向Selector注册接受新连接事件,对新连接连接,因为采用异步方式处理,所以为新连接创建一个Connection的实例维持连接状态,并 在Selector注册相应的读事件,以读取请求;对读事件,获取对应的Connection对象,由readAndProcess方法读取并处理请求; 处理请求时,创建Call对象,包含连接和RPC调用的具体参数信息,放入一个队列中,等待处理;
(3)创建10个Handler线程,互斥的从Call队列中取出Call的实例,传入参数调用call方法进行RPC调用(该方法由Server实现);将处理完成的结果放入Call对应Connection的回复队列中,并向Selector注册写回复请求
(4)创建Responder实例线程;该线程获取写回复请求,执行异步写,一次性回复一个Connection的所有Call调用结果
4.2 HRegionServer访问master的RPC:
在启动regionservre时,采用代理模式创建了接口HMasterRegionInterface的一个本地代理,在调用接口方法时,委托给 Invoker对象的invoke方法,该方法又通过HBaseClient作为客户端向HBaseServer发送请求,并获取返回结果
4.3 HMaster与HRegionServer交互信息的格式
(1)HServerInfo:包括服务器地址,端口,主机名,regionserver的负载信息HServerLoad
(2)HRegionInfo:包括region id,起、止Key,region包含的table信息,是否在线,regionName等
(3)HMsg:HMaster和HRegionServer通信的指令,master发送给 region server的指令包括:启动指定region;停止指定region;分裂region;compact region;停止regionserver;停止指定region而无需回复;停止所有用户region,从region server到master的指令:regionserver正在服务指定region;regionserver已不再服务指定 region;regionserver正在关闭;已停用所有用户region;Flush缓存;运行Major Compaction;分裂指定region
4.4 交互协议
HRegionServer启动时:
(1)HRegionServer获取服务端协议版本并比较
(2)HRegionServer调用HMasterRegionInterface的 regionServerStartup接口,报告该regionserver已启动;HMaster将regionserver的管理都交给 ServerManager;HMaster返回hbase.regionserver.address,fs.default.name和 hbase.rootdir等配置信息
(3)然后定期(hbase.regionserver.msginterval)定期调用HMasterRegionInterface的regionServerReport接口报告regionserver的状态,并接受指令
4.5 master对regionserver的管理:ServerManager
ServerManager维护三个Map记录可用regionserver状况,serversToServerInfo:从serverName到其HServerInfo的映射;
serversToLoad:从serverName到其最近HServerLoad的映射;
loadToServers:从HServerLoad到其serverName集合的映射(相同负载的regionserver);
一个SetdeadServers维护已死亡的regionserver信息;
(1)regionserver启动后向master报告:
a.ServerManager的 regionServerStartup方法先检查是否已存在相同的regionserver,若存在且通过startCode判断其是否 stale(???),若stale则终止该regionserver:删除该regionserver对应的HServerInfo、 HServerLoad,加入到deadServers队列,并向master的RegionServerOperation队列中增加一个关闭该 server操作,等待处理
b.检查该regionserver是否在deadServers队列
c.然后在zookeeper的/hbase/rs/增加Znode中记录新的regionserver,并向该Znode增加一个ServerExpirer,在该节点被删除后expire该regionserver
d.serversToServerInfo,serversToLoad,loadToServers增加相应记录
(2)处理regionserver的heart beat报告(这一部分逻辑比较复杂)
a.检查该regionserver是否已经dead(与deadServers中的记录进行比较)
b.第一条HMsg是:regionserver正在关闭,从 serversToServerInfo中删除该regionserver;调用RegionManager,使该regionserver上的所有 meta和root region下线;检查是否有root和meta region正在该server启动,有则重新分配该region ;从serversToLoad和loadToServers从删除该regionserver的load信息;然后依次读取后面每条msg,msg类型 必需为:停止region;通过master的RegionManager来管理region,最后向regionserver返回空的HMsg.
c.第一条HMsg是:MSG_REPORT_QUIESCED,即regionserver关闭所有user regions,则quiescedServers记录该状态regionserver的数据;当master接受到shutdown请求时,而所有 regionserver仅服务metar regions则关闭master
d.若master已关闭,则返回REGIONSERVER_STOP的msg
e.若serversToServerInfo中查不到该regionserver,也向regionserver发送REGIONSERVER_STOP的msg
f.前面处理掉所有异常情况,然后调用processRegionServerAllsWell 处理regionserver的HMsg:更新serversToServerInfo,serversToLoad,loadToServers中 regionserver对应的信息;依次处理每条HMsg:regionserver正在处理open指定region的请求:累加该指令次数 regionserver已open指定region:若region被重复打开了,则回复regionsever关闭;是root region,则从未分配region列表中删除,并记录root region所在regionserver地址;非root region,设置状态为open,向RegionServerOperationQueue添加ProcessRegionOpen操作,连 接至该region的meta region所在regionserver,该regionserver客户端获得HRegionInterface的代理,put该region的信息 到meta region中(key为region名,CATALOG_FAMILY下有主机和端口、startCode两列);若region是meta region,则加入RegionManager中的扫描队列中。regionserver已关闭指定region:如果是root region则清理RegionManager中roo向master的RegionServerOperationQueue中增加ProcessRegionClose操作:MSG_REPORT_SPLIT,MSG_REPORT_SPLIT_INCLUDES_DAUGHTERS,regionserver分裂region操作:都委托给RegionManager
(3)ServerMonitor线程,定期dump deadServers和regionserver数量及每个regionserver的平均服务region数量
4.6 regionserver处理master返回的HMsg:regionserver将master返回的消息放入一队列中,由其worker线程从队列中依次取出并处理
(1)处理MSG_REGIONSERVER_QUIESCE,关闭所有user region:从HRegionServer记录的所有online regions中选出所有user region,每个region启动一个线程进行执行关闭
(2)MSG_REGION_OPEN,打开指定region:若整个集群的root region还没打开,则不处理该指令,并重新放回队列中;
(3)MSG_REGION_CLOSE,关闭指定region:从online regions中选出要关闭的region,关闭该region,并给master回复已关闭的信息
(4)MSG_REGION_SPLIT,分裂指定region:先flush缓存数据;设置region的标识变量splitRequest为true;region放入CompactSplitThread线程的compactionQueue队列中等待分裂
(5)MSG_REGION_MAJOR_COMPACT和MSG_REGION_COMPACT:region放入CompactSplitThread线程的compactionQueue队列中等待Compaction
(6)MSG_REGION_FLUSH:flush指定region
五、HMaster对HRegion的管理
HMaster对HRegion的管理:master将region的分派,状态管理等工作委托给RegionManager
(1)RegionManager成员变量:rootRegionLocation,root region所在regionserver地址;rootScannerThread和MetaScanner,root及meta region的扫描线程;numberOfMetaRegions,meta regions数量由rootScannerThread设置;onlineMetaRegions,在线的meta regions;regionsInTransition,记录正在状态变化中的region,region的状态有:
UNASSIGNED-等待分配到server
PENDING_OPEN-告诉server打开,但还未完成
OPEN-已打开region,但尚未在meta region中标识
CLOSING-已将close msg放入队列中,但还尚未发送到regionserver
PENDING_CLOSE-已将regionserver发送给regionserver
CLOSED-已关闭region,但尚未在meta region中标识
maxAssignInOneGo,一次分配给一个regionserver的最多region数量;LoadBalancer,负责 regionserver间负载均 衡;regionsToSplit,regionsToCompact,regionsToMajorCompact,regionsToFlush,需 split/compact/MajorCompact/Flush的region
(2)RegionManager工作流程:
HMaster初始化时创建RegionManager实例;
清除RegionManager中root region的地址及状态信息,设置root region状态为UNASSIGNED并加入regionsInTransition中;
HMaster启动各种后台服务时,RegionManager后台启动rootScannerThread和metaScannerThread线程;
regionserver向master报告时,ServerManage在regionServerReport方法中调用RegionManager来处理跟region相关的msg.
(3)RegionManager处理各种Region相关HMsg
a.regionserver正在关闭
RegionManager关闭该 regionserver上的所有region,包括root,meta和user region,并对root meta和online的user regionr重新分配,对offline user region从regionsInTransition删除其状态信息
b.regionserver已open指定region
RegionManager判断该 region是否重复分配了,重复分配则返回关闭该region指令;若open的是root region,则在master的ServerConnection和RegionManager中记录root region所在地址;若open user region,具体操作参见ServerManager
c.regionserver已关闭指定region:
关闭root region
关闭meta region:从RegionManager的onlineMetaRegions中删除该region;
regionsInTransition中设置该region为CLOSED;
向master的RegionServerOperationQueue中增加ProcessRegionClose操作:往meta中写入该region offline状态,或若该region需重新分配则RegionManager设置该region待分配状态
d.regionserver已split某个region:
读取连续的后两条HMsg从中取出分裂产生的两个HRegionInfo;
从regionsToSplit和regionsToCompact中删除被分裂的HRegionInfo;
从onlineMetaRegions中查找应包含新region的meta region,并尝试从对应的regionserver中读取新region,若存在则不做处理,不存在则将新region设置成待分配状态;
若meta region被分裂,则从onlineMetaRegions中删除掉该region,并增加meta region的数量numberOfMetaRegions;
(4)组织返回给regionserver的HMsg:
a.对一个标记为需Close的region,增加一条MSG_REGION_CLOSE的HMsg,并在中regionsInTransition设置其状态为PENDING_CLOSE
b.若正执行open region的数量少于hbase.regions.nobalancing.count指定的值,则从regionsInTransition中获取待分配的region(root region优先)
c.若无region分配给该regionserver,则比较 该regionserver的region负载数量和整个集群的平均负载+slop系数(由参数hbase.regions.slop指定),由一算法 (balanceFromOverloaded和balanceToLowloaded方法),计算该regionserver需close的 region数量,并从负载最严重的region中选择指定数量的非root、meta region和不在regionsInTransition中的region,发出HMsg在该regionserver中close这些region, 并设置状态为PENDING_CLOSE
d.综合考虑多个整个集群的负载, 将需分配的region分配到多个regionserver:
通过ServerManager找出负载最轻的那些regionserver,分摊一些region到这些regionserver;
doRegionAssignment方法分配一个region到指定regionserver,设置状态状态为PENDING_OPEN,并发送MSG_REGION_OPEN消息;
e.从regionsToCompact,regionsToSplit,regionsToFlush,regionsToMajorCompact中获取需要执行对应操作的region并发送对应HMsg
(5)MetaScanner和RootScanner线程
这两个线程都继承自BaseScanner,通过初始化时扫描 root和meta region可以知道所有的region并进行分配;当分裂一个region时,regionserver会通知master分配新region,但 master可能会丢失这些分裂信息,而新region通常没有regionserver的信息,因此周期性地扫描发现这些新region并进行分配。
a.RootScanner运行时机:master收到regionserver的root region被open指令;首次扫描后定期自动扫描,扫描过程描述:
从RegionManager获取root region所在rs地址;
通过master的ServerConnection获取HRegionInterface接口代理,遍历root region内所有meta region的记录;
检查region对应的HServerInfo是否存在,不存在则表示尚未分配rs,加入到regionsInTransition,设置状态为UNASSIGNED;
检查所有已分裂的parent,
b.MetaScanner运行时机:当一个meta region上线;定期自动扫描上线的meta region;扫描过程逻辑类似RootScanner
六、数据模型HTableDescriptor
HBase中表结构由HTableDescriptor描述(包括HColumnDescriptor),对表的新增\修改\删除操作在接口HMasterInterface中定义,而该接口由HMaster实现
(1)HTableDescriptor包含:
表名,byte[]和String格式;
表的元信息,以key-value形式存储,包括文件最大的大小(默认256M)、是否只读、flush时内存占用大小(默认64M)、是否root或meta region、DEFERRED_LOG_FLUSH;
表的各Family描述HColumnDescriptor;
(2)HColumnDescriptor:描述column family的信息,包括:
压缩格式(不压缩,仅压缩value,压缩block中的一系列记录);
数据的版本数量;
block的大小
是否在内存中;
是否cache block;
是否使用bloomfilter;
cell内容的存活时间(ttl);
是否复制。
当一个column family创建后,其参数不能修改,除非删除掉该column family后新建一个,但删除column family也会删除掉该column family下的数据.另外,HTableDescriptor中包含ROOT_TABLEDESC和META_TABLEDESC两个实例以描述 root和meta表:
ROOT_TABLEDESC包含一个info的column family,META_TABLEDESC包含一个info和historian两个column family
(3)建表过程(HMaster的createTable方法):若指定了splitKeys则为该table按指定键初始创建多个region,否则仅创建一个region
a.为table创建HRegionInfo
b.判断是不是所有的meta region都online(由RegionManager的MetaScanner扫描线程分配meta region)
c.判断serverManager是否有足够regionserver来创建table
d.根据步骤a中新建的HRegionInfo创建table:
从RegionManager的 onlineMetaRegion查找该HRegionInfo应放入哪一个meta region中:在onlineMetaRegion中查找仅比regionName小的meta region,而regionName由tableName,起始Key,和regionId(root为0,meta为1,user当前时间)组成,同 过master的ServerConnection获取HRegionInterface代理连接到该meta region,并查找对应该table为Key的记录是否存在,若存在则报错该表已存在,由RegionManager根据HRegionInfo创建新 的user region:在rootDir目录下新建以tableName为名的目录,在tableName目录下新建一个region的目录(经编码后的 regionName),并新建一个HRegion对象
将region对应记录放入对应meta region中;
设置该region状态为未分配,等待分配给regionserver.
(4)对表的其它操作:disable,enable,delete,这些操作封装在继承自TableOperation的类中
该类先获得要操作表的所有meta region,扫描这些meta region中所有该表的user region信息并做相应处理,最后处理meta region。
七、数据存储
regionserver负责region内数据的存储及处理客户端的读写请求:
写请求:当regionserver接受到写请求,先写入一个WAL(write-ahead log) HLog中(同一regionserver的所有log写入同一文件中);然后再写入缓存HStore中;
读请求:先检查HStore中是否存在被请求数据,不存在则读HFiles
Flush缓存:当缓存达到配置的大小,则flush到磁盘,写入到新的HFile,并在HLog中做一个标记。(当重做该日志时,忽略上次标记前的记录)Flush时regionserver仍会将读写请求挂起,直至新的HFile产生成功
Compaction:当flush产生的MapFile数量过多时,会运行compaction合并多个HFile并产生新的Map,以减少文件数量并清除过期和已被删除数据。合并的形式为:
minor compaction,合并近期产生的几个HFile
major compaction,合并所有HFile
Split:当一个region的所有HFiles大小超过指定值(256M),则将父region分为两半,但子region仍从父的MapFile读 取。当一个子region触发了compaction,则会复制数据到自己的目录,当两个子region均执行过compaction,父region将 会被回收由此可见,HBase数据有三种不同存储:HLog,HStore和HFile,并由HRegion管理
(1)写(Put)过程:
HRegionServer实现HRegionInterface接口,该接口定义了对region读写操作的接口,客户端获取该接口在本地的代理,调用put系列方法写入数据;
a.首先,HRegionServer检查该regionserver是否正在关闭、文件系统是否可用
b.从onlineRegions中获得要写入的 HRegion,并检查该regionserver上所有region的使用的缓存大小是否超过上限,则逐个flush占用内存最大的region,并将 该region加入compactSplitThread线程的队列中以等待compact
c.调用HRegion的put方法写入数据
d.检查HRegion是否处于只读模式
e.检查HRegion的Memstore大小是否超过hbase.hregion.memstore.flush.size * hbase.hregion.memstore.block.multiplier,若超过了则请求flush并阻塞写操作
f.doMiniBatchPut方法写入数据,对每个Put,先根据HRegionInfo检查是否存在对应的column family
g.获取与要操作的行对应的Lock:若Client提供了Lock,则判断给的LockId是否存在对应的行;Client未提供Lock,则产生一个lock,并保存(internalObtainRowLock方法)
h.设置插入cell的value的timestamp(updateKVTimestamps方法)
i.将Put操作写入到WALEdit(Write ahead log),并append到HLog中(addFamilyMapToWALEdit和HLog.append方法)
j.写入到memstore中,由ReadWriteConsistencyControl保持读写一致性,调用Store.add方法
k.释放锁并返回写入数据所占空间的大小
(2)读(Get)过程:HRegionServer实现HRegionInterface接口,该接口提供get方法
a.首先,HRegionServer检查该regionserver是否正在关闭、文件系统是否可用
b.从onlineRegions中获得要读取的HRegion,调用HRegion的get方法
c.若Get设置了column family,检查是否存在;否则读取HTableDescriptor中所有的column family
d.根据Get创建对应Scan实例,设置startRow、stopRow、filter、maxVersions、TimeRange等(Get操作对应的Scan实例startRow=stopRow)
e.HBase定义了两个接口InternalScanner和 KeyValueScanner,InternalScanner用于在regionserver上读取下一行指定列的所有键值对 KeyValueScanner用于从各种存储中读取下一个键-值对。在查询中涉及到各个实现见下图:
RegionScanner:实现InternalScanner接口,它对要扫描的column family对应的Store创建KeyValueScanner实例(StoreScanner)(每个column family中的列存储在同一Store中),并将这些StoreScanner的实例交给KeyValueHeap管理;调用其next(List results)方法时,委托给各个column family的StoreScanner。
StoreScanner:实现InternalScanner和KeyValueScanner接 口,创建对StoreFile和MemStore进行扫描的KeyValueScanner实例(StoreFileScanner和 MemeStore),并将这些Scanner交给KeyValueHeap管理,而StoreFileScanner对HFile文件的读取又交给了 HFileScanner。