作死系列之论如何搞崩生产环境HBASE
结果描述
- HBASE集群被搞垮一台机器
- 集群的平响有
40ms
左右 暴增至40s
左右
需求背景
- 当前有两条数据流 A 与 B , 消息管道均为 Kafka , 其中, A 数据流中存有行为的
开始与结束信息
, B 数据流中存有在生命周期期之内,离散的行为数据
. - 需要将 A 与 B两个管道的数据合并为一条数据, 同时要将 B 中的离散行为数据合并为一条完整的,合并完成的数据, 写入 hbase.
- 数据大小 : A管道数据1条, 大小 2kb , B管道 单条数据
0.1kb
, 但是数量多,45条/s
, 一个生命周期大约500条
数据, 数据大小500 * 0.1 = 50 kb
.
方案设计
- 使用hbase多列存储数据, data_a 列, 存储 A 数据管道的数据, data_b 列存储数据管道 B 的数据
- B 管道中的数据, 每订阅到一条 , 使用HBase原生支持的
append
方法, 将数据追加到末尾, 从而达到将离散数据合并的目的
.
服务上线
按照如上方案, 进行实现之后, 进行上线. 上线之后, 大约半小时左右, 负责 HBase 同学电话报警, 集群平响异常拉高
, 且有 一台机器发生宕机
. 还好我们留了后手, 从配置中心关闭了对HBase的数据使用, 快速止损.
问题定位
上述方案听起来没啥问题, 技术和业务都没啥问题的样子, 但是为什么上线之后, 会产生这么糙单的问题呢? 这里忽略了一个问题, 也是HBase核心特性之一 : 数据的多版本存储
, 是不是有点想到了什么? 没错, 每次 append 都会生成一个新的数据版本, append 多少次, 就有多少数据版本.
现在依旧假设一个生命周期内, 会产生 500 条离散数据, 那我们来计算一下, 这些数据的大小 :
- 首先, 500 条数据, 就会产生 500 个数据版本,
每一个版本的数据大小 = 前一个数据版本大小 + 单条数据大小
- 数据总条数计算规则 : 1 + (1+2) + (1+2+3) + ... + (1+2+3+....+500) = 20958500 条数据, 大小为 : 20958500 * 0.1 = 2095850 kb, 约为 2046M, 2GB左右
- 业务系统有配置重试, 当写 HBase 超时时, 会重试一次, 假定重试概率为 10%, 那实际写入的数据应给是 500 * (1 + 10%) = 550 , 按照上面的规则重新计算, 数据大小 27880600 kb 约为 2723MB , 2.5GB左右.
怎么样, 数据是不是比想象中的大太多了, 明明预期不过 50kb 左右, 它咋就膨胀到 2.5GB 了, 数据量可是 膨胀5W倍
, 夭寿啊!
方案再设计
- 将 append 操作变更为
put
操作 , 数据量相同的情况下, put 与 append QPS 相同, 但是会节省大量存储空间
- 离散的数据 rowKey 为 :
公共前缀_纳秒时间戳
, 不同生命周期的数据, 公共前缀(基于业务生成)不相同, row key 设计原则参见: hbase row ke的设计原则 - 当订阅到 A 管道中生命周期结束的事件消息, 通过 HBase 的
Scan扫描命令
, 扫描数据, 合并后存入 data_b 列 - 注意, 因为能匹配到公共的前缀, 此处 Scan 命令的性能是有保障的, 可以不用担心.
复盘
为什么在生产环境会导致这个问题?
- 测试环境没有足够的数据, 没做大数据量的数据测试, 而基于 append 的实现, 从业务而言是没问题的, 所以没发现
- 对 HBase 理解程度不足, 方案设计不够谨慎.
那是不是永远不要使用Append命令?非也.因为一次故障就彻底否定一个方案, 全权是一种因噎废食的行为, 只要在使用前充分评估好 数据量
以及 数据大小
等, 完全可以放心使用.
总结
当一个服务涉及到自己不熟悉的技术时, 一定要做到 充足
的技术调研, 但是由于不熟悉, 我们自身所理解的 充足调研
是远不充分的, 所以要有 配套的降级方案
, 当出现异常时, 可以及时止损.
每天上一当, 当当不一样! 感谢阅读!