1.架构
- Client
- 访问HBase,如put、get、scan
- 维护cache以加速访问
- Zookeeper
- HA,保证 任何时候集群只有一个active Master
- 所有Region的寻址入口
- 参与RegionServer宕机恢复:实时监控RegionServer上下线信息,通知HMaster
- 管理系统核心元数据:正常工作的RegionServer集合、保存hbase:meta表所在的RegionServer地址
- HMaster 各种管理工作
- 处理用户的管理请求:建表、修改表、权限操作、切分表、合并数据分片、Compaction等
- 管理集群中所有RegionServer:包括RegionServer中Region的负载均衡、宕机恢复以及Region迁移
- 清理过期日志和文件
- RegionServer:真正干活的进程,响应用户的IO请求
- WAL(HLog):有两大作用
- 实现数据高可靠:HBase写入数据时,先写入缓存MemStore,再异步刷新落盘,掉电易失。写入缓存前首先顺序写入HLog,HLog存储在HDFS,用于宕机恢复
- 实现主从复制
- 多个持久化级别,在高可靠行和写入性能之间进行权衡
- SKIP_WAL(只写缓存,不写HLog,生产环境不建议使用)
- ASYNC_WAL(异步写入)
- SYNC_WAL(同步写入,只是被写入文件系统,可是Flush到HDFS还有其他策略)
- FSYNC_WAL(同步写入并强制落盘,性能最差)
- USER_DEFAULT(默认使用SYNC_WAL)
- BlockCache:读缓存。基于两个原理,空间局部性(最近将读取的KV数据可能和当前读取到的KV数据地址邻近)和时间局部性(一个KV数据正在被访问,那么近期还可能被访问)。
- LRUBlockCache和BucketCache
- 缓存对象是一系列Block,一个Block默认是64K
- Region:数据表的一个分片。默认每个表开始只有一个Region,当达到阈值就会水平切分,分裂为两个Region。
- 一个Region有一个或多个store组成,一个store对应一个CF。
- 一个Store对应一个CF,有一个MemStore和多个HFile组成。
- MemStore为写缓存,数据先写入MemStore,当写满(默认128M)之后再异步flush成一个HFile文件。当文件数超过一定阈值后,会执行Compact操作,合并小文件
- WAL(HLog):有两大作用
2.写入流程
- Client发请求到RegionServer
- 根据表及rowkey在缓存中查找是否能找到RegionServer及Region,如果可以,直接发送写入请求到RegionServer
- 如果在缓存中没找到,则
- 访问zk获取hbase:meta所在的RegionServer信息
- 向hbase:meta所在的RegionServer发送查询请求,查找rowkey所在的RegionServer及Region。客户端接收结果并将结果缓存到本地。
- 向目标RegionServer发送写请求。RegionServer收到请求后,解析出Region信息,查到对应的Region对象,并将数据写入到MemStore
- Region写入:追加首先写入WAL,再写入对应Region列簇的MemStore
- 追加写入WAL
- MemStore使用ConcurrentSkipListMap来 存储KeyValue
- MemStore超过阈值flush形成HFile
3.读取流程(get 也相当于scan,故只考虑scan)
- 与写流程基本一致,但要注意,scan可能访问大量数据,所以并不是一次RPC请求就能完成读请求。rowkey范围也可能跨多个region。
- 构建scanner iterator体系。每个Store由MemStore和StoreFile构成,分别为MemStore和StoreFile构建scanner。
- 过滤掉不满足查询条件的StoreFileScanner。过滤条件有三个:TImeRange(StoreFile元数据中有TimeRange属性)、KeyRange(StoreFile中KeyValue有序,若文件[firstKey, lastKey)与[StartRow, stopRow)无交集,则过滤)、BloomFilter(StoreFile有BloomFilter相关的DataBlock,可以确定待检索rowkey是否一定不在该File)
- 每个scanner查找对应的rowkey。MemStore中查找简单,略过。StoreFile中查找较复杂,简而言之:根据StoreFile索引树查找对应的DataBlock(类似B+树)
- 该Store中所有scanner合并构建小根堆,每次pop出最小的keyvalue,保证有序。
- 获取keyvalue并进行过滤:是否已删除(key, column, cf),TimeStamp,scan设置的各种filter是否满足,是否满足查询中设定的版本
4.使用
- 创建表:如果
- schema设计:列数无上限,但列簇名、列名要短,最好单字符,节省HFile空间占用和网络传输成本。
- rowkey设计:https://hbase.apache.org/book.html#rowkey.design 总体概况为3个原则
- 加盐:给rowkey添加随机前缀。随机前缀的个数和region数相当。适用于某些热点key pattern。加盐可以提高写吞吐,但是对读不友好。需要用所有前缀+rowkey的组合查询。
- hash:rowkey组成: (hash(key) + key),给定的行有确定的前缀,这样分散了region负载,并且也使读操作能用get点查询。
- 反转:适用于rowkey实际值,前缀不均匀,后缀随机的场景如手机号、时间戳、自增id等。若常需要按scan取最近n条数据,则更要利用rowkey有序性。
- scan注意事项:filter是在hbase server提前做过滤,pageFilter有坑。由于filter只在Region内有效,当scan跨region后page就失效了。所以分页不建议使用PageFilter,而是限制(startRow, stopRow, limit as pageSize)