ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

HBase与Iceberg全面对比:列式存储演进与数据湖选型实战

HBase与Iceberg全面对比:列式存储演进与数据湖选型实战 最近和几个团队聊大数据生态里的存储选型几乎绕不开HBase和Iceberg。这两个词放在一起本身就是列式存储技术演进过程中很有意思的一个切面前者是很多同学入行时第一个接触的分布式NoSQL数据库后者是数据湖时代被反复提起的开放表格式。很多人问我HBase是不是要被替代了我的回答通常是先别急着下结论搞清楚它们各自解决什么问题你自然知道选谁。这篇文章不打算堆概念而是从一个实际操作过的从业者视角把HBase和Iceberg拆开来看它们各自解决了什么问题为什么会有技术和架构上的演进以及在真实项目里怎么配合使用。如果你正在准备HBase面试题、纠结WAL预写日志异常、被Shell自动拆分和预分区折腾过、或者想搞清楚Sqoop怎么操作HBase再或者想理解Iceberg到底比Hive好在哪这篇都能给你一个比较完整的参考。1. 先理清HBase一个为在线读写而生的列族存储系统1.1 HBase学的是什么宽表、RowKey与列族很多人刚学HBase时会被它的数据模型绕晕。它确实不是标准关系表但也不是简单的KV数据库。HBase的表由四部分构成RowKey、列族Column Family、列限定符Column Qualifier和时间戳。行键是每一行的唯一标识整个表的数据按RowKey的字典序排列列族是物理存储的基本单位一个列族里可以有很多列但一张表通常只有几个列族每个列族下挂的列却可以非常灵活。我举个实际例子一张用户信息表可以设计两个列族一个是info存姓名、年龄、手机号一个是behavior存最近浏览、购买、点击等行为数据。查询一个用户时通过RowKey直接拿到对应的行再按需取不同列族里的内容。这种“宽表稀疏列”的模型很擅长处理半结构化数据因为不同行可以有不同的列不用提前把所有字段写死。但要注意HBase虽然常被归到“列式”技术里它并不是传统意义上的列式存储。更准确的叫法是列族存储一个列族物理上会分到不同的HFile里但列族内部仍然以KeyValue形式按行组织。这个细节很重要因为它决定了HBase在随机读写上的优势也决定了它在全表扫描和聚合分析上的劣势。1.2 LSM与WAL写入为什么快宕机为什么不丢HBase能支撑高并发写入核心是LSM-Tree思想而保证数据不丢的核心是WAL也就是预写日志。这两块放在一起讲才能真正理解HBase的写入链路客户端先写RegionServer上的WAL再写内存里的MemStoreMemStore攒到一定大小后才会刷成HFile落到HDFS。所以HBase的随机写入实际上是很轻量的顺序写不直接修改磁盘上的老文件这也是它写入快的根本原因。WAL默认保存在HDFS上路径通常是你配置的hbase.rootdir下比如/hbase/WALs/{RegionServer主机名},{端口},{进程起始时间戳}/{Region名}/...。老版本里可能还会看到/hbase/.logs这样的目录新版本统一使用WALs复数目录。很多人会拿“hbase wals路径”去搜其实就是想知道这个目录在哪、怎么配置。一般情况下不需要手动配置单独的路径跟着hbase.rootdir走就行但如果你把hbase.rootdir指到本地文件系统那WAL也会落在本地生产环境这是非常危险的因为机器一挂WAL和数据副本都可能一起没。WAL预写日志异常我后面会单独讲排查过程。这里先记住一个原则WAL不是可以随便删的缓存它是RegionServer宕机后恢复内存中未刷盘数据的唯一依据。遇到WAL相关报错第一反应绝不应该是删除WAL文件。1.3 Region自动拆分与预分区HBase Shell实操HBase表在物理上会按RowKey范围切分成很多Region每个Region由一个RegionServer负责。刚开始建表时只有1个Region所以一旦写入压力很大所有请求都会打在一个RegionServer上这就是热点问题。解决手段就是预分区也就是在建表时直接按RowKey范围切好多个Region。用HBase Shell创建一张预分区表非常直接# 建表并指定3个自动split点 create t_sensor, cf, SPLITS [001, 100, 200] # 或者指定region数量和一个切分算法 create t_sensor, cf, {NUMREGIONS 16, SPLITALGO HexStringSplit}如果RowKey设计得比较规则比如用设备ID加时间戳拼接那预分区一定要对齐RowKey前缀。比如设备ID范围是A001到A999那split点就按字母区间来设否则数据还是会倾斜。HBase也有自动拆分常见策略有ConstantSizeRegionSplitPolicy、IncreasingToUpperBoundRegionSplitPolicy和SteppingSplitPolicy不同版本默认策略不一样。自动拆分的优点是省心缺点是拆分过程会带来Region上的额外IO而且Region数量不可控。在流量可预估的线上表里我更倾向于预分区加日常监控而不是完全依赖自动拆分。Shell里还经常用到describe、list、scan、get这些命令。写预分区时如果搞错split点顺序HBase会直接报错所以脚本里最好把键值排序后再写进去。1.4 本机安装与端口清单从Linux到Windows的实战记录HBase安装不复杂但涉及组件多容易踩坑。Linux下一般下载解压二进制包改conf/hbase-site.xml、conf/hbase-env.sh设置JAVA_HOME然后执行start-hbase.sh。如果只是单机学习不需要单独部署Hadoop和ZooKeeperHBase自带ZooKeeper和一个local模式但生产环境一定会把hbase.rootdir指向HDFS并用独立ZooKeeper集群。清理端口对排障非常重要我把常用端口列成一张表组件默认端口用途ZooKeeper2181HBase元数据协调、RegionServer注册HMaster RPC16000客户端与Master通信的RPC端口HMaster Web UI16010Master管理页面RegionServer RPC16020数据读写的主要RPC端口RegionServer Web UI16030单个RegionServer监控页面Hadoop NameNode如果用了9870/8020HDFS Web UI和RPC老版本HBase的端口是60000、60010、60020、60030看到教程里是这些基本可以判断那是旧版。新版一律以16010/16020/16030为主。Windows上安装HBase是另一个故事。Native IO在Windows下需要winutils.exe并且要设置HADOOP_HOME和PATH。很多报错信息直接提示找不到winutils解决办法是下载对应Hadoop版本的winutils.exe放到一个目录然后配置环境变量。更省事的方案是用WSL跑Linux版HBase但如果你只是想快速看下Shell效果Windows上也可以凑合。注意Windows本地文件系统上HBase的权限和文件锁行为跟Linux不太一样很多诡异的“无法启动RegionServer”问题换到Linux环境就消失了。2. Iceberg到底是什么重新定义数据湖里的“表”2.1 从“宽表”到“分析表”HBase解决不了的问题HBase很强但它解决的是在线读写问题。比如用户画像、订单状态、设备状态这类需要按主键高速读写的场景HBase是很好的选择。可如果让你拿HBase跑一个统计报表比如按天聚合几亿行数据你会非常痛苦。因为HBase的Scan是建立在KeyValue模型上的虽然有列族隔离但本质上还是在扫KeyValue要支持复杂SQL、多表Join、大范围聚合运维和开发成本都很高。而数据分析场景需要的是真正的列式存储数据按列组织查询只读取用到的列辅以高压缩率、谓词下推、向量化执行让全表扫描变得很快。Parquet、ORC这类列式文件格式解决了单文件层面的问题但它还缺一个东西谁来管理这些文件让它们看起来像一张表并且支持事务、Schema演化、多引擎共享这正是Iceberg出现的背景。把Iceberg叫“表格式”而不是“存储引擎”是因为它本身不存数据也不执行计算。它定义了一套元数据规范一个Iceberg表在存储上是一堆数据文件加一堆元数据文件任何支持Iceberg的引擎比如Spark、Flink、Presto、Trino都能用同一套事务规则读写同一张表。这才是它在“大数据生态”里被反复提及的原因。2.2 Iceberg的核心抽象快照、Manifest与数据文件理解Iceberg的关键是元数据的三层结构最上层是metadata.json一个表的最新元数据中间是Manifest List记录某个快照里包含哪些Manifest文件再往下是Manifest文件里面记录了具体的数据文件路径、分区信息、列统计、行数等。每次写操作提交时Iceberg会生成一个新的快照这个快照就是某一时刻表所有数据文件的一致视图查询可以基于任意快照执行这就是时间旅行能力的基础。Manifest文件里存了数据文件级别的统计信息比如某个Parquet文件的min/max值、分区值、行数。查询引擎在做计划时可以先读Manifest进行文件裁剪把根本不包含目标数据的文件直接过滤掉再配合列式文件的列裁剪和谓词下推就能做到只扫描必要的那一小部分数据。这个机制比Hive的目录层面分区要细得多Hive只告诉你“在哪一个分区目录里”Iceberg直接告诉你“这个文件里有没有你要的数据”。下面是一段Spark SQL创建Iceberg表的简单示例-- 在Spark中创建一个Iceberg表 CREATE TABLE db.user_events ( user_id BIGINT, event_name STRING, event_time TIMESTAMP ) USING iceberg PARTITIONED BY (days(event_time)); -- 查询时读取某个历史快照 SELECT count(*) FROM db.user_events VERSION AS OF 1234567890123;2.3 列式存储与Iceberg的“化学反应”Iceberg本身不强制你用Parquet还是ORC但实际部署中绝大多数表都选择Parquet因为它在Spark生态里配合度最好。列式存储给Iceberg提供的优势是压缩率高、列裁剪天然、编码方式适合分析负载。而Iceberg给列式存储提供的优势是把散落的列式文件提升为有事务、有快照、有Schema的“表”让多个引擎可以安全地写同一张表。从HBase到Iceberg我理解的技术演进脉络是这样的HBase用列族概念实现了“同一张表里不同业务数据分别存储”的物理隔离这给了很多人第一次接触“列式思想”的机会但它的底层还是LSM和KeyValue。到了数据湖阶段Parquet把“真正按列存储”做到了极致Iceberg再把文件管理、事务、快照这些数据库能力补上最终让数据湖里的分析表既具备传统数仓的一致性又保留了对象存储的弹性。所以“列式存储技术在大数据生态中的演进”不是HBase变成Iceberg而是从“列族存储思想”演进到“列式文件开放表格式”的完整方案。HBase的列族思想给后来者打了个样Iceberg则把它推到了分析型工作负载真正需要的高度。3. 把两者放在同一张图上HBase与Iceberg的全面对比维度HBaseIceberg数据模型RowKey 列族 列限定符标准关系表模型底层文件HFileLSM TreeKeyValueParquet/ORC等列式文件存储位置HDFSHDFS、S3、OSS等对象存储写入方式WAL MemStore随机写追加写 快照提交随机点查非常强Get是核心能力弱需要文件裁剪甚至外部索引分析扫描弱不适合复杂聚合强列裁剪、谓词下推很好事务能力行级原子性快照隔离、支持ACID语义Schema演化列族内加列方便改行键麻烦支持加列、改列、嵌套结构演化多引擎共享基本局限在HBase生态Spark/Flink/Presto/Trino统一读写典型场景在线KV、实时特征、订单状态湖仓一体、数据分析、批流一体3.1 随机读与顺序扫描的取舍HBase最擅长的是“按RowKey直接Get”这个操作走MemStore加BlockCache速度快、延迟稳定。如果你要查一个用户的实时特征HBase几乎是标准答案。但如果你要扫全表做报表HBase会很吃力因为它的Scan还是以行和KeyValue为粒度数据在磁盘上的分布很难为“只取几列”做优化。Iceberg则完全站在分析一侧常规操作就是全表扫描或分区扫描配合列式文件即使查几TB的表也能通过列裁剪把IO降下来。选型不是比谁强而是看工作负载。如果业务是“告诉我某一个用户最近的状态”选HBase如果业务是“告诉我全平台所有用户过去30天的行为分布”选Iceberg做分析底座更合适。3.2 一致性与事务模型行键原子性 vs 快照隔离HBase的单行操作是原子的多行操作需要自己用checkAndMutate或者走multiPut来做辅助控制但它本质上不是一个面向事务的数据库。Iceberg不一样它把数据库里常见的快照隔离搬到了数据湖上每次提交是一个原子操作读请求只看到已提交的快照写请求之间不会互相覆盖。这解决了一个很实际的问题以前多个任务同时写Hive分区表时可能读到半截数据而Iceberg表上不会。这种差异也决定了使用方式。HBase适合高并发小写入单点查询要求低延迟Iceberg适合批量提交或流式微批写入查询要求高吞吐、结果一致。如果你看到需求里写着“要求多条数据同时更新且不能出现中间状态”那HBase原生能力是不够的建议上层加事务协调或者把数据落到Iceberg再做后续处理。3.3 在数据平台中的典型协作方式不是替代是互补我参与过的数据平台改造里HBase和Iceberg往往是共存的而不是二选一。比如在线推荐服务需要毫秒级获取用户最新行为这个数据从Kafka实时写到HBase离线算法团队要做大规模特征分析同一个行为数据再通过Flink或Spark写到Iceberg的Parquet表里。两套链路并跑各有各的用处。这种“实时查询走HBase分析计算走Iceberg”的模式现在很常见。它的好处是各取所长HBase的随机读能力强Iceberg的分析能力强。代价是两份数据、两套链路需要考虑一致性问题。实践中一般用消息队列的位点或者批次时间做对齐再在Iceberg侧做数据质量校验。4. HBase高频问题排查从WAL异常到Shell坑点4.1 WAL预写日志异常排查实录WAL异常大概是HBase运维里最让人头疼的问题尤其是RegionServer异常重启后日志里出现类似WAL file ... is corrupt之类的信息。我先说一个核心观点WAL文件如果损坏意味着内存里还没刷到HFile的数据可能找不回来了这时候最危险的操作是“看到文件坏了就想删”。我曾经遇到过一个测试集群某台机器磁盘坏了RegionServer崩溃HDFS上一个WAL文件块读不出来。当时的处理步骤是先看RegionServer日志确认是哪个WAL文件再到HDFS上用hdfs fsck检查文件块状态确认副本情况然后尝试用HBase自带的hbase hbck工具检查Region元数据和分配情况。如果WAL确实无法恢复而该Region的数据可以从其他副本或备份中重建再考虑跳过这个WAL文件。核心原则是任何清理动作发生前必须先确认数据有可靠来源。另外生产环境不要轻易关WAL。有人为了提升写入性能把hbase.wal.disabled设成true这等于拿数据可靠性换性能一旦RegionServer宕机MemStore里所有未刷盘数据全部丢失。对于大部分在线业务来说这个风险不值得冒。更合理的优化是调整WAL的刷盘策略和MemStore刷写阈值让写入性能和可靠性匹配业务要求。4.2 自动拆分与预分区操作中的几个反直觉问题预分区看起来很聪明但实际操作里容易栽跟头。第一个坑不是Region越多越好。Region数量过多会让RegionServer维护大量元数据会造成频繁的Region切换反而降低性能。第二个坑预分区只能解决“写是均匀的”这个前提。如果RowKey设计有热点比如用自增ID当RowKey即使预分区了新数据还是集中在最后一个Region上热点依然存在。反过来自动拆分也不省心。自动拆分让Region数量动态增长看似智能化但拆分过程本身会有比较多的内部操作如果表的写入压力已经很高一次拆分可能加剧抖动。我在生产里更习惯按流量模型预先算好Region数量预估单Region承受的写入TPS和存储量再结合RegionServer数量得出一个相对合理的预分区数。Shell里指定split点时先排序再检查有没有重复因为重复split点会导致建表失败或分区异常。4.3 Sqoop从MySQL导入HBase的细节与参数Sqoop虽然不算新工具但很多传统数仓环境里还在用。把MySQL表导入HBase的典型命令sqoop import \ --connect jdbc:mysql://localhost:3306/testdb \ --username root \ --password secret \ --table user_info \ --columns id,name,age,email \ --hbase-table user_hbase \ --column-family cf \ --hbase-create-table \ --hbase-row-key id \ --split-by id \ -m 4这里有一个关键点--hbase-row-key只能指定一个字段作为RowKey。如果业务需要的组合主键Sqoop原生不友好你得在MySQL里先做一个拼接字段或者用自定义查询把主键拼好再导入。另一个常见问题是源表主键有NULLNULL会被当成空RowKey导入后所有数据挤在一行上产生极其严重的热点。所以导入前先对源数据做清洗保证RowKey不为空并且分布均匀。如果数据量很大普通Put方式导入会给RegionServer带来很大压力。这时可以加--hbase-bulkload参数让Sqoop先生成HFile再批量加载到HBase导入速度会明显提升对在线服务的影响也更小。我试过用bulkload导入千万级数据比逐条Put省了一半以上的时间。4.4 面试常问的几个HBase原理题把几个高频面试题放一起因为它们背后全是同一个底层机制。比如“为什么HBase适合随机写”因为写操作先写顺序日志WAL和内存MemStore不直接随机改磁盘所以快。“为什么读取很快”因为有BlockCache、布隆过滤器、HFile的索引块能快速定位KeyValue。“MemStore刷写条件有哪些”内存大小超过阈值、WAL文件数量过多、RegionServer级别MemStore比例超限、手动刷写等。“Region分裂是什么”当一个Region的数据量超过阈值会按RowKey范围一切为二交给不同的RegionServer管理。我建议准备面试时不要只背结论把LSM Tree和HFile的物理结构画一遍。真正理解了从写路径到读路径的底层原理HBase的绝大多数面试题都能应对。WAL预写日志异常这类场景题也一样只要明白WAL是“内存数据的安全网”就不会在排查时走弯路了。5. 从HBase到Iceberg工程演进迁移与共存5.1 什么情况值得从HBase迁移到Iceberg不是所有HBase表都该迁。如果一张表的核心访问模式是点查比如“根据用户ID查状态”并且并发很高、延迟要求很严那它继续留在HBase是最省事的方案。但如果这张表的消费方是数仓每天要全量扫描做聚合分析并且你还希望多套计算引擎能共用同一份数据那迁到Iceberg的收益就很明显。还有一个信号是“数据规模变大后HBase的Scan慢到不可接受”。因为HBase的强项不是分析大量Scan任务会把RegionServer的IO和CPU打满反而影响在线读写。把分析流量从HBase剥离到Iceberg是很多平台做湖仓改造的直接原因。5.2 迁移的常用路径双写、回填与校验真正动迁移时不建议直接停机切数据。我推荐先做双写在线链路继续写HBase同时通过Flink或Kafka消费者把同样的事件写到Iceberg表。双写期间把离线分析任务逐步切到Iceberg等验证稳定后再停掉HBase的写入或保留一段时间做回放。存量数据回填也有讲究。从HBase导出历史数据到Iceberg最简单的方案是用Spark读取HBase的表写成Iceberg表。但要注意RowKey和列族的映射HBase的行键要变成Iceberg表的主键列列族里的各个qualifier要展开成单独的字段。这个展开过程最容易出错因为HBase的列是稀疏的不同行可能字段都不一样导出前先梳理字段全集缺省值填NULL或默认值。回填完成后做数据校验不能只对比行数。HBase按RowKey查出来的是最新版本的数据Iceberg快照里可能还有历史版本两者口径要先对齐。我们当时的做法是两套数据都按主键分组对比每个字段的分布和抽样值最后再跑一遍核心报表确定结果一致后才算迁移完成。5.3 实际落地经验一个用户标签平台的改造分享一个比较真实的案例。之前维护过一个用户标签平台标签数据一开始全放在HBase里在线接口读取没问题但算法团队每天要扫全量用户标签做训练数据每天凌晨一个全表Scan把RegionServer拖得很难受而且Scan结果还要导成Parquet再给Spark用等于绕了一大圈。改造方案是加了一张Iceberg标签表分区字段是日期底层用Parquet标签列按业务域组织。Flink任务每小时从Kafka同步增量标签到Iceberg同时保留HBase作为在线特征存储。算法团队改读Iceberg后全量扫描时间从原来的小时级降到分钟级HBase侧的Scan压力也消失了。但这张Iceberg表的文件管理也带来新问题开始没做小文件合并快照越积越多后面加了定期执行快照过期和文件Rewrite才稳定下来。6. 数据湖时代的技术选型常见问题速查与实用建议6.1 HBase与Iceberg选型速查表判断条件推荐选择原因需要高并发随机点查延迟要求毫秒级HBase按RowKey的Get路径非常成熟需要大规模分析、报表、多表JoinIceberg列式文件和文件级裁剪带来更好扫描性能需要多引擎共用同一种表还要保证ACIDIceberg开放表格式Spark/Flink/Presto统一读写已经有稳定HBase集群瓶颈只是分析保留HBase另建Iceberg分析层避免高风险笨重迁移各取所长写入极其频繁数据量巨大还要流批一体Iceberg Flink微批提交、快照隔离更适合流式入湖数据只做状态存储按主键覆盖更新HBase天然支持按行覆盖简单直接这张表不是规则只是参考。实际选型还要看团队对组件的熟悉程度、现有集群的硬件条件、以及运维成本。技术架构最忌讳的是一刀切看到新东西就想替换旧的看到旧的稳定就不愿意改进。6.2 运维与开发中的实用建议HBase侧重点监控RegionServer的MemStore大小、BlockCache命中率、WAL文件数量和Region的平均负载。WAL文件如果长时间不清理通常是Replication或者日志回放出了问题要尽早排查。Compaction也要关注日常写入越多Compaction压力越大建议关注大合并和小合并的耗时必要时通过参数限制并发否则磁盘IO会被合并任务占满。Iceberg侧最重要的是文件数和快照管理。流式写入如果不合并会产生大量小文件查询性能会越来越差。定时执行rewrite_data_files合并小文件、expire_snapshots清理历史快照是Iceberg表的基本日常维护。注意快照保留时间不能设置得太短否则时间旅行能力会受影响也不能太长不然元数据膨胀很快。还有一点很实际HBase的WALs路径和Iceberg的元数据路径都要纳入备份范围别只备份业务数据文件。元数据没了表就识别不了WAL没了内存未落盘数据就丢了。6.3 一个小技巧把Iceberg当作HBase分析层的“镜子”最后分享一个我在项目里觉得很好用的模式不需要推翻HBase只把它当成一个在线缓存而把Iceberg作为它背后的分析镜像。具体做法是业务写HBase时把同样的数据发送到Kafka再由Flink以微批方式写入Iceberg。这样在线接口继续走HBase分析和算法任务走Iceberg。这个模式的好处有两个一是HBase的物理设计不需要调整不用为了分析查询去折腾HBase的Scan二是Iceberg上的表结构可以按分析需求单独设计比如更好的分区、排序和压缩策略数据质量反而更容易控制。当然它也有成本就是两条链路的数据一致性问题。但只要业务上能接受分钟级延迟这个“实时HBase 分析Iceberg”的组合比硬把两者揉成一个系统要实用得多。我自己折腾完这两套系统以后最大的感受是技术选型要回到场景本身。HBase教给我的是LSM、WAL、RowKey这些底层基本功这些知识放在今天的大数据生态里依然是硬通货Iceberg则让我重新思考了数据湖里“表”到底应该怎么组织。二者不是同一个层次的组件也不是简单的替代关系更多是在不同负载下各自发光。如果你正在做存储选型不妨先把访问模式画清楚哪些请求是点查哪些请求是扫描再决定让谁上场。
返回列表