谨用作读《HBase不睡觉书》笔记
CAP理论 –> Base理论 –> NoSql
CAP 理论
在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容忍性),三者不可得兼,这是是NOSQL数据库的基石。
一致性(C):数据一致更新,所有数据变动都是同步的 (等同于所有节点访问同一份最新的数据副本)
可用性(A):良好的响应性能,即便部分节点出现故障 (对数据更新具备高可用性)
分区容忍性(P):可靠性,即分区相当于通信的时限要求(系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况)
任何分布式系统,只能同时满足其中两种;不需要考虑三者兼顾,更重要的是根据实际要求进行取舍
因为当前网络硬件肯定会出现数据延迟和丢包问题,因而分区容忍性是必须实现的,关键就在于C和A之间的取舍和权衡,这就有了Base理论。
Base 理论
是对CAP中C和A进行权衡后的结果,即基本可用(Basically Available)、软状态(Soft State)和最终一致性(Eventually consistent)。核心思想是,无法做到强一致性,根据业务特点,采用适当的方式来达到最终一致性。
基本可用:指分布式系统出现不可预知的故障时,允许损失部分可用性,但并非是系统不可用,可能是出现响应延迟或者分流等
软状态:允许系统中的数据存在中间状态(即允许不同节点之间的副本进行数据同步存在延时),并且该中间状态不会影响系统整体可用
最终一致性:经过一段延时后,所有副本数据同步最终一致。
因果一致性:因果一致性是指,如果进程A在更新完某个数据项后通知了进程B,那么进程B之后对该数据项的访问都应该能够获取到进程A更新后的最新值,并且如果进程B要对该数据项进行更新操作的话,务必基于进程A更新后的最新值,即不能发生丢失更新情况。与此同时,与进程A无因果关系的进程C的数据访问则没有这样的限制。
读己之所写:读己之所写是指,进程A更新一个数据项之后,它自己总是能够访问到更新过的最新值,而不会看到旧值。也就是说,对于单个数据获取者而言,其读取到的数据一定不会比自己上次写入的值旧。因此,读己之所写也可以看作是一种特殊的因果一致性。
会话一致性:会话一致性将对系统数据的访问过程框定在了一个会话当中:系统能保证在同一个有效的会话中实现“读己之所写”的一致性,也就是说,执行更新操作之后,客户端能够在同一个会话中始终读取到该数据项的最新值。
单调读一致性:单调读一致性是指如果一个进程从系统中读取出一个数据项的某个值后,那么系统对于该进程后续的任何数据访问都不应该返回更旧的值。
单调写一致性:单调写一致性是指,一个系统需要能够保证来自同一个进程的写操作被顺序地执行。
Not Only SQL:Hbase
Hbase是基于Hadoop存储的一个Key-Value列式数据库,因为key-value,Hbase响应会很快,即使数据再大;因为列式存储,字段再多,也能分散负载。同样这也导致了Hbase不可能很快,通常说的快是指,在大数据的环境下,相比较其他数据库系统,慢的不明显。
适用场景:单表数据量超过千万,并且并发也高;数据分析需求弱,不需要那么灵活;
不适用:报表分析,数据分析要求高(需要Join);单表数据量不超过千万。
LSM树和B+树
B+树:传统的机械磁盘具有快速顺序读写、慢速随机读写的特性,这对磁盘的存储结构和算法影响较大。关系型数据库为了改善访问速度,通常会对数据进行排序后存储,加快数据检索速度,因此,这也就需要数据在不断地插入、更新、删除等操作后依然有序,导致在插入的时候有大量的随机I/O。
LSM树:Log-Structured Merge Tree。核心思想是将对数据的修改增量保持在内存中,达到指定的大小限制后将这些修改操作批量写入磁盘,这也导致了读取性能会下降,需要先看是否命中内存,否则需要检索各个文件。Hbase则是提供了一个blockCache,然后才是MemStore,最后Hfile(即StoreFile)。
Hbase存储主要设计思想:
SML树原理把一棵大树拆分成N棵小树,它首先写入内存中,随着小树越来越大,内存中的小树(MemStore)会flush到磁盘中,磁盘中的树(HFile)定期可以做merge操作(Compact),合并成一棵大树(HFile),以优化读性能。
- 因为小树先写到内存中,为了防止内存数据丢失,写内存的同时需要暂时持久化到磁盘,对应了HBase的MemStore和HLog
- MemStore上的树达到一定大小之后,需要flush到HRegion磁盘中(一般是Hadoop DataNode),这样MemStore就变成了DataNode上的磁盘文件StoreFile,定期HRegionServer对DataNode的数据做merge操作,彻底删除无效空间,多棵小树在这个时机合并成大树,来增强读性能。
两种 compaction
- minor compaction:选取一些小的、相邻的StoreFile将他们合并成一个更大的StoreFile,在这个过程中不会处理已经Deleted或Expired的Cell。一次Minor Compaction的结果是更少并且更大的StoreFile。
- major compaction:将所有的StoreFile合并成一个StoreFile,在这个过程中,标记为Deleted的Cell会被删除,而那些已经Expired的Cell会被丢弃,那些已经超过最多版本数的Cell会被丢弃。一次Major Compaction的结果是一个HStore只有一个StoreFile存在。Major Compaction可以手动或自动触发,然而由于它会引起很多的IO操作而引起性能问题,因而它一般会被安排在周末、凌晨等集群比较闲的时间。
Region 定位
早期的三层查询:-ROOT- –> .META. –> Region
- 用户通过查找ZK上的 /hbase/root-region-server节点来知道-ROOT-表在哪个regionserver上;
- 访问该regionserver上的 -ROOT- 表,看看需要的数据在哪个.META. ,.META.在哪个regionserver上;
- 访问 .META. 表,看需要查询的rowkey在哪个region上以及该region在哪个regionserver上;
- 访问目标regionserver,读取region中数据;
- 缓冲部分 .META. 表信息,以备下次查询。
三层模型,极大地扩展了region的数量限制。但是,完全不需要那么多region,并且-ROOT-一直以来都是一行数据,形同虚设。同时增加了代码复杂度。
现在的两层模型: .META. –> Region
- 用户通过查找ZK上的 /hbase/root-region-server节点来知道 .META. 表在哪个regionserver上;
- 访问该regionserver上的 .META. 表,看看需要的数据在哪个region上以及该region在哪个regionserver上;
- 访问目标regionserver,读取region中数据;
- 缓冲 .META. 表信息,下次访问不需要再加载.META. 信息了。