关于 hbase 分页的一点思考
hbase 是一款出色的 nosql 模型,最近有个业务数据量太大,好几亿,分库分表的话虽然业内已有成熟的方案,但是会带来一定运维成本,且业务需要关心 shard 的维度,考虑了 hbase 方案,如果hbase 各项测试正常,会直接把存储切到 hbase,其中排序+分页问题在 hbase 实现需要一些技巧,在网上也看了很多方案,所以特地将 hbase 分页有关的一些理解记录一下。
hbase 数据获取方式
- 直接根据 rowkey 查找,速度最快
- scan,指定 startrowkey、endrowkey 和 limit获取数据,在 rowkey 设计良好的情况下,效率也不错
- 全表扫,强烈不推荐这种做法,效率极差,在线业务不用考虑这种方式
hbase 数据排序怎么做?
我觉得这个分两种情况,一是数据量比较少,业务上每次拉取所有的数据,可以在客户端做排序,二是数据比较多,需要分页,这种情况下客户端做显然不合适,因为要从服务器拉取所有数据,排序完成,获取某一页,剩余的数据全都不用,资源损耗比较严重,比较推荐做法是充分利用 hbase rowkey 的特性,数据是按照 rowkey 字典序排列的,如果排序字段是不变的,可以把排序字段加到 rowkey 里,这样吐出的数据自然就是有序的。
如何排序的字段是可以变的呢?
假设业务上有个查询,排序的字段是可以变得,这样放到 rowkey 里就不合适了,因为排序字段变,意味着 rowkey 也会变,不是推荐的做法。这时候考虑的一种实现就是使用 redis 维护一个zset 索引,score 是排序字段,value 是对应记录的 rowkey,每次排序的字段变了,就去更新 zset 对应的数据。查询的话就相当于先去 redis 查出 rowkey 列表,然后根据 rowkey 列表去 hbase 批量查。
产品分页的方式?
常见的两种方式,一种是更多这种按钮,不支持跳转到某一页,一种是可以选择某个特定的页进行跳转。这两种方式 hbase 在实现上会有区别,下面会分别介绍下。
更多方式的分页
这种分页不支持跳转到某一页,只能不断地下一页下一页,使用 hbase 可以以一种比较简单的方式实现。由服务端告诉 app 端或者 web 端下一页请求的参数,假设某一页获取20条数据,服务端去获取21条,第21条数据的 rowkey 就是下一次扫描的 startrowkey,把它加到返回给 web 或者 app 的参数里,这样就可以实现分页。有人可能会说,假设下一页的 startrowkey 返回给前端之后,这时候有新的数据插入,不是会有问题吗?这种情况其实还好,首先互联网应用大多是读多写少,你浏览某个列表时,列表内容更新的概率本来就小,就算真的发生,数据会按照排序方式插入到列表首,你不刷新首屏内容,仅仅也就是新加的内容没展现出来,不影响其他内容的展示,而只要你一刷新首屏,新的内容就出来了。
直接跳转到某一页
假设分页可以直接跳转到某一页呢?这个用 hbase 实现确认比较尴尬,hbase scan 扫描的时候本来就是根据 rowkey 范围和 limit 扫描,想到的实现方式依旧是 zset,score 排序字段,value 是 rowkey。也不一定非用 redis,反正就是要有一个地方维护查询索引。去 hbase 直接根据 rowkey 查询。
redis 内存容量如何估?
用 zset 的话,怎么估计内存容量呢?个人是使用 info memory 查看当前内存使用情况,创建一个 zset,看看占用多少空间,再加入一个 key-value,看看占用多少,其实就是先算一下 zset 结构本身的维护需要多少空间,加入一个键值对又需要多少空间,然后根据你要保存的量看一下需要多少空间,是否可以接受。一些压缩参数之类的需要和生产环境一致,来保证估算的准确性。