和写流程相比,HBase读数据是一个更加复杂的操作流程,这主要基于两个方面的原因:其一是因为整个HBase存储引擎基于LSM-Like树实现,因此一次范围查询可能会涉及多个分片、多块缓存甚至多个数据存储文件;其二是因为HBase中更新操作以及删除操作实现都很简单,更新操作并没有更新原有数据,而是使用时间戳属性实现了多版本。删除操作也并没有真正删除原有数据,只是插入了一条打上”deleted”标签的数据,而真正的数据删除发生在系统异步执行Major_Compact的时候。很显然,这种实现套路大大简化了数据更新、删除流程,但是对于数据读取来说却意味着套上了层层枷锁,读取过程需要根据版本进行过滤,同时对已经标记删除的数据也要进行过滤。
总之,把这么复杂的事情讲明白并不是一件简单的事情,为了更加条理化地分析整个查询过程,接下来笔者会用两篇文章来讲解整个过程,首篇文章主要会从框架的角度粗粒度地分析scan的整体流程,并不会涉及太多的细节实现。大多数看客通过首篇文章基本就可以初步了解scan的工作思路;为了能够从细节理清楚整个scan流程,接着第二篇文章将会在第一篇的基础上引入更多的实现细节以及HBase对于scan所做的基础优化。因为理解问题可能会有纰漏,希望可以一起探讨交流,欢迎拍砖~
Client-Server交互逻辑
运维开发了很长一段时间HBase,经常有业务同学咨询为什么客户端配置文件中没有配置RegionServer的地址信息,这里针对这种疑问简单的做下解释,客户端与HBase系统的交互阶段主要有如下几个步骤:
- 客户端首先会根据配置文件中zookeeper地址连接zookeeper,并读取/<hbase-rootdir>/meta-region-server节点信息,该节点信息存储HBase元数据(hbase:meta)表所在的RegionServer地址以及访问端口等信息。用户可以通过zookeeper命令(get /<hbase-rootdir>/meta-region-server)查看该节点信息。
- 根据hbase:meta所在RegionServer的访问信息,客户端会将该元数据表加载到本地并进行缓存。然后在表中确定待检索rowkey所在的RegionServer信息。
- 根据数据所在RegionServer的访问信息,客户端会向该RegionServer发送真正的数据读取请求。服务器端接收到该请求之后需要进行复杂的处理,具体的处理流程将会是这个专题的重点。
通过上述对客户端以及HBase系统的交互分析,可以基本明确两点:
- 客户端只需要配置zookeeper的访问地址以及根目录,就可以进行正常的读写请求。不需要配置集群的RegionServer地址列表。
- 客户端会将hbase:meta元数据表缓存在本地,因此上述步骤中前两步只会在客户端第一次请求的时候发生,之后所有请求都直接从缓存中加载元数据。如果集群发生某些变化导致hbase:meta元数据更改,客户端再根据本地元数据表请求的时候就会发生异常,此时客户端需要重新加载一份最新的元数据表到本地。
-----------------此处应有华丽丽的分隔线----------------
RegionServer接收到客户端的get/scan请求之后,先后做了两件事情:构建scanner体系(实际上就是做一些scan前的准备工作),在此体系基础上一行一行检索。举个不太合适但易于理解的例子,scan数据就和开发商盖房一样,也是分成两步:组建施工队体系,明确每个工人的职责;一层一层盖楼。
构建scanner体系-组建施工队
scanner体系的核心在于三层scanner:RegionScanner、StoreScanner以及StoreFileScanner。三者是层级的关系,一个RegionScanner由多个StoreScanner构成,一张表由多个列族组成,就有多少个StoreScanner负责该列族的数据扫描。一个StoreScanner又是由多个StoreFileScanner组成。每个Store的数据由内存中的MemStore和磁盘上的StoreFile文件组成,相对应的,StoreScanner对象会雇佣一个MemStoreScanner和N个StoreFileScanner来进行实际的数据读取,每个StoreFile文件对应一个StoreFileScanner,注意:StoreFileScanner和MemstoreScanner是整个scan的最终执行者。
对应于建楼项目,一栋楼通常由好几个单元楼构成(每个单元楼对应于一个Store),每个单元楼会请一个监工(StoreScanner)负责该单元楼的建造。而监工一般不做具体的事情,他负责招募很多工人(StoreFileScanner),这些工人才是建楼的主体。下图是整个构建流程图:
- RegionScanner会根据列族构建StoreScanner,有多少列族就构建多少StoreScanner,用于负责该列族的数据检索
1.1 构建StoreFileScanner:每个StoreScanner会为当前该Store中每个HFile构造一个StoreFileScanner,用于实际执行对应文件的检索。同时会为对应Memstore构造一个MemstoreScanner,用于执行该Store中Memstore的数据检索。该步骤对应于监工在人才市场招募建楼所需的各种类型工匠。
1.2 过滤淘汰StoreFileScanner:根据Time Range以及RowKey Range对StoreFileScanner以及MemstoreScanner进行过滤,淘汰肯定不存在待检索结果的Scanner。上图中StoreFile3因为检查RowKeyRange不存在待检索Rowkey所以被淘汰。该步骤针对具体的建楼方案,裁撤掉部分不需要的工匠,比如这栋楼不需要地暖安装,对应的工匠就可以撤掉。
1.3 Seek rowkey:所有StoreFileScanner开始做准备工作,在负责的HFile中定位到满足条件的起始Row。工匠也开始准备自己的建造工具,建造材料,找到自己的工作地点,等待一声命下。就像所有重要项目的准备工作都很核心一样,Seek过程(此处略过Lazy Seek优化)也是一个很核心的步骤,它主要包含下面三步:
- 定位Block Offset:在Blockcache中读取该HFile的索引树结构,根据索引树检索对应RowKey所在的Block Offset和Block Size
- Load Block:根据BlockOffset首先在BlockCache中查找Data Block,如果不在缓存,再在HFile中加载
- Seek Key:在Data Block内部通过二分查找的方式定位具体的RowKey
整体流程细节参见《HBase原理-探索HFile索引机制》,文中详细说明了HFile索引结构以及如何通过索引结构定位具体的Block以及RowKey
1.4 StoreFileScanner合并构建最小堆:将该Store中所有StoreFileScanner和MemstoreScanner合并形成一个heap(最小堆),所谓heap是一个优先级队列,队列中元素是所有scanner,排序规则按照scanner seek到的keyvalue大小由小到大进行排序。这里需要重点关注三个问题,首先为什么这些Scanner需要由小到大排序,其次keyvalue是什么样的结构,最后,keyvalue谁大谁小是如何确定的:
- 为什么这些Scanner需要由小到大排序?
最直接的解释是scan的结果需要由小到大输出给用户,当然,这并不全面,最合理的解释是只有由小到大排序才能使得scan效率最高。举个简单的例子,HBase支持数据多版本,假设用户只想获取最新版本,那只需要将这些数据由最新到最旧进行排序,然后取队首元素返回就可以。那么,如果不排序,就只能遍历所有元素,查看符不符合用户查询条件。这就是排队的意义。
工匠们也需要排序,先做地板的排前面,做墙体的次之,最后是做门窗户的。做墙体的内部还需要再排序,做内墙的排前面,做外墙的排后面,这样,假如设计师临时决定不做外墙的话,就可以直接跳过外墙部分工作。很显然,如果不排序的话,是没办法临时做决定的,因为这部分工作已经可能做掉了。
- HBase中KeyValue是什么样的结构?
HBase中KeyValue并不是简单的KV数据对,而是一个具有复杂元素的结构体,其中Key由RowKey,ColumnFamily,Qualifier ,TimeStamp,KeyType等多部分组成,Value是一个简单的二进制数据。Key中元素KeyType表示该KeyValue的类型,取值分别为Put/Delete/Delete Column/Delete Family等。KeyValue可以表示为如下图所示:
了解了KeyValue的逻辑结构后,我们不妨再进一步从原理的角度想想HBase的开发者们为什么如此对其设计。这个就得从HBase所支持的数据操作说起了,HBase支持四种主要的数据操作,分别是Get/Scan/Put/Delete,其中Get和Scan代表数据查询,Put操作代表数据插入或更新(如果Put的RowKey不存在则为插入操作、否则为更新操作),特别需要注意的是HBase中更新操作并不是直接覆盖修改原数据,而是生成新的数据,新数据和原数据具有不同的版本(时间戳);Delete操作执行数据删除,和数据更新操作相同,HBase执行数据删除并不会马上将数据从数据库中永久删除,而只是生成一条删除记录,最后在系统执行文件合并的时候再统一删除。
HBase中更新删除操作并不直接操作原数据,而是生成一个新纪录,那问题来了,如何知道一条记录到底是插入操作还是更新操作亦或是删除操作呢?这正是KeyType和Timestamp的用武之地。上文中提到KeyType取值为分别为Put/Delete/Delete Column/Delete Family四种,如果KeyType取值为Put,表示该条记录为插入或者更新操作,而无论是插入或者更新,都可以使用版本号(Timestamp)对记录进行选择;如果KeyType为Delete,表示该条记录为整行删除操作;相应的KeyType为Delete Column和Delete Family分别表示删除某行某列以及某行某列族操作;
- 不同KeyValue之间如何进行大小比较?
上文提到KeyValue中Key由RowKey,ColumnFamily,Qualifier ,TimeStamp,KeyType等5部分组成,HBase设定Key大小首先比较RowKey,RowKey越小Key就越小;RowKey如果相同就看CF,CF越小Key越小;CF如果相同看Qualifier,Qualifier越小Key越小;Qualifier如果相同再看Timestamp,Timestamp越大表示时间越新,对应的Key越小。如果Timestamp还相同,就看KeyType,KeyType按照DeleteFamily -> DeleteColumn -> Delete -> Put 顺序依次对应的Key越来越大。
2. StoreScanner合并构建最小堆:上文讨论的是一个监工如何构建自己的工匠师团队以及工匠师如何做准备工作、排序工作。实际上,监工也需要进行排序,比如一单元的监工排前面,二单元的监工排之后… StoreScanner一样,列族小的StoreScanner排前面,列族大的StoreScanner排后面。
scan查询-层层建楼
构建Scanner体系是为了更好地执行scan查询,就像组建工匠师团队就是为了盖房子一样。scan查询总是一行一行查询的,先查第一行的所有数据,再查第二行的所有数据,但每一行的查询流程却没有什么本质区别。盖房子也一样,无论是盖8层还是盖18层,都需要一层一层往上盖,而且每一层的盖法并没有什么区别。所以实际上我们只需要关注其中一行数据是如何查询的就可以。
对于一行数据的查询,又可以分解为多个列族的查询,比如RowKey=row1的一行数据查询,首先查询列族1上该行的数据集合,再查询列族2里该行的数据集合。同样是盖第一层房子,先盖一单元的一层,再改二单元的一层,盖完之后才算一层盖完,接着开始盖第二层。所以我们也只需要关注某一行某个列族的数据是如何查询的就可以。
还记得Scanner体系构建的最终结果是一个由StoreFileScanner和MemstoreScanner组成的heap(最小堆)么,这里就派上用场了。下图是一张表的逻辑视图,该表有两个列族cf1和cf2(我们只关注cf1),cf1只有一个列name,表中有5行数据,其中每个cell基本都有多个版本。cf1的数据假如实际存储在三个区域,memstore中有r2和r4的最新数据,hfile1中是最早的数据。现在需要查询RowKey=r2的数据,按照上文的理论对应的Scanner指向就如图所示:
这三个Scanner组成的heap为<MemstoreScanner,StoreFileScanner2, StoreFileScanner1>,Scanner由小到大排列。查询的时候首先pop出heap的堆顶元素,即MemstoreScanner,得到keyvalue = r2:cf1:name:v3:name23的数据,拿到这个keyvalue之后,需要进行如下判定:
- 检查该KeyValue的KeyType是否是Deleted/DeletedCol等,如果是就直接忽略该列所有其他版本,跳到下列(列族)
- 检查该KeyValue的Timestamp是否在用户设定的Timestamp Range范围,如果不在该范围,忽略
- 检查该KeyValue是否满足用户设置的各种filter过滤器,如果不满足,忽略
- 检查该KeyValue是否满足用户查询中设定的版本数,比如用户只查询最新版本,则忽略该cell的其他版本;反正如果用户查询所有版本,则还需要查询该cell的其他版本。
现在假设用户查询所有版本而且该keyvalue检查通过,此时当前的堆顶元素需要执行next方法去检索下一个值,并重新组织最小堆。即图中MemstoreScanner将会指向r4,重新组织最小堆之后最小堆将会变为<StoreFileScanner2, StoreFileScanner1, MemstoreScanner>,堆顶元素变为StoreFileScanner2,得到keyvalue=r2:cf1:name:v2:name22,进行一系列判定,再next,再重新组织最小堆…
不断重复这个过程,直至一行数据全部被检索得到。继续下一行…
----------------此处应有华丽丽的分隔符----------------
本文从框架层面对HBase读取流程进行了详细的解析,文中并没有针对细节进行深入分析,一方面是担心个人能力有限,引入太多细节会让文章难于理解,另一方面是大多数看官可能对细节并不关心,下篇文章笔者会揪出来一些比较重要的细节和大家一起交流~
文章最后,贴出来一个一些朋友咨询的问题:Memstore在flush的时候会不会将Blockcache中的数据update?如果不update的话不就会产生脏读,读到以前的老数据?
这个问题大家可以思考思考,我们下一篇文章再见~
问个不相关的问题,有没有hbase安全相关的文章介绍?
hbase安全相关是指权限管理?
是的,类似此类。
安全性个人理解主要包括权限隔离、资源隔离两个部分。权限隔离主要解决租户之间不能互相访问的问题,目前hbase的权限管理比较简单,基于kerberos的角色权限认证,可以参考kerberos相关的文章。(如果有兴趣也可以了解下ranger,可以更加细粒度的处理HBase的权限问题,不过目前还不普及);资源隔离主要解决租户业务之间不会相互干扰的问题,目前HBase还没有很有效的方案,后续应该在2.0版本会推出rsgroup方案,可参考HBase-6721
恩,好的,谢谢了
博主你好,Hbase动态添加的regionserver节点一定要部署在hdfs节点上吗?如果不需要的话,hdfs是配置成HA的,core-site.xml中配置是:
fs.defaultFS
hdfs://mycluster
hdfs-site.xml的配置是:
dfs.nameservices
mycluster
dfs.ha.namenodes.mycluster
nn1,nn2
hbase-site.xml对应配置是:
hbase.rootdir
hdfs://mycluster/hbase
若regionserver部署在一个和hdfs不相关的节点上,regionserver不识别mycluster要怎么办呢?
我试过在动态添加的regionserver节点上,把master节点的别名配成mycluster添加到hosts中去,这样是可以的。不过mycluster应该是主备两个节点的,这样只添加一个节点是不是有问题的?原来的Hbase集群各节点和HDFS集群各节点是一一对应的。
最简单的方法,把core-site.xml和hdfs-site.xml复制到hbase的配置文件夹下//conf,应该就可以了
恩恩。博主再问您个问题:为什么增大memstore会造成插入性能的下降呢?测试好多次,几乎都是线性下降的。
增大memstore是指增大参数“hbase.regionserver.global.memstore.size”还是”hbase.hregion.memstore.flush.size”?线性下降是什么意思?
1、增大JVM是指:我的block cache配置成LRU模式,保持memstore的上线水线和hfile.block.cache.size不变,通过增大JVM大小达到增大memstore的目的。
2、线性下降是指:JVM配置的越大,插入吞吐越低。
3、我也试过JVM不变只改变memstore上下水线,这样测出的结果是改变memstore大小对插入吞吐影响不大。
1、增大memstore是指:我的block cache配置成LRU模式,保持memstore的上线水线和hfile.block.cache.size不变,通过增大JVM大小达到增大memstore的目的。
2、线性下降是指:JVM配置的越大,插入吞吐越低。
3、我也试过JVM不变只改变memstore上下水线,这样测出的结果是改变memstore大小对插入吞吐影响不大。
这是因为写入性能瓶颈基本不在内存大小,而在hlog。增大jvm反而会使得gc性能变差,进而会使得写入性能降低。
MemStore基于跳表,跳表在数据量达到一定规模后,性能就会恶化。可能是因为这个,所以才造成了“增大JVM,写入反而下降”的现象。
不是特别最小堆那里,同一行的数据,Memstore刚开始不是最大吗?为什么会排前面,还是最小堆负责维护Rowkey从小到大,然后在同一Rowkey里,按时间戳从大到小读?
同一rowkey,同一cf,同一column,时间戳越大,kv越小
噢,谢谢。还有一点不是特别明白,HFile是针对一个列族的,里面所有数据都是同样列族,KeyValue里为何还需要把列族重复存那么多次,为何不直接把列族名放在HFile的描述信息里。
这个问题以前也思考过 不过没有想到特别合理的解释 个人感觉这样设计在cf很小的情况下可以更加方便地执行各种操作
我也特别纠结这个地方,如果考虑数据压缩,列族名确实是可以提出来放到每个Hfile的公共描述信息里,构造查询结果时加上相关值即可,压缩成本一点不大,为何HFile中每个KeyValue中都要加上列族名,一直迷惑不解!!!
期待下一篇深入分析
hbase-client 访问一定需要配置regionserver 的hosts 映射么? 有没有其他的办法解决这个问题呢?
hbase-client访问不需要配置hosts映射 只需要配置文件中配置ZK地址就可以 配置hosts映射可能是因为你的域名访问有问题吧
用phoenix客户端连接hbase一定要在后台应用部署的节点所在服务器/etc/hosts中配置全部RegionServer的域名映射么?有没有更好的方式?
必须用域名 我们公司内部都使用域名的
博主你好,”客户端会将hbase:meta元数据表缓存在本地”这里,是将meta表整张表数据缓存到本地,还是将表内对应的某几条记录缓存到本地?
是将之前访问过的region信息缓存,不会缓存所有数据
读取过程中,通过PriorityQueue以小顶堆的形式组织scanner,实现对各个数据分片的顺序扫描和读取,这招确实很妙!
这个思路其实在很多具有数据分片形式的分布式系统中都有用到,确实是一个比较好的实践。
题主您好,请教几个问题:
1.memstore flush是否更新blockcache ?
2.hbase的L1 ,L2 cache 是指什么意思?为什么我配置了blockcache, offheap,combinedbucket 0.8,发现lru 特别大,offheap 好像没有多少?是没成功还是需要另外配置?如何看这个off heap ?
3.offheap 是否在jvm xmx 堆内存中,我理解应该不在。那么rs 占用内存是不是xmx +offheap ?
4.keyvalue 的设计问题:hbase 一行数据200列的话,我看代码存储就是200个kv,每个kv 包含了row, cf, timestamp 等。很多情况下是一次put 一行,很多列,这些列时间戳也都相同,这样存储感觉很浪费。特别是列值比较短的情况下,设计200列与将200列拼接在一起设计为一列对比,可能数据膨胀20倍。性能差距也很明显。这个是出于什么考虑?还是我理解错误?这个我一直没想明白。
题主您好,请教几个问题:
1.memstore flush是否更新blockcache ?
2.hbase的L1 ,L2 cache 是指什么意思?为什么我配置了blockcache, offheap,combinedbucket 0.8,发现lru 特别大,offheap 好像没有多少?是没成功还是需要另外配置?如何看这个off heap ?
3.offheap 是否在jvm xmx 堆内存中,我理解应该不在。那么rs 占用内存是不是xmx +offheap ?
4.keyvalue 的设计问题:hbase 一行数据200列的话,我看代码存储就是200个kv,每个kv 包含了row, cf, timestamp 等。很多情况下是一次put 一行,很多列,这些列时间戳也都相同,这样存储感觉很浪费。特别是列值比较短的情况下,设计200列与将200列拼接在一起设计为一列对比,可能数据膨胀20倍。性能差距也很明显。这个是出于什么考虑?还是我理解错误?这个我一直没想明白。
谢谢您。
1. flush不会更新blockcache
2. L1 cache主要存储元数据(索引、bloomfilter等),L2 cache主要存储实际数据块。offheap好像没办法直接看,只能去top看当前regionserver内存总申请内存与jvm heap的差值
3. offheap不在xmx配置里面。rs占用内存=xmx+offheap
4. 这个问题目前我也解释不清楚。。。。
非常感谢耐心回答。
第4个问题,我们应用也碰到过。我觉得主要看应用的实际情况,进行取舍吧。
1. 设计200列
【优点】:适合较复杂的检索,比如在列/列值上加filter,建二级索引,还有就是动态列(有值才会真正保存数据)适合稀疏矩阵数据的存储;
【缺点】:如果每行每列 都有值(比如默认值是特定值,而不是空),也就是说不是稀疏矩阵类型的数据,这样的话,确实比较浪费空间。
2. 200列拼接在一起
【优点】:节省空间,比如你的需求就是根据rowkey查value,可以将value设计成json串,取回客户端自己解析;
【缺点】:对比1,只能进行相对简单的查询,也没办法在列(业务列,200列)上进行二级索引,更加灵活的filter之类的。
博主您好,HBase作为列式存储,为什么它的scan性能这么低呢,列式存储不是更有利于scan操作么?Parquet格式也是列式,但它的scan这么优秀,他们的性能差异并不是因为数据组织方式造成的么?谢谢啦
这个问题是这样的:
1. HBase不完全是列式存储,确切的说是列族式存储,HBase中可以定义一个列族,列族下可以有都个列,这些列的数据是存在一起的。而且通常情况下我们建议列族个数不大于2个,这样的话每个列族下面必然会有很多列。因此HBase并不是列式存储,更有点像行式存储。
2. HBase扫描本质上是一个一个的随即读,不能做到像HDFS(Parquet)这样的顺序扫描。试想,1000w数据一条一条get出来,性能必然不会很好。问题就来了,HBase为什么不支持顺序扫描?
3. 这是因为HBase支持更新操作以及多版本的概念,这个很重要。可以说如果支持更新操作以及多版本的话,扫描性能就不会太好。原理是这样的,我们知道HBase是一个类LSM数据结构,数据写入之后先写入内存,内存达到一定程度就会形成一个文件,因此HBase的一个列族会有很多文件存在。因为更新以及多版本的原因,一个数据就可能存在于多个文件,所以需要一个文件一个文件查找才能定位出具体数据。
所以HBase架构本身个人认为并不适合做大规模scan,很大规模的scan建议还是用Parquet,可以把HBase定期导出到Parquet来scan
第一点理解,但是第二点有点不太理解,不知道博主有没有研究过Kudu,kudu也是采用的类LSM数据结构,但是却能达到parquet的扫描速度(kudu是纯列式的),kudu的一个列也会形成很多文件,但是好像并没影响它的性能
个人对Kudu不是很懂,不过旁边有Kudu大神。我的理解是这样的:
1. kudu性能并没有达到parquet的扫描速度,可以说介于HBase和HDFS(Parquet)之间
2. kudu比HBase扫描性能好,是因为kudu是纯列存,扫描不会出现跳跃读的情况,而HBase可能会跳跃seek,这是本质的区别。
3. 但kudu扫描性能又没有Parquet好,就是因为kudu是LSM结构,它扫描的时候还是会同时顺序扫描多个文件,并比较key值大小。而Parquet只需要顺序对一个block块中的数据进行扫描即可。这个是两者的区别。
所以说hbase相比parquet,这两个方面都是scan的劣势。
好的,非常感谢博主耐心解答
求教一个关于脏读的问题。
如果用MR做bulkload, 数据不经过memstore和blockcache直接进hfile, 如果某个key在bulkload之前已经存在并且被读取过, 那么数据已经在blockcache中, bulkload更新后再get应该会读到脏数据。如果hbase的get在缓存命中后还要扫描磁盘, 则缓存没有意义。 如果不扫描磁盘, 则会有脏数据, 不符合强一致性的要求。
经实测bulkload后能取到正确的数据而不是脏数据, 楼主对这个有研究吗?
下周二去面杭研大数据岗 面试不会问这么有深度的问题吧?
最近在看HBase的东西,博主写的很好,赞一个
根据hbase:meta所在RegionServer的访问信息,客户端会将该元数据表加载到本地并进行缓存。然后在表中确定待检索rowkey所在的RegionServer信息
关于这点,有个问题。如果client将整个hbase:meta表缓存到本地,那么假如这个表很大,则就非常占用本地jvm的内存大小了。能否按需加载呢。只缓存这个table的meta表数据呢。否则hbase:meta的元数据表则会占用client很多业务需要应有的内存空间了
不是加载整个hbase:meta表的,是用到哪些加载哪些
你好,博主! 我最近在学习hbase, 看完该文章后有一点不明白,在《scan查询-层层建楼》一节中r2的数据为什么会存在hfile1和hfile2中呢?
两个值的版本不一样,比如现在r2写进去一个值,这个值写到了hfile1。过一段时间又对这个值进行了更新,原来的值变成了老版本,这个值写到了hfile2。
一个HFile对应一个列族, 如果这个r2同时存在两个HFile中, 那就是说一个列族可以对应多个HFile吗?
不是一个HFile对应一个列族。一个列族的所有文件存储在一起,并不是一个一个文件,是一堆文件。
博主可以转载文章吗 我会注明原作者的。
可以的
已经关注博主 写得太好了 特意来评论一下
谢谢关注,多多交流
博主能分享下你的学习方式吗?比如要能像你这样深入掌握各式各样的大数据技术,你是怎么做的
刚好有一篇相关文章:http://hbasefly.com/2017/01/02/how-to-study/ 希望有所帮助
这篇文章非常好,非常感谢~
但是我有一些职业上的疑问:
1、比如说学精一门技术大概要多久时间?比如hbase这样的
2、单单大数据技术,就包含了很多杂七杂八的技术,好不容易学精了一门技术,过不了多久好几个新的技术又来了,之前学的技术顶多帮助后面学习更快一点,但是觉得很疲倦,一直奔波于追逐学习新技术,害怕落后被淘汰。范老师觉得自己学习中,会不会有这样的感觉?还是说,以后就觉得容易多了
1. 学精一门技术需要多长时间和很多因素有关系,比如是否专注、是否有实际线上的业务实践机会、是否有相关领域经验等等,像HBase这样的系统,从入门起,在有实践机会的情况下可能要专注花一两年时间才可能算熟悉吧
2. 技术非常多,选择一个领域专注就可以。在一个领域里面越学越简单,比如像HBase、OpenTSDB这些NoSQL领域都基本想通的。但如果跨领域学习,代价就会很大。
嗯,非常感谢范神的解答,我要努力加油了!!!
有个疑问,最开始客户端访问的时候,要先找meta,
所以先到zookeeper上去查看/hbase/meta-region-server下的信息,获取meta表的region归属哪个服务器管理
那获取到regionServer地址后,如何找到meta表呢?是该regionServer一起找吗?
最后一句写错,
那获取到regionServer地址后,如何找到meta表呢?是该regionServer下的所有region都找过去吗
具体没有仔细去看代码 meta-region-server里面应该有meta region的信息
看了,但是似乎没有~
下面是拿到的内容:
[zk: localhost:2181(CONNECTED) 4] get /hbase/meta-region-server
�regionserver:16020�7nX��PBUF
master�}��,
cZxid = 0x1f0000002d
ctime = Fri May 25 12:57:18 CST 2018
mZxid = 0x1f0000002d
mtime = Fri May 25 12:57:18 CST 2018
pZxid = 0x1f0000002d
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 59
numChildren = 0
请教个问题:
“1.2 过滤淘汰StoreFileScanner:根据Time Range以及RowKey Range对StoreFileScanner以及MemstoreScanner进行过滤,淘汰肯定不存在待检索结果的Scanner。上图中StoreFile3因为检查RowKeyRange不存在待检索Rowkey所以被淘汰。该步骤针对具体的建楼方案,裁撤掉部分不需要的工匠,比如这栋楼不需要地暖安装,对应的工匠就可以撤掉”,请问这里过滤StoreFile就是用前面章节中的提到的位数组机制来过滤的么,我理解是位数组只能过滤一小部分的HFile,不知道这样理解是否正确,谢谢。
这个过滤是根据TimeRange和RowKeyRange过滤的,每个HFile文件都会有一个TimeRange:[MinTime, MaxTime]和RowKeyRange:[MinRowKey, MaxRowKey],根据用户查询请求中给定time和rowkey判断是否在这个区间内,如果不在就可以淘汰这个HFile
非常感谢。再追问一下,之前的文章中说“BloomFilter对于HBase的随机读性能至关重要,对于get操作以及部分scan操作可以剔除掉不会用到的HFile文件,减少实际IO次数,提高随机读性能”, 请问StoreFileScanner的TimeRange、RowKeyRange过滤机制与BloomFilter的位数组过滤机制,是否有什么联系,或者说在查询过程中会不会都用到这两个机制? 如果是,他们之间在查询中处理逻辑先后顺序是如何的呢?谢谢。
查询过程先用TimeRange和RowKeyRange过滤 如果没有过滤掉 再用BloomFilter过滤
范神,一个数据量比较大的scan查询:1构建scanner体系,2.进行检索;分多次rpc进行检索并复用之前的scanner体系(?),问题1:如果scan超过我的容忍时间timeout,除了租约机制,有没有机制让client端主动销毁第一步构建的scanner体系,释放资源。问题2:多次rpc之间是每次rpc到来之后才进行后续的检索还是到来之前就把数据准备好。我的应用背景是当用户进行一次比较大如长达几个月的数据查询,timeout,client已经放弃这次scan,但它还是影响到后续别的查询,想一旦client想放弃这次scan,主动释放服务端构建的资源。
服务器端目前没办法主动停止释放资源,你这种场景下好像无解
这里想问个问题,memStore在写入的时候,会往里面写,在读的时候,也会往外读,那么这里的memStore的线程安全是如何保障呢?是不是读、写作为任务,放入一个队列,使用单线程进行一个一个处理,还是直接用锁?
读写是用MVCC保证读取数据的一致性,可以参考另一篇文章http://hbasefly.com/2017/07/26/transaction-2/
非常感谢,这文章之前读过,不过没和这个联想的太深,多谢指点!!
不过还有些不明白的是:现在知道memStore并发写是靠行锁来保证行数据的线程安全,读靠MVCC保证不会读到修改一半的数据,
但是还是没明白memStore这里如何保证线程安全,比如说,memStore这个东西,经常有人给它添加数据进来或者周期行的flush出去,那么读的用户在读memStore的时候,这个memStore的结构大小经常变动,那怎么解决的呢?
比如:
for(int i=0; i<memStore.length;i++) {
//进行读取,但是有时候memStore就flush出去了,或者添加数据进来,这里不就会出错了,如何解决?
}
memstore在flush之前会新创建一个新的skiplist接收新的写入,老的skiplist会执行flush操作,在flush未生成文件之前不会删除,直至生成文件之后
原来是这样,我之前还想是不是用读写锁,你说的hbase的这种方式效率应该会更高些
题主你好
HBase不适合做大范围的Scan是常识,但是取数据的调用是scanner.next(int rows),而建立Scanner的调用是getScanner(Scan scan)。所以调用getScanner的时候服务端做了什么呢?是已经把所有数据扫描完,等client取;还是有next(int rows)调用才取出rows条数据返回client呢?
调用next的时候才去取之后的n条记录返回
你好,咨询下有关写入问题,是先写入WAL还是Memstore。
1. 网上之前的版本大多数都说先写WAL为了保证灾难性后恢复,但是后来我看到开源的HBase已经注意到在某些特殊情况下,会导致内存在短时间不可见的问题,已经优化了先写Memstore后写WAL,那我想问下这种情况下,如何保证灾备呢?
2. 在我们华为HD上HBase已经优化成先写Memstore,就是为了防止内存不可见的问题,当WAL写失败也会回滚。
3.想知道这两者的冲突是如何解决的?
感谢你的回复。
记得在0.98版本以前是先写Memstore再写WAL,只有都完成之后才对客户可见。在0.98以后版本中就改成了先写WAL再写Memstore,这样更加容易理解
老哥 “HBase可能会跳跃seek”
——————
这个是啥意思,我貌似遇到过
blockCache中的数据谁去读?memstorescanner 还是storefilescanner去读?
storefilescanner去读的
你好,我有一个问题,假如这个时候数据刚好写在wal中,还没有刷到memastore中;这个时候去读数据,那么wal中的数据岂不是就读取不到了?
写的事务没结束,是读不到
在Data Block内部通过二分查找的方式定位具体的RowKey????这里不应该是通过遍历查找的吗
嗯,是的,遍历查找
请问:Qualifier如果相同再看Timestamp,Timestamp越大表示时间越新,对应的Key越小。是因为KeyValue.class类中这块代码吗?
static int compareTimestamps(final long ltimestamp, final long rtimestamp) {
// The below older timestamps sorting ahead of newer timestamps looks
// wrong but it is intentional. This way, newer timestamps are first
// found when we iterate over a memstore and newer versions are the
// first we trip over when reading from a store file.
if (ltimestamp rtimestamp) {
return -1;
}
return 0;
}