1、分布式与并行处理
分布式系统
通常,我们说分布式系统的时候,我们都会想到Dubbo框架和SpringCloud框架。这两个框架现在应该是国内用的比较多的两个分布式框架了,特点都是很容易把服务部署在多台机器组成一个高可用的服务集群来应对高并发。所以,我们通常认为分布式系统就是多台机器组成一个集群对外提供服务,每个请求也会被分配到集群中的一台或者多台机子上完成,而用户是感觉不同整个系统封装的细节。
多线程和并行处理
我们都知道多线程是怎样的一个概念,它就是一个并行处理的例子,它是在一台计算机并行的。但是,在我们的认识范畴中,我们所知的并行是应该同时在运作的,但是,多线程的概念是,多个线程在抢占资源,抢到了资源的线程会运作,其它的线程在等待,所以并不是真正意义上的并行,只是因为计算机的运行速度比较快,所以我们可以认为它是并行处理的。
Hadoop的HDFS文件系统,Hbase等分布式系统就是并行处理的,多台机器同时运行,同时存储和读取数据,最后把结果返回给用户。
2、什么是热点和数据倾斜
热点发生在大量的client直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)。
大量访问会使热点region所在的单个机器超出自身承受能力,引起性能下降甚至region不可用,这也会影响同一个RegionServer上的其他region,由于主机无法服务其他region的请求,造成资源浪费。设计良好的数据访问模式以使集群被充分,均衡的利用。
数据倾斜:
Hbase可以被划分为多个Region,但是默认创建时只有一个Region分布在集群的一个节点上,数据一开始时都集中在这个Region,也就是集中在这一个节点上,就算region存储达到临界值时被划分,数据也是存储在少数节点上。这就是数据倾斜。
3、hbase的存储方式引起的热点问题和数据倾斜
HBase中的行是按照rowkey的字典顺序排序的,这种设计优化了scan操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于scan。
rowkey设计是热点的源头。
HBase中,表会被划分为1...n个Region,被托管在RegionServer中。Region有二个重要的属性:
StartKey与EndKey
表示这个Region维护的rowKey范围,当我们要读/写数据时,如果rowKey落在某个start-end key范围内,那么就会定位到目标region并且读/写到相关的数据。
默认的情况下,创建一张表是,只有1个region,
start-end key没有边界,所有数据都在这个region里装,然而,当数据越来越多,region的size越来越大时,大到一定的阀值,hbase认为再往这个region里塞数据已经不合适了,就会找到一个midKey将region一分为二,成为2个region,这个过程称为分裂(region-split)。而midKey则为这二个region的临界(这个中间值这里不作讨论是如何被选取的)。
此时,我们假设假设rowkey小于midKey则为阴被塞到1区,大于等于midKey则会被塞到2区,如果
rowkey还是顺序增大
的,那数据就总会往2区里面写数据,而1区现在处于一个被冷落的状态,而且是半满的。2区的数据满了会被再次分裂成2个区,如此不断产生被冷落而且不满的Region,当然,这些region有提供数据查询的功能。
这种设计是分布式系统一个很大的弊端,而且这样导致数据倾斜和热点问题,从而导致集群的资源得不到很好的利用。
3、预分区和rowkey的散列设计——解决数据倾斜和热点问题
预分区
预分区,让表的数据可以均衡的分散在集群中,而不是默认只有一个region分布在集群的一个节点上。
(预分区个数=节点的倍数,看数据量估算,region不足了会被分列,预分区后每个region的rowkey还是有序的)
一个RegionServer能管理10-1000个Region,0.92.x版本后,默认的Region大小为10G,向下可以支持256MB,向上可以支持到20G,也就是说,
每个RegionServer能管理的数据量为2.5GB-20TB
。
如果有5个节点,3年内数据量为5T,那么分区数可以预设为:
5000G/10G=500个region
这500个Region就会被均衡的分布在集群各个节点上(具体分布看机器的性能和存储空间而定),机器硬盘不足可以添加硬盘,性能不足可以添加新节点(添加新机器)。
Rowkey长度原则(最好不超过16字节)
Rowkey是一个二进制码流,Rowkey的长度被很多开发者建议说设计在10~100个字节,不过建议是越短越好,不要超过16个字节。
原因如下:
(1)数据的持久化文件HFile中是按照KeyValue存储的,如果Rowkey过长比如100个字节,1000万列数据光Rowkey就要占用100*1000万=10亿个字节,将近1G数据,这会极大影响HFile的存储效率;
(2)MemStore将缓存部分数据到内存,如果Rowkey字段过长内存的有效利用率会降低,系统将无法缓存更多的数据,这会降低检索效率。因此Rowkey的字节长度越短越好。
(3)目前操作系统是都是64位系统,内存8字节对齐。控制在16个字节,8字节的整数倍利用操作系统的最佳特性。
rowkey散列原则
把主键哈希后当成rowkey的头部
rowkey唯一原则
必须在设计上保证其唯一性,rowkey是按照字典顺序排序存储的,因此,设计rowkey的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。
时间戳反转
如果数据需要保留多个版本,可以使用反转的时间戳作为rowkey的一部分,用 Long.Max_Value - timestamp 追加到key的末尾,例如 [key][reverse_timestamp] , [key] 的最新值可以通过scan [key]获得[key]的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。
整个rowkey(timestamp并不是必要的,视业务而定)
rowkey=哈希(主键<递增的id\手机号码等>)+Long.Max_Value - timestamp
4、预分区splitkeys选取
(1)取样,先随机生成一定数量的rowkey(10万、100万),将取样数据按升序排序放到一个集合里。
(2)根据预分区的region个数,对整个集合平均分割,即是相关的splitkeys。
(3)HBaseAdmin.createTable(HTableDescriptor tableDescriptor,byte[][] splitkeys)可以指定预分区的splitkey,即指定region间的rowkey临界值。
5、
Column Family列族的设计数量不宜过多(建议不设置多个)
(1)这里必须先知道Hbase的架构设计
HBase的表是由一到多个Region组成的;
Region是由一到多个HStore组成的,HStore对应列族,也就是表中有多个CF,就会有多个个HStore;而分列的时候是根据Region的大小切分的。
(2)现在已经知道必须要先做预分区和key的散列了,那么,假设表中有多个列族,也就是多个CF,对应也就有多个HStore,而此时,假设多个列族的数据分配不均衡就会出现下面情况:
如果某个Region里面的A HStore有1000万条数据,而B HStore里面只有100条数据。那么,这100条数据会被分到多个region中,读取B HStore的数据时,跨了多个region,导致查询效率降低。
(3)Hbase的列族设计是为了加快读取速度的,同一个表的数据,按列族把数据划分后,数据查询时能缩小数据的范围(查询数据时指定列族),查询效率会加快,然而,如果数据分配不均衡就会导致效率降低,所以并不建议多个列族,可以建多个表,数据量小的表Region数量也可以设置小一点。
6、一对多设计和宽表
假设,现在有用户表和银行卡表,一个用户对应多张银行卡
传统的关系型数据(RMDB),我们会设计成两张表,通过关联查询获取数据;
如果Hbase也设计成两张表,那么如果想获取用户和银行卡的数据,就得查询两次才能获取到数据。
如果设计成一张宽表,把用户数据放到银行卡的表上,也就是用户的数据被存放了多次,但是获取数据的时候只需要查询一次就能把用户和用户银行卡的数据查询出来。
宽表的缺点:浪费存储空间,如果修改用户数据,那么是覆盖多条数据,操作繁琐,但是并不影响性能。
宽表的优点:查询效率提高。
两种设计都有优点和缺点,浪费性能还是浪费存储空间,这需要视具体情况而定,需要作出取舍。
源码: https://gitee.com/boat824109722/hbase-api-demo