如何解决HBase顺序读和热点写的矛盾问题

近日,采集组同事遇到一个难题,下面简单描述一下这个问题和我的建议。

问题描述

采集组的同学负责抓取各大商城的商品信息,并把URL链接存储起来,因为URL规模很大,所以采用Mysql分库分表的方式进行打散存储。

调度程序会周期性的从Mysql抽取链接并发往Kafka排队抓取,并且对URL进行了一定条件的where过滤。

抓取过程中可能对URL属性进行更新或者发现新的URL,那么都需要插入到Mysql中。

目前主要瓶颈是Mysql的读取效率太低,随着数据规模的膨胀,完整扫描一遍URL的耗时已经无法接受,因此寻求一个更好的解决方案。

我的建议

这个场景很适合Hbase,即需要高吞吐的读取效率,同时要保障高吞吐的写入效率。

改进读效率

调度程序对URL的抽取是带有条件的,但是好在条件固定,因此可以将筛选条件作为RowKey的前缀部分,这样Hbase就会连续存储相同前缀的数据,此后通过Scan操作可以高效的批读取,将达到非常高的吞吐效率。

比如,此前Mysql的筛选条件是where mall_id=1,那么我们就可以把RowKey设计为:

mall_id+url

这样Scan的时候传入mall_id的前缀,即可顺序扫描得到所有其下的所有URL。

新的困难

虽然读效率提升了,但是根据HBase的特性,写入将会遇到严重的性能问题,这是为什么呢?

我们知道Hbase是全局有序存储的,整个数据被分为若干个Region,所有Region首尾相连覆盖了整个Hbase的key range。

当我们Scan的某个时刻,实际数据都是从同1个region扫描出来的,因为URL会被投入Kafka进而被爬虫抓取,那么其实抓取结果也是基本顺序送回的,必然会写入到同1个region,导致热点写入问题。

这么说,顺序读似乎必然会导致顺序写,难道问题无解了么?

显然,我们稍微动动脑筋就会有解决方案。

Mysql解决写入性能的方法是分库分表,而Hbase却使用单表,难道Mysql就不是热点写了么?似乎哪里不太对?

兼顾读写效率

实际上,调度程序的要求只是扫描某个mall_id下面的所有URL,而对URL之间的顺序没有严格要求,因此我们对Hbase同样应该做分表处理,从而实现写入的打散。

最简单的方法就是根据原先的rowkey(mall_id+url),计算一个整数hash值并取模128,那么就可以得到128个分区ID,那么真正最后的rowkey就是:

分区ID+mall_id+url

仅仅这样并不解决问题,我们的调度程序扫描时需要指定”分区ID”,那么同一个”分区ID”的URL又会被集中抓取,那么又会导致集中写入。

显然,分区是必要条件,不是充分条件,我们需要对读取策略做对应的调整才能实现读写兼备的效果。

因此,调度程序要做的是同时开启128个Scanner,轮转着从128个分区反复扫描URL,确保从调度发出的URL其分区已经打散,这样写入才可以真正的被打散。

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~