05-04-B-HBase与时序库面试与生产事故实战
️ 关键词:HBase 面试题 · LSM-Tree · RowKey 设计 · 写热点 · Region · 时序库 · 降采样 · 监控存储
📌 导读 :A 篇讲"LSM-Tree 怎么工作、RowKey 怎么设计、时序库怎么降采样",本篇讲"面试官怎么考、生产上怎么炸、炸了怎么救 "。高频题按知识依赖链一问一答连贯展开 (每题: 30 秒电梯版 → 深挖版 → 追问链),事故按场景 → 根因 → 预防 → 解决 → 一句话教训五段式复盘。原理细节标注"见 A 篇 x.x 节"。
📑 目录
- 05-04-B-HBase与时序库面试与生产事故实战
-
- 一、高频面试题精讲(连贯问答链)
-
- [链条①:LSM-Tree 与存储四连问](#链条①:LSM-Tree 与存储四连问)
- [链条②:RowKey 与 Region 三连问](#链条②:RowKey 与 Region 三连问)
- 链条③:时序库与选型三连问
- 二、生产事故案例集(五段式复盘)
-
- [事故一:RowKey 时间戳开头,写热点打爆单 Region](#事故一:RowKey 时间戳开头,写热点打爆单 Region)
- [事故二:建表不预分区,单 Region 写入瓶颈](#事故二:建表不预分区,单 Region 写入瓶颈)
- [事故三:user_id 放 Tag,时序库索引爆炸](#事故三:user_id 放 Tag,时序库索引爆炸)
- [事故四:1s 原始数据全量留 1 年,存储爆炸](#事故四:1s 原始数据全量留 1 年,存储爆炸)
- 三、事故速查表
- 四、面试答题万能框架
- [五、与 A 篇的知识点映射](#五、与 A 篇的知识点映射)
一、高频面试题精讲(连贯问答链)
链条①:LSM-Tree 与存储四连问
Q1:HBase 为什么写入快?LSM-Tree 的原理?
** 30 秒电梯版**
LSM-Tree 写流程(见 A 篇 2.1):
- 写 WAL(HLog):预写日志,持久化保证,crash 时恢复未刷盘数据;
- 写 MemStore :内存有序写缓冲,按 RowKey 排序------写入毫秒级,无磁盘 IO;
- MemStore 满刷盘:默认 128MB 满后 Flush 成 HFile(有序数据文件,不可变);
- 后台 Compaction:合并多个 HFile 成大 HFile,清理删除标记。
为什么快 :写路径全程顺序 IO(MemStore 内存写 + HFile 顺序刷盘),没有 B+ 树的随机写维护开销。
一句话总结 :LSM-Tree 把随机写变成顺序写------写 WAL 保命、写 MemStore 求快、刷盘成 HFile、后台 Compaction 合并。
** 深挖版**
| 要点 | 说明 |
|---|---|
| WAL 的作用 | MemStore 是内存,crash 会丢------WAL 先落盘,恢复时重放 WAL |
| HFile 不可变 | 和 ES Segment 同款思想------不可变 = 无锁 + 可压缩 + 可缓存 |
| 与 ClickHouse MergeTree 对比 | 同源 LSM 思想:part/HFile 顺序写 + 后台 merge/Compaction------ClickHouse 为列存分析优化,HBase 为 KV 点查优化 |
** 追问链**:读取流程呢?→ Q2
Q2:HBase 读取为什么比写入慢?怎么优化?
** 30 秒电梯版**
读流程 (见 A 篇 2.2):查 MemStore → BlockCache(读缓存)→ 多个 HFile(从新到旧,布隆过滤器排除不含目标 RowKey 的 HFile)→ 合并多版本。
读慢的原因(读放大) :一行数据可能分散在 MemStore + 多个 HFile 中------要查多层、合并多版本。
优化手段:
- Compaction:减少 HFile 数量 = 减少读放大;
- 布隆过滤器 :快速排除不含目标的 HFile------点查加速神器;
- BlockCache :热数据缓存内存------热点查询加速。
一句话总结 :读放大是 LSM-Tree 的代价------Compaction 减 HFile 数、布隆过滤器跳过无关文件、BlockCache 缓存热数据,三板斧缓解读放大。
** 深挖版**
| 要点 | 说明 |
|---|---|
| 布隆过滤器原理 | 概率数据结构------判断"一定不在"或"可能在",假阳性率可配(默认 1%) |
| Major vs Minor Compaction | Minor 合并部分 HFile;Major 合并全部 + 清理删除标记------Major 开销大,低峰执行 |
| 与 B+ 树对比 | B+ 树一次查找 O(logN);LSM 可能查多层------点查 B+ 树快,海量写 LSM 快 |
** 追问链**:HBase 的数据模型是什么?→ Q3
Q3:HBase 的数据模型?列族怎么设计?
** 30 秒电梯版**
四维模型 (见 A 篇第三章):RowKey (行键,唯一+排序)+ Column Family (列族,物理分组)+ Column (列,动态+稀疏)+ Timestamp(多版本)。
列族设计三原则:
- 列族要少(1~3 个)------列族多 = MemStore 多 = Flush 频繁 = 小 HFile 多;
- 列族内列可动态------不用预定义,稀疏列不占空间;
- 访问模式相近的列放一族------一起读写的列放一起,减少 IO。
一句话总结 :RowKey 是唯一索引,列族是物理分组(1~3 个),列可动态稀疏,时间戳管多版本------HBase 是 schema-free 的列存 KV。
** 深挖版**
| 要点 | 说明 |
|---|---|
| 稀疏列的优势 | 空列不占空间------宽表(几千列)只有有值的列占空间,MySQL 宽表空列也占 |
| 多版本 | 默认保留 3 个版本------可查历史值,但空间翻倍,不需要就设 VERSIONS=1 |
| 列族与 HFile | 每个列族独立 HFile------列族多 = HFile 多 = Compaction 压力大 |
** 追问链**:RowKey 怎么设计?→ Q5
Q4:HBase 和 MySQL 的区别?什么时候选 HBase?
** 30 秒电梯版**
| 维度 | MySQL | HBase |
|---|---|---|
| 存储引擎 | B+ 树(InnoDB) | LSM-Tree |
| 数据量 | 单表千万级 | 万亿行 |
| 写入 | 随机写(维护树) | 顺序写(LSM) |
| 查询 | SQL/JOIN/事务 | RowKey 点查/scan |
| 索引 | 二级索引 | 只有 RowKey(原生无二级索引) |
| 事务 | ACID | 单行事务 |
| 扩展 | 垂直为主 | 水平扩展(Region) |
选 HBase 的场景:海量明细 KV(用户画像/消息记录/订单历史)、写多读少、毫秒点查、水平扩展。
一句话总结 :MySQL 管"关系和事务"(千万级),HBase 管"海量 KV 点查"(万亿级)------HBase 没有 JOIN 和二级索引,复杂查询走 Phoenix 或 ES。
** 深挖版**
| 要点 | 说明 |
|---|---|
| Phoenix | HBase 的 SQL 层------支持二级索引、JOIN、事务,但性能不如原生 API |
| 与 MongoDB 对比 | MongoDB 文档模型灵活,HBase KV+列族更"底层"------HBase 数据量上限更高,MongoDB 建模更灵活 |
| 强一致 | HBase 单行读写强一致(Region 级)------比 Cassandra 的最终一致强 |
链条②:RowKey 与 Region 三连问
Q5:RowKey 设计的原则?怎么避免热点?
** 30 秒电梯版**
三原则 (见 A 篇 4.1):唯一 (唯一索引)、散列 (防热点)、短(16~100 字节,存在每个 Cell 里)。
热点问题 :RowKey 有序(自增 ID/时间戳开头)→ 新数据全写到最后一个 Region → 写热点。
三种规避:
- 加盐 :RowKey 前加随机前缀(
hash(user_id)%16)------写入均匀,范围查询贵; - 哈希:RowKey = MD5(user_id)------完全散列,牺牲范围查询;
- 反转:RowKey = 反转(user_id/时间戳)------保留部分范围能力。
一句话总结 :RowKey 设计是 HBase 第一设计决策------点查用哈希散列,范围查询用"散列前缀+有序后缀",有序 RowKey 是热点的根源。
** 深挖版**
| 要点 | 说明 |
|---|---|
| 时间戳取反技巧 | Long.MAX_VALUE - timestamp------最新数据字典序排前面,scan 先拿最新 |
| 与 ClickHouse 分片键对比 | 同款热点思想:有序分片键/RowKey = 写热点------散列是通用的解法(19-B 篇事故二) |
| RowKey 长度 | 存在每个 Cell 里------100 字节 RowKey × 10 亿行 = 100GB 光 RowKey |
** 追问链**:Region 是什么?→ Q6
Q6:Region 是什么?为什么要预分区?
** 30 秒电梯版**
Region :HBase 的水平分片------表按 RowKey 范围切成多个 Region,分布在不同 RegionServer(见 A 篇第五章)。Region 过大(默认 10GB)自动 Split。
预分区 :建表时预先指定 Split 点,创建多个 Region------不预分区 = 建表时只有 1 个 Region = 写入全打到 1 个 Region = 热点。
一句话总结 :Region 是 HBase 的分片单位,预分区是建表必做项------不预分区 = 单 Region 热点 = 写入瓶颈。
** 深挖版**
| 要点 | 说明 |
|---|---|
| 预分区数量 | 按节点数 × 每节点 Region 数估算(如 10 节点 × 20 = 200 Region) |
| Region 均衡 | HMaster 负责 Region 分配/负载均衡------RegionServer 宕机后 Region 重新分配 |
| 与 ES 分片对比 | ES 分片创建后不可变(reindex 扩),HBase Region 自动 Split------HBase 扩展更自动 |
** 追问链**:HBase 集群架构?→ Q7
Q7:HBase 集群架构?ZooKeeper 起什么作用?
** 30 秒电梯版**
三大组件:
- HMaster :集群管理(Region 分配/负载均衡/DDL)------不处理读写;
- RegionServer:管理 Region,处理读写、MemStore 刷盘、Compaction;
- ZooKeeper :存 HMaster 地址、Region 元数据入口、集群状态------客户端先查 ZK 找 Region 位置,缓存后直连 RegionServer。
一句话总结 :HMaster 管调度、RegionServer 干活、ZooKeeper 当"通讯录"------ZK 挂了客户端找不到 Region 入口,集群不可用。
** 深挖版**
| 要点 | 说明 |
|---|---|
| HMaster 高可用 | 多 HMaster 备份(ZK 选举 active)------HMaster 挂了不影响读写(只影响 DDL/均衡) |
| RegionServer 宕机 | WAL 在 HDFS 上,Region 重新分配后重放 WAL------数据不丢 |
| ZK 集群 | 3 节点 ZK 集群------ZK 单点是反模式(和 ClickHouse 同款教训,19-B 篇事故四) |
链条③:时序库与选型三连问
Q8:时序数据库和 HBase/ClickHouse 的区别?监控数据存哪?
** 30 秒电梯版**
时序数据五特点(见 A 篇 6.1):带时间戳、只追加、按时间范围查、聚合为主、价值随时间衰减。
分工:
| 需求 | 选谁 |
|---|---|
| 监控指标 + 降采样 + 保留策略 | 时序库(InfluxDB/TDengine) |
| 海量 KV 随机读写 | HBase |
| 海量聚合分析 | ClickHouse |
| 时序数据但无降采样需求 | ClickHouse(也能存时序) |
一句话总结 :监控指标用时序库(降采样+保留策略是核心),海量 KV 用 HBase,海量聚合用 ClickHouse------时序库的"降采样+自动删除"是其他库没有的。
** 深挖版**
| 要点 | 说明 |
|---|---|
| 降采样 | 1s 原始留 7 天 → 1min 聚合留 1 年 → 1h 聚合留 5 年------用精度换空间 |
| Tag vs Field | Tag 建索引(维度),Field 不建索引(值)------高基数维度别放 Tag |
| Prometheus 定位 | 监控告警专用(PromQL + 拉模型)------短期存储,长期存远程(InfluxDB/Thanos) |
** 追问链**:降采样怎么实现?→ Q9
Q9:降采样和保留策略怎么设计?
** 30 秒电梯版**
降采样 :把高精度数据聚合为低精度------连续查询(Continuous Query)定时执行(如每 1min 算一次 avg/max/p99),结果写入低精度表。
保留策略 :数据保留时长------超期自动删除(原始 7 天、1min 聚合 1 年、1h 聚合 5 年)。
查询路由 :查最近 1 小时 → 原始数据;查最近 1 月 → 1min 聚合;查最近 1 年 → 1h 聚合------按时间范围路由到对应精度。
一句话总结 :降采样用连续查询定时聚合,保留策略超期自动删,查询按时间范围路由精度------"老数据不值钱,别用新数据的精度存"。
** 深挖版**
| 要点 | 说明 |
|---|---|
| 降采样的聚合函数 | avg/max/min/p99/sum------监控场景 max/p99 比 avg 有用(avg 掩盖毛刺) |
| 保留策略的存储收益 | 全量 1s 数据留 5 年 vs 降采样后------存储成本差 100 倍以上 |
| 与 ClickHouse TTL 对比 | ClickHouse 也有 TTL(超期删除/降精度)------时序库把降采样做成一等公民 |
** 追问链**:时序库出过什么事故?→ Q10
Q10:HBase/时序库在生产上最容易踩的坑?
** 30 秒电梯版**
四大高频坑(详见第二章事故集):
- RowKey 有序导致写热点:时间戳开头,新数据全打最后一个 Region(事故一);
- 不预分区:建表 1 个 Region,写入瓶颈(事故二);
- 高基数维度放 Tag:user_id 放 Tag,时序库索引爆炸(事故三);
- 不降采样:1s 原始数据全量留 1 年,存储爆炸(事故四)。
一句话总结 :RowKey 要散列、建表要预分区、Tag 要低基数、数据要降采样------四条军规挡住 90% 的 HBase/时序库事故。
** 深挖版**
| 要点 | 说明 |
|---|---|
| 上线检查清单 | RowKey 散列了吗?预分区了吗?Tag 基数检查了吗?降采样+保留策略配了吗? |
| 监控四件套 | Region 均衡度、Compaction 队列、时序库存储增长、Tag 基数 |
| 容量规划 | HBase:数据量 × 副本数 / 单节点容量;时序库:原始数据量 × 保留天数 × 压缩率 |
二、生产事故案例集(五段式复盘)
事故一:RowKey 时间戳开头,写热点打爆单 Region
** 事故场景**
用户行为记录表 RowKey 设计为 timestamp_userId("按时间排序方便查最新")。上线后所有写入集中到最后一个 Region(新数据时间戳最大,字典序排最后),该 Region 所在 RegionServer CPU/IO 打满,写入 RT 从 5ms 恶化到 500ms,其他 RegionServer 空闲。
** 根因分析**
RowKey 有序导致写热点 (Q5):时间戳开头的 RowKey 字典序递增------新数据全写到最后一个 Region。和 ClickHouse 范围分片热点同款(19-B 篇事故二)------有序 RowKey/分片键 = 写热点的根源。
️ 预防方案
- RowKey 散列 :
hash(userId)_timestamp或反转时间戳------写入均匀分布; - 加盐 :RowKey 前加
hash%16前缀------16 个前缀均匀分布到各 Region; - Region 均衡监控 :各 Region 写入量打点------倾斜度 >3 倍告警;
- RowKey 评审 :上线前评估 RowKey 的写入分布------有序 RowKey 直接打回。
** 事故解决**
- 止血:手动 Split 热点 Region(临时缓解);
- 根治 :RowKey 改
hash(userId)_反转(timestamp),数据迁移(新建表+双写切换); - 验证:写入均匀分布到 64 个 Region,单 Region RT 恢复 5ms。
** 一句话教训**
时间戳开头的 RowKey = 给最后一个 Region 上刑------有序 RowKey 是写热点的根源;散列前缀+有序后缀才是正解。
事故二:建表不预分区,单 Region 写入瓶颈
** 事故场景**
消息记录表建表时"先跑起来再说",没做预分区(默认 1 个 Region)。上线后写入 QPS 5000,全部打到 1 个 Region ------单 RegionServer 扛不住,写入 RT 恶化。等 Region 自动 Split 时,Split 速度追不上写入速度,持续热点 2 小时。
** 根因分析**
不预分区 (Q6):建表时 1 个 Region → 写入全集中 → 热点 → 自动 Split 追不上写入。预分区是建表必做项,"先跑起来"的代价是 2 小时写入瓶颈。
️ 预防方案
- 预分区必做 :建表时按 RowKey 分布预切 16~256 个 Region------按节点数 × 每节点 Region 数估算;
- 建表评审:无预分区的建表 DDL 直接打回;
- Region 数监控 :Region 数量 + 单 Region 大小打点------单 Region >5GB 告警;
- 压测验证 :上线前压测写入分布------确认均匀分布到各 Region。
** 事故解决**
- 止血:手动预分区 + 数据迁移;
- 根治:建表规范上线(预分区必做);全表排查未预分区的表(3 张);
- 验证:预分区 64 Region,写入均匀分布,RT 稳定 5ms。
** 一句话教训**
不预分区 = 单 Region 热点 = 写入瓶颈------Split 追不上写入的速度;建表预分区是 HBase 的第一军规。
事故三:user_id 放 Tag,时序库索引爆炸
** 事故场景**
用户行为监控用 InfluxDB,开发把 user_id 放在 Tag 里("方便按用户查")。上线后用户量涨到 1000 万,Tag 基数 1000 万------InfluxDB 的 Tag 索引(TSI)膨胀到 50GB,内存占用暴涨,查询变慢,节点 OOM。
** 根因分析**
高基数维度放 Tag (Q8 深挖版):Tag 建索引------每个 Tag 值组合 = 一条时间序列 = 一个索引项 。user_id 基数 1000 万 = 1000 万条时间序列 = 索引爆炸。Tag 应该放低基数维度(host/region/服务名),高基数维度(user_id)放 Field。
️ 预防方案
- Tag 基数检查 :Tag 值数量 < 1 万------高基数维度放 Field;
- 建模评审 :时序数据建模时评估 Tag 基数------user_id/device_id 等高基数 ID 放 Field;
- 序列数监控 :
SHOW SERIES查时间序列数量------持续增长即告警; - 按用户查的需求 :走应用层聚合或 ClickHouse(列存聚合强)------时序库不擅长高基数维度查询。
** 事故解决**
- 止血:重启节点(临时);
- 根治:user_id 从 Tag 改 Field,数据迁移;按用户查询改 ClickHouse;Tag 基数监控上线;
- 验证:Tag 基数降到 200(host+region+服务名),索引 50MB,查询恢复毫秒级。
** 一句话教训**
user_id 放 Tag = 给索引喂 1000 万个值------Tag 建索引,高基数维度放 Tag = 索引爆炸;Tag 放低基数维度,高基数放 Field。
事故四:1s 原始数据全量留 1 年,存储爆炸
** 事故场景**
监控指标用 InfluxDB,开发"图省事"没配降采样和保留策略------1s 精度的原始数据全量保留。一年后数据量涨到 50TB,磁盘告警,查询变慢(扫描数据量太大)。
** 根因分析**
不降采样+不配保留策略 (Q9):1s 原始数据全量留 1 年 = 3153 万数据点/指标/年 × 1 万指标 = 3000 亿数据点。老数据价值低(没人查 1 年前的 1s 精度),但存储成本和新数据一样。
️ 预防方案
- 降采样必配 :1s 原始留 7 天 → 1min 聚合留 1 年 → 1h 聚合留 5 年------连续查询定时聚合;
- 保留策略必配 :超期自动删除------不配保留策略 = 数据无限增长;
- 存储增长监控 :磁盘占用趋势打点------增长超预期即告警;
- 查询路由 :按时间范围路由到对应精度------查老数据用聚合数据。
** 事故解决**
- 止血:手动删除 1 年前的原始数据(释放 40TB);
- 根治:降采样+保留策略上线;存储增长监控上线;
- 验证:存储稳定在 2TB(降采样后),查询 RT 恢复。
** 一句话教训**
1s 原始数据全量留 1 年 = 用新数据的精度存老数据------老数据不值钱,降采样+保留策略是时序库的存储红利;不配 = 存储爆炸。
三、事故速查表
| 现象 | 可能根因 | 快速定位 | 根治方案 |
|---|---|---|---|
| 单 Region CPU/IO 打满 | RowKey 有序写热点 | 各 Region 写入量对比 | RowKey 散列;加盐/反转 |
| 写入瓶颈、Region 少 | 不预分区 | Region 数量 | 预分区 16~256 Region |
| 时序库索引膨胀、OOM | 高基数维度放 Tag | SHOW SERIES 查序列数 | 高基数放 Field;Tag 基数检查 |
| 存储无限增长 | 不降采样/不配保留策略 | 磁盘增长趋势 | 降采样+保留策略 |
| 读取慢 | HFile 多(读放大) | Compaction 队列;HFile 数 | Major Compaction;布隆过滤器 |
| RegionServer 宕机 | 硬件/网络故障 | HMaster 日志;ZK 状态 | WAL 恢复+Region 重分配(自动) |
| 时序查询慢 | 扫描数据量大 | 查询时间范围+精度 | 查询路由到聚合数据 |
四、面试答题万能框架
#mermaid-svg-vIla8ogjJt150K8l{font-family:Microsoft YaHei;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vIla8ogjJt150K8l .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vIla8ogjJt150K8l .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vIla8ogjJt150K8l .error-icon{fill:hsl(220.5882352941, 100%, 98.3333333333%);}#mermaid-svg-vIla8ogjJt150K8l .error-text{fill:rgb(8.5000000002, 5.7500000001, 0);stroke:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-vIla8ogjJt150K8l .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vIla8ogjJt150K8l .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vIla8ogjJt150K8l .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vIla8ogjJt150K8l .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vIla8ogjJt150K8l .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vIla8ogjJt150K8l .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vIla8ogjJt150K8l .marker{fill:#0b0b0b;stroke:#0b0b0b;}#mermaid-svg-vIla8ogjJt150K8l .marker.cross{stroke:#0b0b0b;}#mermaid-svg-vIla8ogjJt150K8l svg{font-family:Microsoft YaHei;font-size:16px;}#mermaid-svg-vIla8ogjJt150K8l p{margin:0;}#mermaid-svg-vIla8ogjJt150K8l .label{font-family:Microsoft YaHei;color:#333;}#mermaid-svg-vIla8ogjJt150K8l .cluster-label text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-vIla8ogjJt150K8l .cluster-label span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-vIla8ogjJt150K8l .cluster-label span p{background-color:transparent;}#mermaid-svg-vIla8ogjJt150K8l .label text,#mermaid-svg-vIla8ogjJt150K8l span{fill:#333;color:#333;}#mermaid-svg-vIla8ogjJt150K8l .node rect,#mermaid-svg-vIla8ogjJt150K8l .node circle,#mermaid-svg-vIla8ogjJt150K8l .node ellipse,#mermaid-svg-vIla8ogjJt150K8l .node polygon,#mermaid-svg-vIla8ogjJt150K8l .node path{fill:#fff4dd;stroke:hsl(40.5882352941, 60%, 83.3333333333%);stroke-width:1px;}#mermaid-svg-vIla8ogjJt150K8l .rough-node .label text,#mermaid-svg-vIla8ogjJt150K8l .node .label text,#mermaid-svg-vIla8ogjJt150K8l .image-shape .label,#mermaid-svg-vIla8ogjJt150K8l .icon-shape .label{text-anchor:middle;}#mermaid-svg-vIla8ogjJt150K8l .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vIla8ogjJt150K8l .rough-node .label,#mermaid-svg-vIla8ogjJt150K8l .node .label,#mermaid-svg-vIla8ogjJt150K8l .image-shape .label,#mermaid-svg-vIla8ogjJt150K8l .icon-shape .label{text-align:center;}#mermaid-svg-vIla8ogjJt150K8l .node.clickable{cursor:pointer;}#mermaid-svg-vIla8ogjJt150K8l .root .anchor path{fill:#0b0b0b!important;stroke-width:0;stroke:#0b0b0b;}#mermaid-svg-vIla8ogjJt150K8l .arrowheadPath{fill:#0b0b0b;}#mermaid-svg-vIla8ogjJt150K8l .edgePath .path{stroke:#0b0b0b;stroke-width:2.0px;}#mermaid-svg-vIla8ogjJt150K8l .flowchart-link{stroke:#0b0b0b;fill:none;}#mermaid-svg-vIla8ogjJt150K8l .edgeLabel{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-vIla8ogjJt150K8l .edgeLabel p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-vIla8ogjJt150K8l .edgeLabel rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-vIla8ogjJt150K8l .labelBkg{background-color:rgba(243.9999999999, 220.9999999998, 255, 0.5);}#mermaid-svg-vIla8ogjJt150K8l .cluster rect{fill:hsl(220.5882352941, 100%, 98.3333333333%);stroke:hsl(220.5882352941, 60%, 88.3333333333%);stroke-width:1px;}#mermaid-svg-vIla8ogjJt150K8l .cluster text{fill:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-vIla8ogjJt150K8l .cluster span{color:rgb(8.5000000002, 5.7500000001, 0);}#mermaid-svg-vIla8ogjJt150K8l div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:Microsoft YaHei;font-size:12px;background:hsl(220.5882352941, 100%, 98.3333333333%);border:1px solid hsl(220.5882352941, 60%, 88.3333333333%);border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vIla8ogjJt150K8l .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vIla8ogjJt150K8l rect.text{fill:none;stroke-width:0;}#mermaid-svg-vIla8ogjJt150K8l .icon-shape,#mermaid-svg-vIla8ogjJt150K8l .image-shape{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);text-align:center;}#mermaid-svg-vIla8ogjJt150K8l .icon-shape p,#mermaid-svg-vIla8ogjJt150K8l .image-shape p{background-color:hsl(-79.4117647059, 100%, 93.3333333333%);padding:2px;}#mermaid-svg-vIla8ogjJt150K8l .icon-shape .label rect,#mermaid-svg-vIla8ogjJt150K8l .image-shape .label rect{opacity:0.5;background-color:hsl(-79.4117647059, 100%, 93.3333333333%);fill:hsl(-79.4117647059, 100%, 93.3333333333%);}#mermaid-svg-vIla8ogjJt150K8l .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vIla8ogjJt150K8l .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vIla8ogjJt150K8l :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 被问 HBase / 时序库。
① 先给骨架 LSM-Tree 写读
流程+RowKey 三原则 30 秒讲清全貌。
② 按追问深挖 存储线:WAL /
MemStore / HFile /
Compaction 设计线:
RowKey 散列 / 预分区 /
列族 1~3 个 时序线:降采样 /
保留策略 / Tag 基数。
③ 落到生产视角 四条军规:
RowKey 散列 / 预分区必做
Tag 低基数 / 降采样必配。
④ 用事故收尾 写热点打爆 Regi
on / 索引爆炸 / 存储爆炸 有画面感的案例胜过背书。
加分技巧:
- 谈 LSM-Tree 主动讲"写路径全程顺序 IO,读放大用 Compaction+布隆过滤器缓解"------读写不对称的完整理解;
- 谈 RowKey 主动说"时间戳取反让最新数据排前面,scan 先拿最新"------设计技巧;
- 谈时序库主动带"Tag 建索引、高基数放 Field、降采样用精度换空间"------时序建模三要点;
- 谈选型主动讲"监控用时序库、海量 KV 用 HBase、聚合用 ClickHouse、交易用 MySQL"------四者分工;
- 被问"遇到过什么 HBase 问题",用事故一(RowKey 热点)------"有序 RowKey 打爆单 Region"的教训最有设计教育意义。
五、与 A 篇的知识点映射
| 本篇题目/事故 | A 篇《20-A-HBase与时序库详解》对应章节 |
|---|---|
| Q1 LSM-Tree 写 | 2.1 |
| Q2 读放大 | 2.2/2.3 |
| Q3 数据模型 | 三章 |
| Q4 vs MySQL | 1.3/七章 |
| Q5 RowKey 设计 | 4.1/4.2 |
| Q6 Region/预分区 | 5.1/5.2 |
| Q7 集群架构 | 5.1 |
| Q8 时序库分工 | 6.1/七章 |
| Q9 降采样 | 6.3 |
| Q10 生产坑 | 全文军规汇总 |
| 事故一 RowKey 热点 | 4.2 |
| 事故二 不预分区 | 5.2 |
| 事故三 Tag 爆炸 | 6.2 |
| 事故四 不降采样 | 6.3 |
📌 结语 :HBase 面试题的尽头是"设计意识 "------RowKey 设计决定热点与否、预分区决定扩展性、列族数量决定 Compaction 压力;时序库面试题的尽头是"衰减意识 "------数据价值随时间衰减,降采样+保留策略把衰减变成存储红利。生产事故的尽头是"军规意识 "------RowKey 散列、预分区、Tag 低基数、降采样必配,每条军规都是一次事故的墓碑。
📌 配套阅读:
如果这篇文章对你有帮助,欢迎点赞、收藏、关注!🚀