HBase核心原理详解:从Region到HFile的完整读写链路 1. HBase到底是怎么“干活”的——先看整体架构再谈原理很多人学HBase容易走两个极端要么只背命令、做实验把put、get、scan用熟了就觉得“会了”要么一上来就啃源码被Region、WAL、HFile、MemStore这些名词劝退。实际上HBase的实现原理并不神秘它做的事情说白了就是一句话把一张巨大的、可以实时读写的表拆成很多小块分散到一堆机器上存起来并且保证随时能查、能改、能删。但这句话背后藏着好几个必须想清楚的问题表怎么拆拆完放哪客户端怎么知道去哪个机器找数据写入的时候万一机器宕机了怎么办读取的时候怎么尽量快这一篇我就用“从一条数据进去到一条数据出来”的完整链路把HBase的核心实现原理讲透。学完这一篇你再去看HBase面试题会发现大部分题其实都是在问同一件事——HBase怎么用分布式的手段解决单机数据库解决不了的问题。这篇内容适合已经会装HBase、会跑基本命令的人也适合正在准备大数据面试的开发者和运维。已经熟练使用HBase但没系统梳理过原理的人这篇文章也可以帮你把零散的知识点串成一条线。1.1 一张表是怎么被拆开的Region与RowKeyHBase里的表不是物理上存在一个文件里的而是按照行键RowKey的字典序范围把表切成若干个连续的片段每个片段叫做一个Region。你可以把Region理解成“表的一个分片”每个Region负责一段RowKey区间比如Region A负责RowKey从“a”到“m”的数据Region B负责从“n”到“z”的数据。这个设计和MySQL的分库分表思路有点像但HBase把“分片”做成了内建能力不需要你手动指定数据去哪个节点。表刚创建时只有一个Region随着数据不断写入Region会越来越大。当某个Region的大小超过阈值默认是10GB由参数hbase.hregion.max.filesize控制HBase会把这个Region从中间某个RowKey位置**分裂Split**成两个子Region各自负责一半的范围。Region分裂是HBase实现原理中非常关键的一个机制它保证了表的规模可以无限扩展下去——数据多了就多分裂几次分裂出来的Region由Master调度分散到不同的RegionServer上。这也是HBase被称为“在线扩容”数据库的根本原因扩容不需要停服不需要手动迁移数据Region自己会“长”到新机器上去。不过这里有个新手很容易忽略的细节Region分裂不是立刻把数据文件也切成两半。HBase采用了一种很聪明的策略——分裂时先创建两个新的Region引用它们指向的还是原来那同一个数据文件只是逻辑上各自认领了文件中的前半段和后半段。真正把文件物理拆开要等到后续的合并Major Compaction才会做。这样做的好处是分裂操作非常轻量几乎瞬间完成不会因为分裂导致长时间的IO阻塞。1.2 谁来管这些RegionMaster、RegionServer与ZooKeeperRegion只是逻辑上的分片真正“扛”Region、提供读写服务的是RegionServer。一个RegionServer进程可以管理多个RegionRegionServer自己本身是一个独立的Java进程通常和HDFS的DataNode部署在同一台物理机上目的就是让计算靠近数据减少网络传输开销。集群里还有一个Master进程它负责管理所有的RegionServer做的事情包括分配Region给具体的RegionServer、处理RegionServer宕机后的Region转移、监听Region分裂并调整元数据、做负载均衡把繁忙节点上的Region挪到空闲节点。Master并不是读写路径上的瓶颈因为客户端读写数据完全不经过Master它只负责“管理”不负责“干活”。那客户端怎么知道数据在哪个RegionServer上呢这里就是ZooKeeper登场的时机了。ZooKeeper在HBase集群中扮演“协调者”的角色主要存三类关键信息当前集群里有哪些RegionServer在线、Master是谁、以及HBase的元数据表hbase:meta所在的RegionServer地址。你需要注意一点HBase的架构设计遵循了“元数据与数据分离”的原则。ZooKeeper只保存“meta表在哪台机器上”这一层信息meta表本身则保存在某个RegionServer上。meta表里记录的是“每个用户Region的RowKey范围落在哪个RegionServer上”这第二层信息。所以客户端要找到一条数据实际上要经过两次路由先问ZooKeeper找到meta表再问meta表找到目标RegionServer最后才向那个RegionServer发起真正的读写请求。这个两层路由的机制是理解HBase原理的一个分水岭。很多人在排查问题时发现“有时能查到有时查不到”往往就是因为meta表本身发生了分裂或者迁移而客户端的缓存又没来得及更新。后面我会在“常见问题”部分专门讲这个场景。2. 数据落盘之前经历了什么MemStore、WAL与HFile讲完架构我们来看一条数据从客户端发起写入到最终持久化到磁盘中间到底经过哪些环节。这个链路是HBase实现原理中最核心的部分也是面试中几乎必考的内容。2.1 写入路径三步走先写日志再写内存最后落盘HBase的写入设计参考了一个特别经典的思想先写日志Write-Ahead LogWAL再更新内存缓存之后在合适的时机合并刷写到磁盘。这个思路和MySQL的Redo Log、Redis的AOF本质上是一回事——牺牲一点点写入延迟换取数据的安全性。具体来说当客户端发起一个put请求时经过路由定位到目标RegionServer后RegionServer上的处理流程是这样的第一步追加写入WAL。RegionServer会先把这次写入操作以HLog的形式顺序追加到HDFS上。HLog是HBase的预写日志每个RegionServer维护一个或多个HLog文件。这一步的关键词是“顺序追加”因为顺序写磁盘比随机写要快一到两个数量级这是HBase能扛住高并发写入的底气之一。只有WAL写入成功这次put才算真正“成功”并返回客户端响应。第二步写入MemStore。WAL写完后RegionServer把数据写入Region内对应的MemStore——MemStore是一块内存缓冲区每个列族对应一个MemStore。数据在内存里会按照RowKey排序存储这样后续查询走内存扫描时会非常快同时刷写磁盘时也能按顺序输出。写入MemStore成功之后客户端立刻就能读到这条数据不需要等它落盘。第三步异步刷写Flush成HFile。MemStore不会无限涨下去当它的大小超过阈值默认hbase.hregion.memstore.flush.size为128MB或者RegionServer上所有MemStore的总大小超过限制默认是堆内存的40%时RegionServer会触发刷写操作把当前MemStore里的数据按顺序写出为一个HFile文件存储在HDFS上。刷写完成后MemStore清空之前对应的WAL日志就可以安全删除了。如果你用一句话总结HBase的写入原理那就是**先用顺序写日志保证不丢数据再用内存保证写得快最后再批量落盘保证最终持久化。**这也解释了为什么HBase非常适合写密集型的场景——它把“随机写”转化成了“顺序写内存写”。2.2 HFile的存储结构为什么它能扛住海量数据HFile是HBase在HDFS上的最终存储格式它的设计非常讲究不是简单地把数据堆在一起而是分成了很多块Block每块都有独特的用途。理解HFile的结构很多HBase调优的问题就迎刃而解了。一个HFile文件内部主要由这几类Block组成Trailer Block文件的尾巴记录了文件中其他Block的偏移量信息是读取文件时的入口。Data Block真正存放用户数据的地方默认大小64KB。每个Data Block内包含多条KeyValue数据KeyValue在Block内按RowKey有序排列。Index Block索引块记录了每个Data Block的起始RowKey和它在文件中的偏移量用于快速定位某个RowKey所在的Data Block。Bloom Block布隆过滤器块用于快速判断“某个RowKey是否在文件中可能不存在”从而跳过大量不必要的扫描。Meta Block元信息块存储一些文件级别的元数据。这个结构设计得最精巧的地方在于索引块和布隆过滤器让HBase能在大数据量的文件里做“定点查找”。你可以把HFile想象成一本几千页的书索引块就是目录告诉你某个字在哪一页布隆过滤器是一个“快速排除”工具告诉你某个字大概率不在前300页。所以HBase读取数据时并不是从头到尾把HFile扫一遍而是通过多层索引和布隆过滤器先迅速定位到可能包含目标数据的少数几个Data Block再去这些Block里精确查找。这就是HBase敢于说“我可以处理百亿行数据而查询延迟还在毫秒到十毫秒级别”的技术底气。2.3 为什么引入HDFS却不直接在HDFS上建文件索引有一个问题很多初学者都会困惑HDFS自己也有文件、目录和块的概念为什么HBase不直接让HDFS“知道”自己的表结构而是要自己弄一套Region、HFile和索引机制原因在于两者的定位完全不同。HDFS是一个通用的分布式文件系统它是“无差别对待”所有文件的它不关心文件里面存的是什么东西也没有能力对一行一行结构化数据做高效检索。HDFS擅长的是一次写入、多次读取的大文件流式访问而不是毫秒级的随机单行读写。HBase在这条链路里扮演的角色就是把“分布式文件系统”包装成一个“分布式数据库”。它利用HDFS提供的高可靠、高可扩展的底层存储能力三副本、自动容错同时在HDFS之上自己实现了一套面向结构化数据的内存缓存、索引、排序、检索和事务语义。打个比方HDFS是物流仓库货架整齐、安保到位但它不管每个箱子里装的具体是什么商品HBase是仓库里的一套智能分拣系统知道每个箱子在哪个货架、里面是什么货、怎么最快找到它。两者结合的结果是底层的可靠性、扩展性问题由HDFS解决上层的结构化存储、检索、一致性问题由HBase自己解决。这也解释了为什么HBase的部署必须要依赖HDFS和ZooKeeper——它天生是站在HDFS肩膀上的分布式存储系统。3. 读一条数据有多“折腾”从Client到RegionServer的完整路由前面讲了写入路径和存储结构那读取呢读取路径同样重要而且里面藏着很多性能优化的技巧。3.1 客户端三级路由Client Cache的关键作用客户端要读取一条RowKey为user001的数据第一步并不是直接找RegionServer而是先查自己本地缓存的Region位置信息。HBase客户端无论是Shell还是Java API内部维护了一张缓存表记录了“哪个RowKey范围在哪个RegionServer上”这是它从上一次请求中学习到的结果。如果本地缓存没有命中客户端就会发起一次全量路由查询先向ZooKeeper获取hbase:meta表所在的RegionServer地址再向那个RegionServer请求meta表中包含user001的Region信息拿到目标RegionServer地址后客户端会把这个对应关系缓存到本地下次再请求同样的RowKey范围就直接跳过前两步。这个客户端缓存机制是HBase读写性能的重要保障因为路由信息属于“低频变化、高频访问”的数据把它们缓存在客户端能减少大量网络往返。但缓存也带来了一个问题如果Region发生了分裂、迁移或者某个RegionServer宕机了客户端手里的旧缓存会指向一个已经不存在的地址此时客户端会抛NotServingRegionException——这个是HBase开发里最经典的报错之一。遇到这个异常时客户端会自动进行“重试刷新缓存”操作它收到异常后会清除本地缓存重新走一遍完整路由流程拿到新的Region地址后再重试这次请求。所以你在写HBase的Java代码时会发现用connection.getTable()获取的Table实例天然具备这种自动恢复能力但如果你自己实现路由逻辑而没有做缓存刷新就很可能会出现“程序第一次能读到Region一迁移就读不到”的诡异问题。3.2 服务器端读取BlockCache先行HFile兜底请求到达RegionServer后读取也不是直接去磁盘上翻HFile而是按照“内存优先、磁盘兜底”的顺序去找先查BlockCache。BlockCache是RegionServer级别的读缓存专门缓存最近被读取过的HFile Block和MemStore一样驻留在JVM堆内。如果数据在BlockCache里命中了直接返回性能极高。再查MemStore。如果BlockCache没命中就去MemStore里找。MemStore里存着最新写入、尚未刷盘的数据所以在MemStore里能找到的最多是比HFile更新的数据。最后查HFile。如果MemStore里也没有才去HDFS上读取HFile。读取时会先经过文件索引和布隆过滤器定位到目标Data Block然后从磁盘加载该Block到内存同时把Block放入BlockCache供后续复用。这里有一个非常关键的细节HBase的读取并不是只查一个文件。因为MemStore可能已经多次刷写成了多个HFile同一个RowKey的数据可能分布在多个HFile里比如一次put在HFile1另一次put在HFile2。所以读取时RegionServer要把该Region列族目录下所有相关的HFile都扫描一遍找出同一条RowKey的所有版本再按照时间戳或者写入顺序合并挑选出合适的版本返回。这也就引出了HFile的数量和读取性能的关系HFile越多读一条数据需要扫描的文件数越多读性能越差。因此后面会有主动合并多个小HFile成一个文件的“Compaction”机制。3.3 布隆过滤器用极小代价拦截“必定不存在”的查询布隆过滤器是HBase读取链路里一个非常有实用价值的设计。假如你要查一个根本不存在于任何HFile中的RowKey如果没有布隆过滤器RegionServer会把所有HFile的索引都翻一遍再逐个读文件确认最后才告诉你查不到——这是一个非常耗时的过程。有了布隆过滤器之后RegionServer会先拿RowKey去每个HFile的布隆Block里算一笔账这个RowKey经哈希函数映射后那几个对应的位是不是都在1。只要有一个位不在1就可以100%断定这个RowKey不在当前HFile里从而直接跳过这个文件省去实际IO。布隆过滤器有一个特性你要理解它只能判断“一定不存在”不能判断“一定存在”。如果布隆过滤器说“可能存在”那就需要实际读取Data Block去确认。换句话说布隆过滤器存在的意义是减少无谓的磁盘IO而不是替代精确查找。默认情况下HBase对RowKey启用布隆过滤器ROW类型如果你在业务上经常按“RowKey列族限定符”组合查询可以改成ROWCOL类型把过滤粒度做得更细代价是布隆过滤器会占用更多内存。实操中的经验是布隆过滤器对“稀疏查询”场景帮助最大——也就是你查询的RowKey在数据集中只占极少比例时大部分HFile都能被过滤掉但如果你总是做全表扫描布隆过滤器不仅没什么用还会白白占用内存这时候就值得考虑关掉它。4. 数据在后台怎么“自己整理自己”合并与分裂机制HBase长期运行后Region的数量、HFile的数量都会越来越多如果不做后台整理整个系统的读写性能会迅速劣化。这里面有一个完整的后台自愈、自管理的机制链值得每一个使用HBase的人深入了解。4.1 为什么HFile会越来越多以及Compact的三种模式MemStore每次刷写都会生成一个新的HFile如果一个Region持续写入一段时间后列族目录下可能积累几十个甚至上百个小HFile。读取时每查一条数据都要遍历所有这些文件性能自然就崩了。HBase用**Compaction合并**来解决这个问题它分为两种模式Minor Compaction将相邻的几个小HFile合并成一个稍大的HFile。Minor Compaction不会清理被删除或过期的数据只是单纯合并文件降低文件数量。它执行频繁、代价较小。Major Compaction将一个Region中一个列族的所有HFile合并成一个大HFile同时清理掉所有被标记删除的数据、超过版本保留期限的旧数据、以及超过TTL的数据。Major Compaction是真正意义上的“数据库整理”执行代价大但效果也最彻底。这里有个调优中特别常见的矛盾Major Compaction既必要又危险。说它必要是因为长期不合并HFile太多读性能持续劣化而且磁盘上会残留大量已删除数据说它危险是因为Major Compaction期间会有大量磁盘IO和网络IO如果集群本身负载已经很高再做Major Compaction很容易把集群拖垮严重时甚至会让RegionServer OOM或者响应超时。我个人的习惯是对于业务高峰期的集群关闭自动Major Compaction改在业务低峰期比如凌晨手动触发。具体做法是在HBase Shell里对某张表执行major_compact 你的表名或者用定时任务在凌晨批量对核心表做合并。HBase还提供了一套机制让你的表在一段时间内不参与自动Major Compaction你可以通过hbase.hregion.majorcompaction参数控制周期也可以按表设置MAJOR_COMPACTION属性。4.2 Region分裂的触发临界点与你的业务模式Region分裂机制前面提过这里补充一些实操中容易踩坑的细节。Region分裂的触发条件是Region内某个列族的Store文件总大小超过阈值但阈值不是简单的10GB而是一个会随着Region数量动态变化的公式。当RegionServer上Region总数量较少时阈值是2 * hbase.hregion.max.filesize也就是20GB当Region数量增多后阈值会按照hbase.hregion.split.memstore.size和hbase.hregion.max.filesize之间的某个线性关系变化总体趋势是集群Region越多分裂阈值越宽松防止Region数量爆炸式增长。——这背后的逻辑是Region数量并不是越多越好。每个Region都有对应的MemStore、BlockCache开销Region数量动辄上万个时光维护元数据和心跳就会占用大量内存和网络带宽。所以HBase通过动态调整分裂阈值把Region数量控制在合理范围内。另一个值得注意的场景是预分区Pre-splitting。很多新手在创建表时不指定分区然后疯狂写入几千万条数据结果所有写入请求在一开始都集中在同一个Region上——热点问题。预分区的思路是在创建表时就按RowKey的分布规律把表预先切成16个、32个甚至更多的Region并手动指定每个Region的StartKey和EndKey让初始写入就可以被分散到多个RegionServer上。这个操作在HBase里就一行命令create t1, cf1, SPLITS [a, b, c, d]但背后需要你对RowKey的分布有预估否则预分区反而会切出不均衡的Region。4.3 StoreFile数量暴涨的应急处理有一个高频出现的运维场景是某一天突然发现一个大表HFile数量从几十涨到了几千读写性能直线下降RegionServer出现频繁的Full GC。你可以用HBase的Web UI或者hbase hbck命令来检查每个Region的StoreFile数量正常情况下应该是个位数到几十个。出现StoreFile暴涨通常意味着RegionServer的刷写异常频繁或者Compaction一直做不完。常见原因有三类MemStore被写满后反复触发Flush写入速度极快但Compaction吞吐跟不上导致文件越积越多。Compaction被阻塞可能是合并队列太长或者RegionServer的IO已经饱和。RegionServer内存设置不合理MemStore的占比太高导致每次Flush的数据量小产生大量小文件。应急处理时优先考虑降低写入速度从业务侧限流→ 提高Compaction线程数hbase.regionserver.thread.compaction.small和hbase.regionserver.thread.compaction.large→ 对相关表手动执行Major Compaction → 如果还不行就得检查RegionServer所在的机器磁盘IO是不是已经成了瓶颈。5. 数据一致性与容灾HBase如何做到“不丢数据”最后这部分把HBase的容灾一致性和多版本数据管理讲清楚这也是面试中仅次于读写路径的高频考点。5.1 WAL的两种写入级别Wait与AsyncHBase允许多种WAL写入级别由hbase.durable属性控制对于put操作还可以单独设置USE_DEFAULT使用表默认设置。SYNC_WAL等待WAL日志写入HDFS并返回成功后才返回给客户端最安全但延迟最高。FSYNC_WAL比SYNC_WAL更严格不只是写入HDFS客户端缓存还要求刷到磁盘fsync安全性最高延迟也更高。ASYNC_WAL写入WAL后不等待同步结果就返回性能最好但存在数据丢失风险。默认情况下HBase使用SYNC_WAL。在单机高并发测试时你可以对比一下SYNC_WAL和ASYNC_WAL的写入吞吐差距通常ASYNC_WAL能提升一到两倍的写入性能但这个代价是RegionServer进程崩溃时可能丢失最后几秒的数据。所以生产环境怎么选取决于你数据容忍丢失的底线。金融、交易类场景必须SYNC_WAL日志类、可容忍少量丢失的数据可以考虑ASYNC_WAL来换取更高吞吐。5.2 WAL的故障恢复RegionServer宕机后发生了什么当一个RegionServer突然宕机时ZooKeeper会在会话超时默认90秒后察觉到这个RegionServer失联。随后Master会接管做下面几件事将该宕机RegionServer名下所有的Region标记为“不可用”并分配到集群中其他存活的RegionServer上。新的RegionServer加载这些Region时读取该Region的WAL日志按Region分组把日志里的写入操作重新灌入新的MemStore再触发一次刷写生成HFile。这个重放日志的过程就是“恢复”。等所有WAL日志重放完成Region才能重新对外提供读写服务。注意这个恢复过程的耗时和WAL日志大小成正比如果宕机前有大量写入还没刷盘恢复时间可能长达几分钟甚至更久。这也是分布式数据库的一个常态——你有多少数据没来得及落盘故障恢复时间就有多长。所以如果你的业务对可用性要求极高就要把MemStore刷写阈值调低一些增加刷写频率来缩短日志恢复时间代价是会产生更多小HFile、加重Compaction压力。5.3 多版本并发控制一个Cell里存了多个历史版本HBase的每个单元格Cell即RowKey列族列标识符定位的一个值并不仅仅存一个值而是可以保存多个历史版本。每个版本由时间戳标识版本数量由列族的VERSIONS属性控制默认是1你可以设置成3、5或者更多。这个设计非常有意思它意味着HBase天然支持“查一下这个数据3秒前和3小时前的值分别是什么”——这对于审计、回溯、以及一些时序数据分析场景特别有用。比如你在HBase Shell里执行get user, user001, {COLUMN info:name, VERSIONS 3}就会返回info:name这个单元格最近3个版本的值。如果你传入自定义时间戳去写入甚至可以为过去的时间点补写一个版本读取时按时间戳查就能看到“历史上那个时刻”的状态。但多版本不是越多越好。列族的VERSIONS参数调得越大存储空间消耗越大因为每个版本都是一条独立的KeyValue记录。同时Major Compaction会清理超过VERSIONS限制的旧版本。所以日常开发里我建议你根据业务实际需求设置版本数大部分业务1到3个版本足够最多也不建议超过10个否则磁盘占用会让你很心痛。5.4 删数据并不是真的“立刻没了”另一个经常让人意外的事实是HBase的Delete操作也是“写”一条特殊的KeyValue记录我把它理解为墓碑标记Tombstone。当你删除一条数据时HBase并不会立刻物理删除那部分数据而是在对应的HFile里写入一条删除标记。查询时如果读到这个标记就把对应的数据屏蔽掉。真正物理删除要等到Major Compaction把所有HFile合并成一个时墓碑标记连同它指向的数据才会一起被清除。这个设计的直接后果是你在HBase Shell里执行deleteall后磁盘空间并不会立刻释放而且磁盘上的数据在理论上还可能通过工具从HFile里翻出来。对安全要求高的场景你要知道这只是一个“逻辑删除”物理清除必须等Major Compaction完成。反过来“删了还要能恢复”的场景也曾在生产环境中发生过——只要还没做Major Compaction误删的数据其实还有救可以从HFile层面想办法。6. 实操心得与建议我踩过的坑和总结出来的经验写了这么多原理最后分享几个我这些年用HBase实际工作中摸出来的经验和踩过坑的地方希望对你有直接帮助。第一RowKey设计是HBase性能的“命门”。一切原理最终都会落到RowKey上。同一个表RowKey设计得好几十亿数据也能秒查设计得差几百万数据就能把Region打得热点频出。最常见的优化手段是加盐Salting和反转Reversing加盐是在RowKey前面拼一个随机前缀或分桶前缀让写入分散到不同Region反转是把时间戳、自增ID这类单调递增的后缀翻转到前面避免所有新数据都写到同一个Region。切记不要使用纯自增ID作为RowKey否则你所有的写入都会怼到最后一个Region上其他Region闲置整个分布式集群退化成单机。第二扫全表Scan是HBase最容易被滥用的操作。HBase的Scan适合做大范围的顺序扫描但如果你频繁对一个大表做不带条件的Scan性能一定会非常难看。生产环境里我更推荐用PageFilter配合起始RowKey实现分页每次只取一页数据而不是一把梭把几百万行全部load到内存里。如果你确实需要做分析型的大规模扫描把数据导出到Hive或者Spark里做计算会比在HBase里反复Scan合理得多。第三列族数量尽量少。HBase官方推荐一个表最多设计2到3个列族因为它设计上就是“一个列族对应一个MemStore、一组HFile”列族多了刷写和合并的联动复杂度会剧增而且不同列族的数据量不平衡会导致Region的拆分、合并非常难做。如果一个表需要存多种属性尽量合并到一个列族用不同限定符区分这样对性能友好得多。第四监控一定要盯着RegionServer的日志和GC趋势。HBase的RegionServer是一个内存大户堆内存动辄几十GB频繁Full GC会让整个RegionServer暂停服务JVM Stop The World而ZooKeeper一旦发现它心跳超时就会触发Region迁移迁移本身又是大量IO和网络消耗容易形成连锁故障。我处理过不止一次“RegionServer雪崩”起因都是GC停顿导致ZooKeeper会话过期、触发大规模Region迁移迁移过程中又把其他节点拖垮。所以配置RegionServer堆内存时不要盲目给大通常建议留出30%左右的空间给操作系统的Page Cache同时设置合理的GC参数G1收集器、停顿时间目标在100ms以内并且对Full GC的频率和耗时设置监控告警。第五HBase面试题的底层逻辑其实就几条。回想一下你看到的那些高频HBase面试题HBase和Hive有什么区别HBase的存储结构是什么HBase为什么写快读慢为什么说HBase是列式存储准确说是面向列族的存储RegionServer宕机后怎么恢复这些问题真正在问的就是我们这一篇讲的内容——架构分工、写入链路、HFile结构、读取优化、故障恢复。把这篇文章的思路吃透再去看题你会发现大部分答案都是可以自己推出来的而不是死记硬背的。我的个人体会是HBase不是那种“读一遍文档就会用”的数据库你要理解它的原理才能在设计表结构、调参、排查故障时心里有底。尤其在大数据这个领域生产环境中没有太多试错机会如果你所在的项目里已经开始引入HBase强烈建议你先把这一篇的原理链路彻底搞明白再去做实验和调优。这样后面无论遇到安装配置、表设计还是Java API开发的问题你都会有一个清晰的坐标系去定位问题在哪里。