一、引言为什么需要Compaction和Region Split在前两篇文章中我们了解了HBase的读写流程和MemStore Flush机制。Flush将MemStore中的数据刷写到HDFS生成HFile文件。但随着数据不断写入会产生以下问题HFile数量爆炸每次Flush生成一个新HFile导致大量小文件读取性能下降读取需要查找多个HFileIO开销增大存储空间浪费过期数据、删除标记占用空间Region过大单个Region数据量过大影响读写性能HBase通过**Compaction合并和Region Split分裂**两个机制来解决这些问题。本文将深入剖析这两个机制的工作原理、触发条件和优化策略。二、Compaction机制详解2.1 什么是CompactionCompaction是HBase将多个小的HFile合并成更大的HFile的过程同时清理过期数据和删除标记。它是HBase自动执行的后台任务对上层应用透明。上图展示了Compaction的两种类型Minor Compaction选择部分相邻的小HFile合并成较大的HFileMajor Compaction合并Store中的所有HFile清理过期数据2.2 为什么需要Compaction问题1HFile数量过多影响读取性能场景一个Store有10个HFile读取一个RowKey 无Compaction时 - 需要检查所有10个HFile - 即使布隆过滤器过滤掉大部分仍需打开多个文件 - 读取延迟 10 × 文件打开时间 数据读取时间 有Compaction后合并为2个HFile - 只需检查2个HFile - 读取延迟 2 × 文件打开时间 数据读取时间 - 性能提升约5倍问题2过期数据占用存储空间场景VERSIONS1同一Cell多次更新 HFile-1: row1|cf:c1|ts1000 → value1 HFile-2: row1|cf:c1|ts2000 → value2 HFile-3: row1|cf:c1|ts3000 → value3 由于VERSIONS1只有ts3000的版本有效 但ts1000和ts2000的版本仍占用存储空间 Compaction后 HFile-merged: row1|cf:c1|ts3000 → value3 旧版本被清理释放存储空间问题3删除标记需要物理清理场景删除一行数据 写入时 Put: row1|cf:c1|ts1000 → value1 Delete: row1|cf:c1|ts2000 → (Delete标记) 读取时 - 发现ts2000的Delete标记 - 返回数据不存在 - 但ts1000的数据仍存储在HFile中 Compaction后 - Delete标记和旧数据都被物理删除 - 释放存储空间2.3 Minor Compaction详解定义与特点Minor Compaction小合并是将相邻的若干个小HFile合并成一个较大的HFile的过程。核心特点特性说明选择性合并只选择部分相邻的HFile不是全部不清理过期数据保留所有版本的数据和Delete标记IO开销小涉及的数据量小对业务影响小频率高自动频繁触发保持HFile数量可控触发条件Minor Compaction的触发条件由多个参数控制!-- hbase-site.xml中的Compaction配置 --!-- 触发Compaction的最小HFile数量 --propertynamehbase.hstore.compaction.min/namevalue3/value/property!-- 触发Compaction的最大HFile数量 --propertynamehbase.hstore.compaction.max/namevalue10/value/property!-- HFile大小小于此值时被认为是小文件优先Compaction --propertynamehbase.hstore.compaction.min.size/namevalue134217728/value!-- 128MB --/property!-- HFile大小大于此值时被认为是大文件不参与Compaction --propertynamehbase.hstore.compaction.max.size/namevalue9223372036854775807/value!-- Long.MAX_VALUE默认无上限 --/property!-- Compaction的比率阈值 --propertynamehbase.hstore.compaction.ratio/namevalue1.2/value/property触发逻辑1. 检查Store中的HFile数量 - 如果HFile数量 hbase.hstore.compaction.min默认3 - 进入Compaction候选队列 2. 选择Compaction的文件 - 从最小的HFile开始选择相邻的文件 - 文件大小满足file_size sum(larger_files) × ratio - 即小文件的大小不超过比它大的文件总和的ratio倍 3. 执行Compaction - 将选中的HFile合并成一个新的HFile - 新HFile的大小约为选中文件大小之和Minor Compaction的执行过程执行前 Store ├── HFile-1 (5MB) ├── HFile-2 (8MB) ├── HFile-3 (10MB) ← 选中合并 ├── HFile-4 (15MB) ← 选中合并 ├── HFile-5 (20MB) └── HFile-6 (100MB) ← 太大不参与 Minor Compaction选中HFile-3和HFile-4 - 10MB 15MB 25MB - 25MB (20MB 100MB) × 1.2 144MB ✓ 执行后 Store ├── HFile-1 (5MB) ├── HFile-2 (8MB) ├── HFile-new (25MB) ← 合并生成 ├── HFile-5 (20MB) └── HFile-6 (100MB) 旧HFile-3和HFile-4标记为删除在下次Compaction时清理Minor Compaction的IO模型Minor Compaction采用顺序读写模式效率较高读取阶段 - 顺序读取选中的HFile - 按RowKey顺序合并 - 内存中维护一个优先队列PriorityQueue 写入阶段 - 顺序写入新的HFile - 生成Block Index和布隆过滤器 - 原子替换旧HFile 特点 - 顺序读 顺序写 磁盘IO效率极高 - 不涉及随机IO - 对业务影响较小2.4 Major Compaction详解定义与特点Major Compaction大合并是将Store中的所有HFile合并成一个大HFile的过程同时清理过期数据和Delete标记。核心特点特性说明全量合并合并Store中的所有HFile清理过期数据删除超过版本数限制的数据物理删除清理Delete标记的数据IO开销大涉及全部数据消耗大量磁盘IO频率低默认7天一次或手动触发触发条件!-- Major Compaction配置 --!-- Major Compaction的时间间隔毫秒 --propertynamehbase.hregion.majorcompaction/namevalue604800000/value!-- 7天 7 × 24 × 60 × 60 × 1000 --/property!-- Major Compaction的抖动时间毫秒 --propertynamehbase.hregion.majorcompaction.jitter/namevalue0.50/value!-- 50%的抖动避免所有Region同时Compaction --/property触发逻辑1. 时间触发 - 距离上次Major Compaction超过7天 - 加上抖动时间避免所有Region同时触发 2. 手动触发 - 通过Shell命令major_compact table_name - 通过Java APIadmin.majorCompact(TableName.valueOf(table_name)) 3. 禁用自动触发 - 设置hbase.hregion.majorcompaction 0 - 完全手动控制Major Compaction时机Major Compaction的执行过程执行前 Store ├── HFile-1 (5MB) ├── HFile-2 (8MB) ├── HFile-3 (10MB) ├── HFile-4 (15MB) ├── HFile-5 (20MB) └── HFile-6 (100MB) Major Compaction合并所有HFile 读取阶段 1. 打开所有6个HFile 2. 使用优先队列PriorityQueue按RowKey排序 3. 逐个读取KeyValue 处理阶段 4. 版本控制只保留最新VERSIONS个版本 - 如果VERSIONS3同一Cell只保留3个最新版本 - 旧版本丢弃 5. TTL检查删除超过TTL的数据 - 如果TTL86400秒1天 - 当前时间 - timestamp 86400的数据丢弃 6. Delete标记清理 - 如果Delete标记后没有新版本 - 删除该Cell的所有历史版本 - 物理删除数据 写入阶段 7. 顺序写入新的HFile 8. 生成Block Index和布隆过滤器 9. 原子替换所有旧HFile 执行后 Store └── HFile-merged (约158MB) ← 合并后的单一大文件Major Compaction的数据清理规则清理规则按优先级 1. Delete标记清理最彻底 场景Put Delete HFile-1: row1|cf:c1|ts1000 → value1 HFile-2: row1|cf:c1|ts2000 → (Delete标记) Major Compaction后 - 如果Delete后没有新版本完全删除该Cell - 结果该Cell不存在 2. 版本数限制清理 场景VERSIONS1多次Put HFile-1: row1|cf:c1|ts1000 → value1 HFile-2: row1|cf:c1|ts2000 → value2 HFile-3: row1|cf:c1|ts3000 → value3 Major Compaction后 - 只保留最新1个版本 - 结果row1|cf:c1|ts3000 → value3 3. TTL过期清理 场景TTL3600秒1小时当前时间5000 HFile-1: row1|cf:c1|ts1000 → value1 ← 5000-100040003600过期 HFile-2: row1|cf:c1|ts4500 → value2 ← 5000-45005003600保留 Major Compaction后 - 删除过期数据 - 结果row1|cf:c1|ts4500 → value22.5 Compaction的IO影响与优化Compaction对业务的影响影响类型Minor CompactionMajor Compaction磁盘IO中等部分文件高全部文件网络IO中等HDFS副本复制高CPU中等排序、合并高内存中等优先队列高对读写的影响较小较大可能阻塞持续时间秒级~分钟级分钟级~小时级Compaction优化策略策略1调整Compaction参数!-- 增加Minor Compaction的阈值减少频率 --propertynamehbase.hstore.compaction.min/namevalue5/value!-- 默认3增加到5 --/property!-- 增加Minor Compaction的最大文件数 --propertynamehbase.hstore.compaction.max/namevalue15/value!-- 默认10增加到15 --/property!-- 调整Compaction比率 --propertynamehbase.hstore.compaction.ratio/namevalue1.0/value!-- 默认1.2降低为1.0更积极合并 --/property策略2禁用自动Major Compaction手动控制!-- 禁用自动Major Compaction --propertynamehbase.hregion.majorcompaction/namevalue0/value!-- 0表示禁用 --/property然后在业务低峰期手动触发# 手动触发Major Compactionhbase(main):001:0major_compacttable_name# 或只触发某个Regionhbase(main):002:0major_compactregion_name策略3使用分层CompactionStripe CompactionHBase 1.3支持Stripe Compaction策略将HFile按RowKey范围分层传统Compaction Store ├── HFile-1 (全范围) ├── HFile-2 (全范围) └── HFile-3 (全范围) Stripe Compaction Store ├── Stripe-1 (row1~row1000) │ ├── HFile-1 │ └── HFile-2 ├── Stripe-2 (row1001~row2000) │ ├── HFile-3 │ └── HFile-4 └── Stripe-3 (row2001~row3000) └── HFile-5优势Compaction只涉及部分Stripe减少IO读取时只需查找相关Stripe的HFile特别适合范围查询多的场景配置propertynamehbase.hstore.engine.class/namevalueorg.apache.hadoop.hbase.regionserver.StripeStoreEngine/value/property策略4限制Compaction的带宽!-- 限制Compaction的IO带宽字节/秒 --propertynamehbase.hstore.compaction.throughput.lower.bound/namevalue52428800/value!-- 50MB/s --/propertypropertynamehbase.hstore.compaction.throughput.upper.bound/namevalue104857600/value!-- 100MB/s --/property三、Region Split机制详解3.1 为什么需要Region SplitHBase表的数据按RowKey范围分布在多个Region中。初始时一张表只有一个Region。随着数据不断增长单个Region会变得越来越大带来以下问题问题说明影响读写热点所有请求集中在一个RegionServer该节点负载过高Compaction耗时大Region的Compaction涉及大量数据影响业务恢复时间长RegionServer宕机时大Region恢复慢可用性降低MemStore过大大Region的MemStore占用更多内存增加GC压力Region Split将大Region分裂成两个较小的Region分散负载。上图展示了Region Split的基本概念一个大的Region分裂成两个较小的Region分别负责不同的RowKey范围。3.2 Region Split的触发条件Region Split的触发条件在HBase不同版本中有所变化0.94版本之前简单阈值触发条件 当某个Region中的某个Store下所有StoreFile的总大小 超过 hbase.hregion.max.filesize默认10GB 即StoreFile总大小 10GB → 触发Split 缺点 - 不考虑Region数量 - 大表和小表使用相同阈值不公平0.94版本之后动态阈值触发条件 当某个Region中的某个Store下所有StoreFile的总大小 超过 Min(R^2 × hbase.hregion.memstore.flush.size, hbase.hregion.max.filesize) 其中 - R 当前RegionServer中属于该Table的Region个数 - hbase.hregion.memstore.flush.size 128MB默认 - hbase.hregion.max.filesize 10GB默认 计算示例 假设一个Table在RegionServer上有4个Region R 4 R^2 16 阈值 Min(16 × 128MB, 10GB) Min(2GB, 10GB) 2GB 即当StoreFile总大小超过2GB时触发Split 假设一个Table在RegionServer上有10个Region R 10 R^2 100 阈值 Min(100 × 128MB, 10GB) Min(12.8GB, 10GB) 10GB 即当StoreFile总大小超过10GB时触发Split动态阈值的优势场景R值阈值效果表刚创建Region少1~3128MB~1.1GB快速Split快速分散表已成熟Region多1010GB上限避免过度Split大表大接近10GB减少Split频率小表小较小及时Split避免单Region过大相关配置参数!-- Region Split阈值上限 --propertynamehbase.hregion.max.filesize/namevalue10737418240/value!-- 10GB --/property!-- MemStore Flush大小参与Split阈值计算 --propertynamehbase.hregion.memstore.flush.size/namevalue134217728/value!-- 128MB --/property3.3 Region Split的详细过程Region Split是一个复杂的过程涉及多个组件的协作上图展示了Region Split的完整流程RegionServer决定Split一个Region在Zookeeper中创建Split节点通知HMasterHMaster更新Meta表标记Region正在SplitRegionServer关闭父Region创建两个子Region子Region打开开始服务HMaster更新Meta表标记Split完成父Region的数据通过引用文件Reference File访问Compaction时将引用文件的数据复制到子Region步骤详解步骤1Split准备RegionServer检测到某个Region需要Split - StoreFile总大小超过阈值 - 或手动触发Split 计算Split点 - 读取Region中所有StoreFile的RowKey范围 - 找到中间位置的RowKey作为Split点 - 例如RowKey范围是[row1, row10000] - Split点可能是row5000步骤2在Zookeeper中注册Split状态RegionServer在Zookeeper中创建节点 /hbase/region-in-transition/parent_region_name:SPLITTING 通知HMaster - HMaster监听Zookeeper节点 - 发现Split事件开始协调步骤3关闭父RegionRegionServer执行 1. 暂停父Region的写入 2. 等待正在进行的操作完成 3. 关闭父Region 4. 父Region不再接受新的读写请求步骤4创建子Region在HDFS上创建子Region的目录结构 /hbase/data/default/table_name/ ├── parent_region/ ← 父Region已关闭 │ └── column_family/ │ └── hfiles... │ ├── daughterA_region/ ← 子Region A │ └── column_family/ │ └── reference_files ← 引用文件指向父Region的数据 │ └── daughterB_region/ ← 子Region B └── column_family/ └── reference_files ← 引用文件指向父Region的数据 引用文件Reference File - 不是真正的数据文件 - 包含指向父Region HFile的引用 - 以及Split点信息上半部分或下半部分 - 读取时通过引用文件访问父Region的数据步骤5打开子RegionRegionServer执行 1. 打开daughterA和daughterB 2. daughterA负责 [StartRow, SplitPoint) 3. daughterB负责 [SplitPoint, EndRow) 4. 子Region开始接受读写请求步骤6更新Meta表HMaster更新hbase:meta表 删除父Region的记录 - RegionName: parent_region - StartRow: row1 - EndRow: row10000 - Location: RegionServerA 添加子Region的记录 - RegionName: daughterA - StartRow: row1 - EndRow: row5000Split点 - Location: RegionServerA - RegionName: daughterB - StartRow: row5000 - EndRow: row10000 - Location: RegionServerA步骤7数据迁移异步Split完成后子Region通过引用文件访问父Region的数据 读取daughterA的数据 1. 检查daughterA的HFile 2. 检查daughterA的引用文件 3. 通过引用文件找到父Region的HFile 4. 只读取RowKey SplitPoint的数据 数据迁移通过Compaction - 子Region触发Minor Compaction - 将引用文件指向的数据复制到子Region的HFile中 - 完成后删除引用文件 - 父Region的数据在确认无引用后删除步骤8清理父Region当所有子Region的引用文件都被Compaction处理完毕后 - HMaster通知RegionServer删除父Region - 父Region的HFile被删除 - Zookeeper中的Split节点被删除 - Split过程完全结束3.4 Region Split的注意事项注意1Split过程中的读写Split期间的数据访问 1. Split准备阶段关闭父Region前 - 读写正常进行 2. Split执行阶段关闭父Region后子Region打开前 - 短暂阻塞通常毫秒级 - 客户端会重试 3. Split完成后子Region打开后 - 读写恢复正常 - 客户端通过Meta Cache找到新的Region位置 注意 - Split过程对应用基本透明 - 客户端会自动处理Region位置变化 - 但大量并发Split可能导致短暂的性能波动注意2Split后的负载均衡Split完成后两个子Region通常在同一RegionServer上 RegionServerA ├── daughterA (row1~row5000) └── daughterB (row5000~row10000) HMaster会检查负载均衡 - 如果RegionServerA的Region数量过多 - 会将daughterB迁移到RegionServerB 迁移后 RegionServerA └── daughterA (row1~row5000) RegionServerB └── daughterB (row5000~row10000) 这样实现了负载均衡注意3预分区避免自动Split频繁的自动Split会带来性能波动。对于已知数据分布的场景建议预分区# 手动设定预分区hbase(main):001:0createstaff1,info,partition1, SPLITS[1000,2000,3000,4000]# 生成16进制序列预分区hbase(main):002:0createstaff2,info,partition2,{NUMREGIONS15, SPLITALGOHexStringSplit}# 按照文件中设置的规则预分区# 创建splits.txt文件内容如下# aaaa# bbbb# cccc# ddddhbase(main):003:0createstaff3,partition3, SPLITS_FILEsplits.txt上图展示了加盐SaltRowKey的设计通过前缀将自增RowKey分散到不同Region避免热点问题。3.5 Region Split的优化策略策略1预分区// Java API创建预分区表byte[][]splitKeysnewbyte[][]{Bytes.toBytes(1000),Bytes.toBytes(2000),Bytes.toBytes(3000),Bytes.toBytes(4000)};HBaseAdminadminnewHBaseAdmin(conf);HTableDescriptortableDescnewHTableDescriptor(TableName.valueOf(staff));tableDesc.addFamily(newHColumnDescriptor(info));admin.createTable(tableDesc,splitKeys);策略2调整Split阈值!-- 增加Split阈值减少Split频率 --propertynamehbase.hregion.max.filesize/namevalue21474836480/value!-- 20GB --/property策略3手动控制Split# 手动触发Splithbase(main):001:0splittable_name# 指定Split点hbase(main):002:0splittable_name,split_rowkey# 或Split某个Regionhbase(main):003:0splitregion_name四、Compaction与Region Split的关系4.1 相互影响Compaction和Region Split相互影响需要协调场景1Compaction触发Split - Major Compaction后HFile变大 - 可能超过Split阈值 - 触发Region Split 场景2Split影响Compaction - Split后子Region的HFile较小 - 更容易触发Minor Compaction - 加速数据迁移引用文件的处理 场景3同时触发时的优先级 - HBase优先处理Split - Split完成后再触发Compaction - 避免同时进行大量IO操作4.2 协同优化优化策略 1. 预分区 合理Compaction - 预分区避免频繁自动Split - 手动控制Major Compaction时机 - 在业务低峰期进行Compaction 2. 监控Compaction和Split频率 - 如果Compaction过于频繁增加阈值 - 如果Split过于频繁增加预分区或调整阈值 - 保持两者平衡 3. 避免Compaction和Split同时进行 - Major Compaction期间禁止手动Split - Split完成后等待数据迁移完成再Compaction五、监控与调优实践5.1 Compaction监控通过Web UI监控RegionServer Web UI (http://regionserver:16030) ├── Block Cache ├── Tasks ← 查看正在进行的Compaction任务 ├── Memory └── Requests通过JMX监控// Compaction队列大小hbase.regionserver.compactionQueueSize// Compaction时间hbase.regionserver.compactionTime// Compaction IOhbase.regionserver.compactionKbytes通过Shell监控# 查看Compaction状态hbase(main):001:0compaction_statetable_name# 输出MAJOR, MINOR, NONE# 手动触发Compactionhbase(main):002:0compacttable_name# Minor Compactionhbase(main):003:0major_compacttable_name# Major Compaction5.2 Region Split监控通过Web UI监控HMaster Web UI (http://master:16010) ├── Table Regions ← 查看Region数量和分布 ├── Region Servers ← 查看各节点的Region数量 └── Procedures ← 查看正在进行的Split操作通过Shell监控# 查看表的Region信息hbase(main):001:0list_regionstable_name# 查看Region详情hbase(main):002:0regioninforegion_name5.3 调优检查清单检查项正常范围异常处理HFile数量/Store 10个检查Compaction是否正常Compaction队列 5个增加Compaction线程或降低频率Major Compaction间隔7天左右根据业务调整Region大小1~10GB过大则Split过小则合并Region数量/RegionServer20~100个过多则调整预分区过少则SplitSplit频率每天几次过于频繁则预分区或增加阈值六、总结6.1 Compaction核心要点类型Minor CompactionMajor Compaction合并范围部分相邻HFile所有HFile数据清理不清理清理过期数据和Delete标记触发条件HFile数量达到阈值时间间隔默认7天或手动IO开销中等高频率高低目的减少HFile数量彻底清理数据优化存储6.2 Region Split核心要点项目说明触发条件StoreFile总大小超过阈值动态计算Split点中间位置的RowKey子Region两个分别负责上下半部分RowKey范围数据访问通过引用文件访问父Region数据数据迁移通过Compaction异步完成负载均衡HMaster自动迁移子Region6.3 优化口诀Compaction调优Minor频繁控文件Major定时清数据手动控制避高峰分层策略更精细。Region Split调优预分区避频繁Split动态阈值自适应监控大小及时调负载均衡保稳定。如果本文对你有帮助欢迎点赞、收藏、关注专栏有问题请在评论区留言讨论。
HBase Compaction与Region Split的数据治理机制
发布时间:2026/6/8 1:04:19
一、引言为什么需要Compaction和Region Split在前两篇文章中我们了解了HBase的读写流程和MemStore Flush机制。Flush将MemStore中的数据刷写到HDFS生成HFile文件。但随着数据不断写入会产生以下问题HFile数量爆炸每次Flush生成一个新HFile导致大量小文件读取性能下降读取需要查找多个HFileIO开销增大存储空间浪费过期数据、删除标记占用空间Region过大单个Region数据量过大影响读写性能HBase通过**Compaction合并和Region Split分裂**两个机制来解决这些问题。本文将深入剖析这两个机制的工作原理、触发条件和优化策略。二、Compaction机制详解2.1 什么是CompactionCompaction是HBase将多个小的HFile合并成更大的HFile的过程同时清理过期数据和删除标记。它是HBase自动执行的后台任务对上层应用透明。上图展示了Compaction的两种类型Minor Compaction选择部分相邻的小HFile合并成较大的HFileMajor Compaction合并Store中的所有HFile清理过期数据2.2 为什么需要Compaction问题1HFile数量过多影响读取性能场景一个Store有10个HFile读取一个RowKey 无Compaction时 - 需要检查所有10个HFile - 即使布隆过滤器过滤掉大部分仍需打开多个文件 - 读取延迟 10 × 文件打开时间 数据读取时间 有Compaction后合并为2个HFile - 只需检查2个HFile - 读取延迟 2 × 文件打开时间 数据读取时间 - 性能提升约5倍问题2过期数据占用存储空间场景VERSIONS1同一Cell多次更新 HFile-1: row1|cf:c1|ts1000 → value1 HFile-2: row1|cf:c1|ts2000 → value2 HFile-3: row1|cf:c1|ts3000 → value3 由于VERSIONS1只有ts3000的版本有效 但ts1000和ts2000的版本仍占用存储空间 Compaction后 HFile-merged: row1|cf:c1|ts3000 → value3 旧版本被清理释放存储空间问题3删除标记需要物理清理场景删除一行数据 写入时 Put: row1|cf:c1|ts1000 → value1 Delete: row1|cf:c1|ts2000 → (Delete标记) 读取时 - 发现ts2000的Delete标记 - 返回数据不存在 - 但ts1000的数据仍存储在HFile中 Compaction后 - Delete标记和旧数据都被物理删除 - 释放存储空间2.3 Minor Compaction详解定义与特点Minor Compaction小合并是将相邻的若干个小HFile合并成一个较大的HFile的过程。核心特点特性说明选择性合并只选择部分相邻的HFile不是全部不清理过期数据保留所有版本的数据和Delete标记IO开销小涉及的数据量小对业务影响小频率高自动频繁触发保持HFile数量可控触发条件Minor Compaction的触发条件由多个参数控制!-- hbase-site.xml中的Compaction配置 --!-- 触发Compaction的最小HFile数量 --propertynamehbase.hstore.compaction.min/namevalue3/value/property!-- 触发Compaction的最大HFile数量 --propertynamehbase.hstore.compaction.max/namevalue10/value/property!-- HFile大小小于此值时被认为是小文件优先Compaction --propertynamehbase.hstore.compaction.min.size/namevalue134217728/value!-- 128MB --/property!-- HFile大小大于此值时被认为是大文件不参与Compaction --propertynamehbase.hstore.compaction.max.size/namevalue9223372036854775807/value!-- Long.MAX_VALUE默认无上限 --/property!-- Compaction的比率阈值 --propertynamehbase.hstore.compaction.ratio/namevalue1.2/value/property触发逻辑1. 检查Store中的HFile数量 - 如果HFile数量 hbase.hstore.compaction.min默认3 - 进入Compaction候选队列 2. 选择Compaction的文件 - 从最小的HFile开始选择相邻的文件 - 文件大小满足file_size sum(larger_files) × ratio - 即小文件的大小不超过比它大的文件总和的ratio倍 3. 执行Compaction - 将选中的HFile合并成一个新的HFile - 新HFile的大小约为选中文件大小之和Minor Compaction的执行过程执行前 Store ├── HFile-1 (5MB) ├── HFile-2 (8MB) ├── HFile-3 (10MB) ← 选中合并 ├── HFile-4 (15MB) ← 选中合并 ├── HFile-5 (20MB) └── HFile-6 (100MB) ← 太大不参与 Minor Compaction选中HFile-3和HFile-4 - 10MB 15MB 25MB - 25MB (20MB 100MB) × 1.2 144MB ✓ 执行后 Store ├── HFile-1 (5MB) ├── HFile-2 (8MB) ├── HFile-new (25MB) ← 合并生成 ├── HFile-5 (20MB) └── HFile-6 (100MB) 旧HFile-3和HFile-4标记为删除在下次Compaction时清理Minor Compaction的IO模型Minor Compaction采用顺序读写模式效率较高读取阶段 - 顺序读取选中的HFile - 按RowKey顺序合并 - 内存中维护一个优先队列PriorityQueue 写入阶段 - 顺序写入新的HFile - 生成Block Index和布隆过滤器 - 原子替换旧HFile 特点 - 顺序读 顺序写 磁盘IO效率极高 - 不涉及随机IO - 对业务影响较小2.4 Major Compaction详解定义与特点Major Compaction大合并是将Store中的所有HFile合并成一个大HFile的过程同时清理过期数据和Delete标记。核心特点特性说明全量合并合并Store中的所有HFile清理过期数据删除超过版本数限制的数据物理删除清理Delete标记的数据IO开销大涉及全部数据消耗大量磁盘IO频率低默认7天一次或手动触发触发条件!-- Major Compaction配置 --!-- Major Compaction的时间间隔毫秒 --propertynamehbase.hregion.majorcompaction/namevalue604800000/value!-- 7天 7 × 24 × 60 × 60 × 1000 --/property!-- Major Compaction的抖动时间毫秒 --propertynamehbase.hregion.majorcompaction.jitter/namevalue0.50/value!-- 50%的抖动避免所有Region同时Compaction --/property触发逻辑1. 时间触发 - 距离上次Major Compaction超过7天 - 加上抖动时间避免所有Region同时触发 2. 手动触发 - 通过Shell命令major_compact table_name - 通过Java APIadmin.majorCompact(TableName.valueOf(table_name)) 3. 禁用自动触发 - 设置hbase.hregion.majorcompaction 0 - 完全手动控制Major Compaction时机Major Compaction的执行过程执行前 Store ├── HFile-1 (5MB) ├── HFile-2 (8MB) ├── HFile-3 (10MB) ├── HFile-4 (15MB) ├── HFile-5 (20MB) └── HFile-6 (100MB) Major Compaction合并所有HFile 读取阶段 1. 打开所有6个HFile 2. 使用优先队列PriorityQueue按RowKey排序 3. 逐个读取KeyValue 处理阶段 4. 版本控制只保留最新VERSIONS个版本 - 如果VERSIONS3同一Cell只保留3个最新版本 - 旧版本丢弃 5. TTL检查删除超过TTL的数据 - 如果TTL86400秒1天 - 当前时间 - timestamp 86400的数据丢弃 6. Delete标记清理 - 如果Delete标记后没有新版本 - 删除该Cell的所有历史版本 - 物理删除数据 写入阶段 7. 顺序写入新的HFile 8. 生成Block Index和布隆过滤器 9. 原子替换所有旧HFile 执行后 Store └── HFile-merged (约158MB) ← 合并后的单一大文件Major Compaction的数据清理规则清理规则按优先级 1. Delete标记清理最彻底 场景Put Delete HFile-1: row1|cf:c1|ts1000 → value1 HFile-2: row1|cf:c1|ts2000 → (Delete标记) Major Compaction后 - 如果Delete后没有新版本完全删除该Cell - 结果该Cell不存在 2. 版本数限制清理 场景VERSIONS1多次Put HFile-1: row1|cf:c1|ts1000 → value1 HFile-2: row1|cf:c1|ts2000 → value2 HFile-3: row1|cf:c1|ts3000 → value3 Major Compaction后 - 只保留最新1个版本 - 结果row1|cf:c1|ts3000 → value3 3. TTL过期清理 场景TTL3600秒1小时当前时间5000 HFile-1: row1|cf:c1|ts1000 → value1 ← 5000-100040003600过期 HFile-2: row1|cf:c1|ts4500 → value2 ← 5000-45005003600保留 Major Compaction后 - 删除过期数据 - 结果row1|cf:c1|ts4500 → value22.5 Compaction的IO影响与优化Compaction对业务的影响影响类型Minor CompactionMajor Compaction磁盘IO中等部分文件高全部文件网络IO中等HDFS副本复制高CPU中等排序、合并高内存中等优先队列高对读写的影响较小较大可能阻塞持续时间秒级~分钟级分钟级~小时级Compaction优化策略策略1调整Compaction参数!-- 增加Minor Compaction的阈值减少频率 --propertynamehbase.hstore.compaction.min/namevalue5/value!-- 默认3增加到5 --/property!-- 增加Minor Compaction的最大文件数 --propertynamehbase.hstore.compaction.max/namevalue15/value!-- 默认10增加到15 --/property!-- 调整Compaction比率 --propertynamehbase.hstore.compaction.ratio/namevalue1.0/value!-- 默认1.2降低为1.0更积极合并 --/property策略2禁用自动Major Compaction手动控制!-- 禁用自动Major Compaction --propertynamehbase.hregion.majorcompaction/namevalue0/value!-- 0表示禁用 --/property然后在业务低峰期手动触发# 手动触发Major Compactionhbase(main):001:0major_compacttable_name# 或只触发某个Regionhbase(main):002:0major_compactregion_name策略3使用分层CompactionStripe CompactionHBase 1.3支持Stripe Compaction策略将HFile按RowKey范围分层传统Compaction Store ├── HFile-1 (全范围) ├── HFile-2 (全范围) └── HFile-3 (全范围) Stripe Compaction Store ├── Stripe-1 (row1~row1000) │ ├── HFile-1 │ └── HFile-2 ├── Stripe-2 (row1001~row2000) │ ├── HFile-3 │ └── HFile-4 └── Stripe-3 (row2001~row3000) └── HFile-5优势Compaction只涉及部分Stripe减少IO读取时只需查找相关Stripe的HFile特别适合范围查询多的场景配置propertynamehbase.hstore.engine.class/namevalueorg.apache.hadoop.hbase.regionserver.StripeStoreEngine/value/property策略4限制Compaction的带宽!-- 限制Compaction的IO带宽字节/秒 --propertynamehbase.hstore.compaction.throughput.lower.bound/namevalue52428800/value!-- 50MB/s --/propertypropertynamehbase.hstore.compaction.throughput.upper.bound/namevalue104857600/value!-- 100MB/s --/property三、Region Split机制详解3.1 为什么需要Region SplitHBase表的数据按RowKey范围分布在多个Region中。初始时一张表只有一个Region。随着数据不断增长单个Region会变得越来越大带来以下问题问题说明影响读写热点所有请求集中在一个RegionServer该节点负载过高Compaction耗时大Region的Compaction涉及大量数据影响业务恢复时间长RegionServer宕机时大Region恢复慢可用性降低MemStore过大大Region的MemStore占用更多内存增加GC压力Region Split将大Region分裂成两个较小的Region分散负载。上图展示了Region Split的基本概念一个大的Region分裂成两个较小的Region分别负责不同的RowKey范围。3.2 Region Split的触发条件Region Split的触发条件在HBase不同版本中有所变化0.94版本之前简单阈值触发条件 当某个Region中的某个Store下所有StoreFile的总大小 超过 hbase.hregion.max.filesize默认10GB 即StoreFile总大小 10GB → 触发Split 缺点 - 不考虑Region数量 - 大表和小表使用相同阈值不公平0.94版本之后动态阈值触发条件 当某个Region中的某个Store下所有StoreFile的总大小 超过 Min(R^2 × hbase.hregion.memstore.flush.size, hbase.hregion.max.filesize) 其中 - R 当前RegionServer中属于该Table的Region个数 - hbase.hregion.memstore.flush.size 128MB默认 - hbase.hregion.max.filesize 10GB默认 计算示例 假设一个Table在RegionServer上有4个Region R 4 R^2 16 阈值 Min(16 × 128MB, 10GB) Min(2GB, 10GB) 2GB 即当StoreFile总大小超过2GB时触发Split 假设一个Table在RegionServer上有10个Region R 10 R^2 100 阈值 Min(100 × 128MB, 10GB) Min(12.8GB, 10GB) 10GB 即当StoreFile总大小超过10GB时触发Split动态阈值的优势场景R值阈值效果表刚创建Region少1~3128MB~1.1GB快速Split快速分散表已成熟Region多1010GB上限避免过度Split大表大接近10GB减少Split频率小表小较小及时Split避免单Region过大相关配置参数!-- Region Split阈值上限 --propertynamehbase.hregion.max.filesize/namevalue10737418240/value!-- 10GB --/property!-- MemStore Flush大小参与Split阈值计算 --propertynamehbase.hregion.memstore.flush.size/namevalue134217728/value!-- 128MB --/property3.3 Region Split的详细过程Region Split是一个复杂的过程涉及多个组件的协作上图展示了Region Split的完整流程RegionServer决定Split一个Region在Zookeeper中创建Split节点通知HMasterHMaster更新Meta表标记Region正在SplitRegionServer关闭父Region创建两个子Region子Region打开开始服务HMaster更新Meta表标记Split完成父Region的数据通过引用文件Reference File访问Compaction时将引用文件的数据复制到子Region步骤详解步骤1Split准备RegionServer检测到某个Region需要Split - StoreFile总大小超过阈值 - 或手动触发Split 计算Split点 - 读取Region中所有StoreFile的RowKey范围 - 找到中间位置的RowKey作为Split点 - 例如RowKey范围是[row1, row10000] - Split点可能是row5000步骤2在Zookeeper中注册Split状态RegionServer在Zookeeper中创建节点 /hbase/region-in-transition/parent_region_name:SPLITTING 通知HMaster - HMaster监听Zookeeper节点 - 发现Split事件开始协调步骤3关闭父RegionRegionServer执行 1. 暂停父Region的写入 2. 等待正在进行的操作完成 3. 关闭父Region 4. 父Region不再接受新的读写请求步骤4创建子Region在HDFS上创建子Region的目录结构 /hbase/data/default/table_name/ ├── parent_region/ ← 父Region已关闭 │ └── column_family/ │ └── hfiles... │ ├── daughterA_region/ ← 子Region A │ └── column_family/ │ └── reference_files ← 引用文件指向父Region的数据 │ └── daughterB_region/ ← 子Region B └── column_family/ └── reference_files ← 引用文件指向父Region的数据 引用文件Reference File - 不是真正的数据文件 - 包含指向父Region HFile的引用 - 以及Split点信息上半部分或下半部分 - 读取时通过引用文件访问父Region的数据步骤5打开子RegionRegionServer执行 1. 打开daughterA和daughterB 2. daughterA负责 [StartRow, SplitPoint) 3. daughterB负责 [SplitPoint, EndRow) 4. 子Region开始接受读写请求步骤6更新Meta表HMaster更新hbase:meta表 删除父Region的记录 - RegionName: parent_region - StartRow: row1 - EndRow: row10000 - Location: RegionServerA 添加子Region的记录 - RegionName: daughterA - StartRow: row1 - EndRow: row5000Split点 - Location: RegionServerA - RegionName: daughterB - StartRow: row5000 - EndRow: row10000 - Location: RegionServerA步骤7数据迁移异步Split完成后子Region通过引用文件访问父Region的数据 读取daughterA的数据 1. 检查daughterA的HFile 2. 检查daughterA的引用文件 3. 通过引用文件找到父Region的HFile 4. 只读取RowKey SplitPoint的数据 数据迁移通过Compaction - 子Region触发Minor Compaction - 将引用文件指向的数据复制到子Region的HFile中 - 完成后删除引用文件 - 父Region的数据在确认无引用后删除步骤8清理父Region当所有子Region的引用文件都被Compaction处理完毕后 - HMaster通知RegionServer删除父Region - 父Region的HFile被删除 - Zookeeper中的Split节点被删除 - Split过程完全结束3.4 Region Split的注意事项注意1Split过程中的读写Split期间的数据访问 1. Split准备阶段关闭父Region前 - 读写正常进行 2. Split执行阶段关闭父Region后子Region打开前 - 短暂阻塞通常毫秒级 - 客户端会重试 3. Split完成后子Region打开后 - 读写恢复正常 - 客户端通过Meta Cache找到新的Region位置 注意 - Split过程对应用基本透明 - 客户端会自动处理Region位置变化 - 但大量并发Split可能导致短暂的性能波动注意2Split后的负载均衡Split完成后两个子Region通常在同一RegionServer上 RegionServerA ├── daughterA (row1~row5000) └── daughterB (row5000~row10000) HMaster会检查负载均衡 - 如果RegionServerA的Region数量过多 - 会将daughterB迁移到RegionServerB 迁移后 RegionServerA └── daughterA (row1~row5000) RegionServerB └── daughterB (row5000~row10000) 这样实现了负载均衡注意3预分区避免自动Split频繁的自动Split会带来性能波动。对于已知数据分布的场景建议预分区# 手动设定预分区hbase(main):001:0createstaff1,info,partition1, SPLITS[1000,2000,3000,4000]# 生成16进制序列预分区hbase(main):002:0createstaff2,info,partition2,{NUMREGIONS15, SPLITALGOHexStringSplit}# 按照文件中设置的规则预分区# 创建splits.txt文件内容如下# aaaa# bbbb# cccc# ddddhbase(main):003:0createstaff3,partition3, SPLITS_FILEsplits.txt上图展示了加盐SaltRowKey的设计通过前缀将自增RowKey分散到不同Region避免热点问题。3.5 Region Split的优化策略策略1预分区// Java API创建预分区表byte[][]splitKeysnewbyte[][]{Bytes.toBytes(1000),Bytes.toBytes(2000),Bytes.toBytes(3000),Bytes.toBytes(4000)};HBaseAdminadminnewHBaseAdmin(conf);HTableDescriptortableDescnewHTableDescriptor(TableName.valueOf(staff));tableDesc.addFamily(newHColumnDescriptor(info));admin.createTable(tableDesc,splitKeys);策略2调整Split阈值!-- 增加Split阈值减少Split频率 --propertynamehbase.hregion.max.filesize/namevalue21474836480/value!-- 20GB --/property策略3手动控制Split# 手动触发Splithbase(main):001:0splittable_name# 指定Split点hbase(main):002:0splittable_name,split_rowkey# 或Split某个Regionhbase(main):003:0splitregion_name四、Compaction与Region Split的关系4.1 相互影响Compaction和Region Split相互影响需要协调场景1Compaction触发Split - Major Compaction后HFile变大 - 可能超过Split阈值 - 触发Region Split 场景2Split影响Compaction - Split后子Region的HFile较小 - 更容易触发Minor Compaction - 加速数据迁移引用文件的处理 场景3同时触发时的优先级 - HBase优先处理Split - Split完成后再触发Compaction - 避免同时进行大量IO操作4.2 协同优化优化策略 1. 预分区 合理Compaction - 预分区避免频繁自动Split - 手动控制Major Compaction时机 - 在业务低峰期进行Compaction 2. 监控Compaction和Split频率 - 如果Compaction过于频繁增加阈值 - 如果Split过于频繁增加预分区或调整阈值 - 保持两者平衡 3. 避免Compaction和Split同时进行 - Major Compaction期间禁止手动Split - Split完成后等待数据迁移完成再Compaction五、监控与调优实践5.1 Compaction监控通过Web UI监控RegionServer Web UI (http://regionserver:16030) ├── Block Cache ├── Tasks ← 查看正在进行的Compaction任务 ├── Memory └── Requests通过JMX监控// Compaction队列大小hbase.regionserver.compactionQueueSize// Compaction时间hbase.regionserver.compactionTime// Compaction IOhbase.regionserver.compactionKbytes通过Shell监控# 查看Compaction状态hbase(main):001:0compaction_statetable_name# 输出MAJOR, MINOR, NONE# 手动触发Compactionhbase(main):002:0compacttable_name# Minor Compactionhbase(main):003:0major_compacttable_name# Major Compaction5.2 Region Split监控通过Web UI监控HMaster Web UI (http://master:16010) ├── Table Regions ← 查看Region数量和分布 ├── Region Servers ← 查看各节点的Region数量 └── Procedures ← 查看正在进行的Split操作通过Shell监控# 查看表的Region信息hbase(main):001:0list_regionstable_name# 查看Region详情hbase(main):002:0regioninforegion_name5.3 调优检查清单检查项正常范围异常处理HFile数量/Store 10个检查Compaction是否正常Compaction队列 5个增加Compaction线程或降低频率Major Compaction间隔7天左右根据业务调整Region大小1~10GB过大则Split过小则合并Region数量/RegionServer20~100个过多则调整预分区过少则SplitSplit频率每天几次过于频繁则预分区或增加阈值六、总结6.1 Compaction核心要点类型Minor CompactionMajor Compaction合并范围部分相邻HFile所有HFile数据清理不清理清理过期数据和Delete标记触发条件HFile数量达到阈值时间间隔默认7天或手动IO开销中等高频率高低目的减少HFile数量彻底清理数据优化存储6.2 Region Split核心要点项目说明触发条件StoreFile总大小超过阈值动态计算Split点中间位置的RowKey子Region两个分别负责上下半部分RowKey范围数据访问通过引用文件访问父Region数据数据迁移通过Compaction异步完成负载均衡HMaster自动迁移子Region6.3 优化口诀Compaction调优Minor频繁控文件Major定时清数据手动控制避高峰分层策略更精细。Region Split调优预分区避频繁Split动态阈值自适应监控大小及时调负载均衡保稳定。如果本文对你有帮助欢迎点赞、收藏、关注专栏有问题请在评论区留言讨论。