RegionService内部架构图,一个RegionServer包含一个BlockCache、一个或多个WAL(默认只有1个,在HBase1.0可以开启MultiWAL功能,开始支持多个WAL。参见HBASE-5699)和多个Region。
预写日志(Write-ahead log,WAL)也称为HLog,WAL在Hbase中有两个核心作用。
默认情况下,所有写入操作(写入、更新以及删除)的数据都先以追加的形式写入WAL,再写入MemStore。
WAL文件被创建出来后会放在/hbase/WALs下(这该文件夹在HDFS上。在0.94版本之前WALs目录的名称为.log),一旦WAL文件过期,则会被移动到/hbase/oldWALs下(该文件夹在HDFS上。在0.94版本之前oldWALs目录的名称为.oldlogs)。
hbase/WALs目录下通常会有多个子目录,每个子目录代表一个对应的RegionServer。
示例:
/hbase/WALs/host1,16020,1585535973311
host1表示对应的RegionServer域名,16020为端口号;1585535973311为目录生成时的时间戳。
每个子目录下存储该RegionServer内的所有WAL文件。
示例:
/hbase/WALs/host1,16020,1585535973311/host1%2C16020%2C1585535973311.1587237184832
可以通过hbase wal命令查看具体WAL文件中的记录内容。
WAL文件生成之后并不会永久存储在系统中,它的使命完成后,文件就会失效最终被删除。整个WAL生命周期如下图所示:
WAL生命周期包含4个阶段:
1)WAL构建:HBase的任何写入(更新、删除)操作都会先将记录追加写入到WAL文件中。
2)WAL滚动:HBase后台启动一个线程,每隔一段时间(由参数'hbase.regionserver.logroll.period'决定,默认1小时)进行日志滚动。日志滚动会新建一个新的日志文件,接收新的日志数据。日志滚动机制主要是为了方便过期日志数据能够以文件的形式直接删除。
3)WAL失效:写入数据一旦从MemStore中落盘,对应的日志数据就会失效。为了方便处理,HBase中日志失效删除总是以文件为单位执行。查看某个WAL文件是否失效只需确认该WAL文件中所有日志记录对应的数据是否已经完成落盘,如果日志中所有日志记录已经落盘,则可以认为该日志文件失效。一旦日志文件失效,就会从/hbase/WALs文件夹移动到/hbase/oldWALs文件夹。注意此时WAL并没有被系统删除。
4)WAL删除:Master后台会启动一个线程,每隔一段时间(参数'hbase.master.cleaner.interval',默认1分钟)检查一次文件夹oldWALs下的所有失效日志文件,确认是否可以删除,确认可以删除之后执行删除操作。确认条件主要有两个:
HBase定义了多个WAL持久化等级,使得用户在数据高可靠和写入性能之间进行权衡。通过设置WAL的持久化等级决定是否开启WAL机制以及WAL的落盘方式。WAL的持久化等级分为如下五个等级。
可以选择通过调用下面语句来设置WAL持久化等级。Put、Append、Increment、Delete都是Mutation的子类,所以它们都有setDurability方法。
Mutation.setDurability(Durability.SKIP_WAL);
当进行改动时,比如Put、Delete、Append来到Region的时候会先放在内存中,最后flush到HFile中。但是这些改动在放入region中的Memstore之前就被立刻写入了WAL中。写入的方式是调用HDFS客户端来写入HDFS,也就是说即使只有一个改动,也会调用HDFS接口来同步(sync)WAL数据。
如果不想完全关闭WAL,又不想每次改动都写入WAL,可以选择采用异步的方式来同步WAL。设定方式还是调用setDurability()方法:
Mutation.setDurability(Durability.ASYNC_WAL);
这样设定后Region会等到条件满足的时候才把操作写入WAL。这里提到的条件主要指的是时间间隔
hbase.regionserver.optionallogflushinterval,默认值是1s。这个时间间隔的意思是HBase间隔多久会把操作从内存写入WAL。
如果使用异步的方式写入WAL的时候出错了怎么办?
出错了是没有任何事务保证的,写入WAL的数据即使写入成功了,如果失败的话也会丢失。如果系统对性能要求极高、对数据一致性要求不高,并且系统的性能瓶颈出现在WAL上的时候,可以考虑使用异步写入WAL。否则,使用默认的配置即可。
WAL一定是一个环状的滚动日志结构,因为这种结构写入效果最高,而且可以保证空间不会持续变大。
WAL的检查间隔由hbase.regionserver.logroll.period定义,默认值为1小时。检查的内容是把当前WAL中的操作跟实际持久化到HDFS上的操作比较,看哪些操作已经被持久化了,被持久化的操作就会被移动到/hbase/oldWALs文件夹内。
一个WAL实例包含有多个WAL文件。WAL文件的最大数量通过hbase.regionserver.maxlogs(默认是32)参数来定义,这个值的大小要怎么定义涉及性能调优,参见WAL的优化。
触发滚动的条件:
- 一个WAL实例包含的WAL文件数量超过了阀值限定。
- 当WAL文件所在的块(Block)快要满了。
- 当WAL所占的空间大于或者等于某个阀值,该阀值的计算公式是:
hbase.regionserver.hlog.blocksize * hbase.regionserver.logroll.multiplier
hbase.regionserver.hlog.blocksize是标定存储系统的块(Block)大小的,如果是基于HDFS的,那么只需要把这个值设定成HDFS的块大小即可。
实际上,如果不设定hbase.regionserver.hlog.blocksize,HBase还是会自己去尝试获取这个参数的值。不过,还是建议设定该值。
hbase.regionserver.logroll.multiplier是一个百分比,默认设定成0.95,意思是95%,如果WAL文件所占的空间大于或者等于95%的块大小,则这个WAL文件就会被归档到oldWALs文件夹。
- WAL是存储在HDFS上的,Memstore是存储在内存中的,HFile又是存储在HDFS上的。
- 数据是先写入WAL,再被放入MemStore,当MemStore的大小增加到超过一定的阈值的时候会被持久化到HFile中。
疑问:数据在进入HFile文件之前已经被存储到HDFS一次了,为什么还需要被放入到MemStore?
这是因为HDFS上的文件只能创建、追加和删除,但是不能修改。对于一个数据库来说,按顺序的存放数据非常重要,这是性能的保障,所以不能按照数据到来的顺序写入磁盘。因此需要使用内存先把数据整理成顺序存放,然后再一起写入磁盘,这就是MemStore存在的意义。
由于数据在写入MemStore之前要先被写入WAL,所以增加MemStore的大小并不能加速写入速度。
MemStore存在的意义是维持数据按照rowkey顺序排列,而不是做一个缓存。
- 由于HDFS上的文件不可修改,为了让数据顺序存储从而提高读取效率,Hbase使用了LSM树结构来存储数据。数据会先在MemStore中整理成LSM树,最后在刷写到Hfile上。
- 优化数据的存储。比如一个数据添加后马上就删除了,这样在刷写的时候就可以直接不把这个数据写到HDFS上。
早期的HDFS上的所有文件都是只能写入不能修改的。后来加入了追加append特性,实现了数据可以顺序增加,但还是不能修改之前的数据。而Hbase是一个随机读写的数据库。MemStore会在数据最终刷写到HDFS之前对文件进行排序处理,这样随机写入的数据就变成了顺序存储的数据,可以提高读取效率。
每一次的刷写都会产生一个全新的Hfile文件。由于HDFS的特性,这个文件不可修改。
HFile 是数据存储的实际载体,创建的所有表、列等数据存储在 HFile 中。
在很多资料中经常会看到管 HFile 叫 StoreFile。其实叫 HFile 或者 StoreFile 都没错,HBase 是基于 Java 编写的,那么所有物理上的东西都有一个对象跟它对应,在物理存储上管 MemStore 刷写而成的文件叫 HFile,StoreFile 就是 HFile 的抽象类而已。
可以将Hfile看成是由一个一个的块组成的。在Hbase中一个块的大小默认为64kb,由列族上的BOLCKSIZE属性定义
- Data:数据块(可选),每个Hfile文件有多个Data块,Hbase表中的数据就存储在这里。
- Meta:元数据块(可选),Meta块只有在文件关闭的时候次才会写入,Meta块存储了该Hfile文件的元数据信息。
- FileInfo:文件信息(必选),是Hfile文件的必要组成部分。只有在文件关闭的时候写入,存储的是这个文件的信息,比如最后一个key(LastKey),平均的key(Avg Key Len)长度
- DataIndex(可选):存储Data块索引信息(Data块的偏移值)的块文件,只有Data块才有DataIndex。
- MetaIndex(可选):存储Meta块索引信息的块文件。只有Meta块才有MetaIndex。
- Trailer(必选):存储了FileInfo、DataIndex、MetaIndex块的偏移值。
《Hbase不睡觉书》笔记