HBase的数据存储方式对于实现自己的数据库产品有借鉴和指导意义,因此总结一下,以作备忘。
HBase在文件系统中存储数据,而为了使系统具备良好的扩展性和可用性,一般使用HDFS作为该文件系统。
HBase的基本存储单元是Key-Value对,其中Key由行键(Row Key)、列族名称、列修饰符、版本号构成。整个HBase存储系统可以认为是维护了一个大型的Key-Value对集合。理所当然地,我们认为HBase中的基本操作是针对Key-Value对的PUT、GET、DELETE操作。HBase确实支持这些基本操作。但是,在实际的应用中,使用这些基本操作通常不是频繁用例,而更多的用例是以行(Row)为单位进行的PUT、GET、DELETE、SCAN操作。为此,HBase自动完成将一行拆分成多个Key-Value对或将属于同一行的各个Key-Value对封装成一行的工作。
HBase维护的各个Key-Value对的集合总是按Key有序的,这种集合包括磁盘上的StoreFile和内存中的MemStore。MemStore存在于内存,且其中的Key-Value对是动态插入。通过实施按序插入可以保证MemStore总是有序。StoreFile虽然存在于磁盘,但是它们是只读的,在某次需要将MemStore写到磁盘时被创建,以最优B+树(由于数据是现成的,因此可以做到最优)的数据组织方式存储了写到磁盘的MemStore,因此也总是有序的。
对HBase状态的更新总是通过向MemStore中添加新的Key-Value对进行,当然在MemStore满或其他Flush事件出现时,需要将MemStore写到磁盘上的StoreFile中。若是PUT一个Key-Value对,可以将该Key-Value对直接插入MemStore;若是DELETE一个Key-Value对,需要首先为该Key-Value对添加一个指明用于删除的标记,然后将其插入MemStore。由于MemStore是有序的,所有具有相同Key的Key-Value对在MemStore中聚集到一起,因此能够顺序读取。当需要GET具有指定Key的Key-Value对时,所有具有这一指定Key的Key-Value对被读取出来,这不仅需要读取MemStore,在存在StoreFile时可能还需要读取各个StoreFile,然后从读取结果中剔除被删除的版本和GET操作不需要的版本,将剩余的Key-Value对返回。
某个Region的某个列族可能有许多StoreFile,通过在每个StoreFile的头信息中指明在本StoreFile中存储的Key-Value对的最小Key和最大Key,使得对于那些Key值不在最小Key和最大Key范围内的GET操作,可以直接跳过该StoreFile。
HBase还使用了一种称为Bloom Filter的StoreFile过滤技术。如果某个Region的某个列族开启了Bloom Filter,HBase为存储在StoreFile中的每个Key-Value对的Key使用散列函数生成一个散列码,将所有散列码去重后存储在StoreFile中。对于某个GET操作,首先检查其Key的散列码是否在StoreFile中出现,若没有出现,则可以确定在StoreFile中不存在要GET的Key,因此直接跳过StoreFile,否则,在StoreFile中可能存在也可能不存在要GET的Key,需要扫描StoreFile。由于Key-Value对的Key位数较多,而散列码的位数可以选取得很少(例如8位),因此存储散列码比存储Key更节省存储空间,在检索时更高效。
每个StoreFile包含多个数据块,每个数据块存储多个Key-Value对。对于那些Key值在StoreFile的最小Key和最大Key范围内的GET操作,HBase在StoreFile内使用索引块指明在每个数据块中存储的Key-Value对的最小Key和最大Key,使得GET操作可以直接定位要扫描的数据块。而每个数据块是有序的,在扫描数据块时可以执行二分查找算法。