1、 HBase的特点是什么?
1)大:一个表可以有数十亿行,上百万列
2)无模式:每行都有一个可排序的主键和任意多的列,列可以根据需要动态的增加,同一张表中不同的行可以有截然不同的列
3)面向列:面向列(族)的存储和权限控制,列(族)独立检索
4)稀疏:空(null)列并不占用存储空间,表可以设计的非常稀疏
5)数据多版本:每个单元中的数据可以有多个版本,默认情况下版本号自动分配,是单元格插入时的时间戳
6)数据类型单一:Hbase中的数据都是字符串,没有类型
2、HBase和Hive的区别?
2.1、两者是什么?
1)Apache Hive是一个构建在Hadoop基础设施之上的数据仓库。通过Hive可以使用HQL语言查询存放在HDFS上的数据。HQL是一种类SQL语言,这种语言最终被转化为Map/Reduce. 虽然Hive提供了SQL查询功能,但是Hive不能够进行交互查询--因为它只能够在Haoop上批量的执行Hadoop
Apache HBase是一种Key/Value系统,它运行在HDFS之上。和Hive不一样,Hbase的能够在它的数据库上实时运行,而不是运行MapReduce任务。Hbase被分区为表格,表格又被进一步分割为列簇。列簇必须使用schema定义,列簇将某一类型列集合起来(列不要求schema定义)。例如,“message”列簇可能包含:“to”, ”from” “date”, “subject”, 和”body”. 每一个 key/value对在Hbase中被定义为一个cell,每一个cell由row-key,列簇、列和时间戳。在Hbase中,行是key/value映射的集合,这个映射通过row-key来唯一标识。Hbase利用Hadoop的基础设施,可以利用通用的设备进行水平的扩展’
2.2、两者的特点
Hive帮助熟悉SQL的人运行MapReduce任务。因为它是JDBC兼容的,同时,它也能够和现存的SQL工具整合在一起。运行Hive查询会花费很长时间,因为它会默认遍历表中所有的数据。虽然有这样的缺点,一次遍历的数据量可以通过Hive的分区机制来控制。分区允许在数据集上运行过滤查询,这些数据集存储在不同的文件夹内,查询的时候只遍历指定文件夹(分区)中的数据。这种机制可以用来,例如,只处理在某一个时间范围内的文件,只要这些文件名中包括了时间格式
HBase通过存储key/value来工作。它支持四种主要的操作:增加或者更新行,查看一个范围内的cell,获取指定的行,删除指定的行、列或者是列的版本。版本信息用来获取历史数据(每一行的历史数据可以被删除,然后通过Hbase compactions就可以释放出空间)。虽然HBase包括表格,但是schema仅仅被表格和列簇所要求,列不需要schema。Hbase的表格包括增加/计数功能
2.3、限制
Hive目前不支持更新操作。另外,由于hive在hadoop上运行批量操作,它需要花费很长的时间,通常是几分钟到几个小时才可以获取到查询的结果。Hive必须提供预先定义好的schema将文件和目录映射到列,并且Hive与ACID不兼容
HBase查询是通过特定的语言来编写的,这种语言需要重新学习。类SQL的功能可以通过Apache Phonenix实现,但这是以必须提供schema为代价的。另外,Hbase也并不是兼容所有的ACID特性,虽然它支持某些特性。最后但不是最重要的--为了运行Hbase,Zookeeper是必须的,zookeeper是一个用来进行分布式协调的服务,这些服务包括配置服务,维护元信息和命名空间服务
2.4、应用场景
Hive适合用来对一段时间内的数据进行分析查询,例如,用来计算趋势或者网站的日志。Hive不应该用来进行实时的查询。因为它需要很长时间才可以返回结果
Hbase非常适合用来进行大数据的实时查询。Facebook用Hbase进行消息和实时的分析。它也可以用来统计Facebook的连接数
2.5、总结
Hive和Hbase是两种基于Hadoop的不同技术--Hive是一种类SQL的引擎,并且运行MapReduce任务,Hbase是一种在Hadoop之上的NoSQL 的Key/vale数据库。当然,这两种工具是可以同时使用的。就像用Google来搜索,用FaceBook进行社交一样,Hive可以用来进行统计查询,HBase可以用来进行实时查询,数据也可以从Hive写到Hbase,设置再从Hbase写回Hive
3、HBase适用于怎样的情景?
3.1、半结构化或非结构化数据
对于数据结构字段不够确定或杂乱无章很难按一个概念去进行抽取的数据适合用HBase。以上面的例子为例,当业务发展需要存储author的email,phone,address信息时RDBMS需要停机维护,而HBase支持动态增加
3.2、记录非常稀疏
RDBMS的行有多少列是固定的,为null的列浪费了存储空间。而如上文提到的,HBase为null的Column不会被存储,这样既节省了空间又提高了读性能
3.3、多版本数据
如上文提到的根据Row key和Column key定位到的Value可以有任意数量的版本值,因此对于需要存储变动历史记录的数据,用HBase就非常方便了。比如上例中的author的Address是会变动的,业务上一般只需要最新的值,但有时可能需要查询到历史值
3.4、超大数据量
当数据量越来越大,RDBMS数据库撑不住了,就出现了读写分离策略,通过一个Master专门负责写操作,多个Slave负责读操作,服务器成本倍增。随着压力增加,Master撑不住了,这时就要分库了,把关联不大的数据分开部署,一些join查询不能用了,需要借助中间层。随着数据量的进一步增加,一个表的记录越来越大,查询就变得很慢,于是又得搞分表,比如按ID取模分成多个表以减少单个表的记录数。经历过这些事的人都知道过程是多么的折腾。采用HBase就简单了,只需要加机器即可,HBase会自动水平切分扩展,跟Hadoop的无缝集成保障了其数据可靠性(HDFS)和海量数据分析的高性能(MapReduce)
4、rowKey的设计原则?
1)Rowkey长度原则
Rowkey 是一个二进制码流,Rowkey 的长度被很多开发者建议说设计在10~100 个字节,不过建议是越短越好,不要超过16 个字节
原因如下:
数据的持久化文件HFile 中是按照KeyValue 存储的,如果Rowkey 过长比如100 个字节,1000 万列数据光Rowkey 就要占用100*1000 万=10 亿个字节,将近1G 数据,这会极大影响HFile 的存储效率
MemStore 将缓存部分数据到内存,如果Rowkey 字段过长内存的有效利用率会降低,系统将无法缓存更多的数据,这会降低检索效率。因此Rowkey 的字节长度越短越好
目前操作系统是都是64 位系统,内存8 字节对齐。控制在16 个字节,8 字节的整数倍利用操作系统的最佳特性
2)Rowkey散列原则
如果Rowkey 是按时间戳的方式递增,不要将时间放在二进制码的前面,建议将Rowkey的高位作为散列字段,由程序循环生成,低位放时间字段,这样将提高数据均衡分布在每个Regionserver 实现负载均衡的几率。如果没有散列字段,首字段直接是时间信息将产生所有新数据都在一个 RegionServer 上堆积的热点现象,这样在做数据检索的时候负载将会集中在个别RegionServer,降低查询效率
3)Rowkey唯一原则
必须在设计上保证其唯一性
5、scan和get的异同?
HBase的查询实现只提供两种方式:
1)按指定RowKey 获取唯一一条记录,get方法(org.apache.hadoop.hbase.client.Get)
Get 的方法处理分两种 : 设置了ClosestRowBefore 和没有设置ClosestRowBefore的rowlock。主要是用来保证行的事务性,即每个get 是以一个row 来标记的。一个row中可以有很多family 和column。
2)按指定的条件获取一批记录,scan方法(org.apache.Hadoop.hbase.client.Scan)实现条件查询功能使用的就是scan 方式。
scan 可以通过setCaching 与setBatch 方法提高速度(以空间换时间);
scan 可以通过setStartRow 与setEndRow 来限定范围([start,end)start 是闭区间,end 是开区间)。范围越小,性能越高。
scan 可以通过setFilter 方法添加过滤器,这也是分页、多条件查询的基础
6、setCache和setBatch方法的使用?
setCache用于设置缓存,即设置一次RPC请求可以获取多行数据。对于缓存操作,如果行的数据量非常大,多行数据有可能超过客户端进程的内存容量,由此引入批量处理这一解决方案
setBatch 用于设置批量处理,批量可以让用户选择每一次ResultScanner实例的next操作要取回多少列,例如,在扫描中设置setBatch(5),则一次next()返回的Result实例会包括5列。如果一行包括的列数超过了批量中设置的值,则可以将这一行分片,每次next操作返回一片,当一行的列数不能被批量中设置的值整除时,最后一次返回的Result实例会包含比较少的列,如,一行17列,batch设置为5,则一共返回4个Result实例,这4个实例中包括的列数分别为5、5、5、2
组合使用扫描器缓存和批量大小,可以让用户方便地控制扫描一个范围内的行键所需要的RPC调用次数。Cache设置了服务器一次返回的行数,而Batch设置了服务器一次返回的列数
假如我们建立了一张有两个列族的表,添加了10行数据,每个行的每个列族下有10列,这意味着整个表一共有200列(或单元格,因为每个列只有一个版本),其中每行有20列
1)Batch参数决定了一行数据分为几个Result,它只针对一行数据,Batch再大,也只能将一行的数据放入一个Result中。所以当一行数据有10列,而Batch为100时,也只能将一行的所有列都放入一个Result,不会混合其他行
2) 缓存值决定一次RPC返回几个Result,根据Batch划分的Result个数除以缓存个数可以得到RPC消息个数(之前定义缓存值决定一次返回的行数,这是不准确的,准确来说是决定一次RPC返回的Result个数,由于在引入Batch之前,一行封装为一个Result,因此定义缓存值决定一次返回的行数,但引入Batch后,更准确的说法是缓存值决定了一次RPC返回的Result个数)
RPC请求次数 = (行数 * 每行列数) / Min(每行的列数,批量大小) / 扫描器缓存
缓存和批量两个参数如何联动,下图中有一个包含9行数据的表,每行都包含一些列。使用了一个缓存为6、批量大小为3的扫描器,需要三次RPC请求来传送数据:
7、HBase中一个cell的结构?
HBase中通过row和columns确定的为一个存贮单元称为cell
Cell:由{row key, column(= + ), version}唯一确定的单元。cell 中的数据是没有类型的,全部是字节码形式存贮
8、简述HBase中compact用途
compact是什么,什么时候触发,分为哪两种,有什么区别,有哪些相关配置参数?在hbase中每当有memstore数据flush到磁盘之后,就形成一个storefile,当storeFile的数量达到一定程度后,就需要将 storefile 文件来进行 compaction 操作
Compact 的作用:
1)合并文件
2)清除过期,多余版本的数据
3)提高读写数据的效率
HBase 中实现了两种 compaction 的方式:minor and major. 这两种 compaction 方式的区别是:
1)Minor 操作只用来做部分文件的合并操作,不做任何删除数据、多版本数据的清理工作
2)Major 操作是对 Region 下的HStore下的所有StoreFile执行合并操作,最终的结果是整理合并出一个文件
9、每天百亿数据存入HBase,如何解决
如何保证数据的存储正确和在规定的时间里全部录入完毕,不残留数据?
需求分析:
1)百亿数据:证明数据量非常大
2)存入HBase:证明是跟HBase的写入数据有关
3)保证数据的正确:要设计正确的数据结构保证正确性
4)在规定时间内完成:对存入速度是有要求的
解决思路:
1)数据量百亿条,什么概念呢?假设一整天60 x 60 x 24 = 86400秒都在写入数据,那么每秒的写入条数高达100万条,HBase当然是支持不了每秒百万条数据的,所以这百亿条数据可能不是通过实时地写入,而是批量地导入。批量导入推荐使用BulkLoad方式(推荐阅读:Spark之读写HBase),性能是普通写入方式几倍以上
2)存入HBase:普通写入是用JavaAPI put来实现,批量导入推荐使用BulkLoad
3)保证数据的正确:这里需要考虑RowKey的设计、预建分区和列族设计等问题
4)在规定时间内完成也就是存入速度不能过慢,并且当然是越快越好,使用BulkLoad
10、请列举几个HBase优化方法?
10.1、减少调整
减少调整这个如何理解呢?HBase中有几个内容会动态调整,如region(分区)、HFile,所以通过一些方法来减少这些会带来I/O开销的调整
Region:如果没有预建分区的话,那么随着region中条数的增加,region会进行分裂,这将增加I/O开销,所以解决方法就是根据你的RowKey设计来进行预建分区,减少region的动态分裂
HFile:HFile是数据底层存储文件,在每个memstore进行刷新时会生成一个HFile,当HFile增加到一定程度时,会将属于一个region的HFile进行合并,这个步骤会带来开销但不可避免,但是合并后HFile大小如果大于设定的值,那么HFile会重新分裂。为了减少这样的无谓的I/O开销,建议估计项目数据量大小,给HFile设定一个合适的值
10.2、 减少启停
数据库事务机制就是为了更好地实现批量写入,较少数据库的开启关闭带来的开销,那么HBase中也存在频繁开启关闭带来的问题
关闭Compaction,在闲时进行手动Compaction
因为HBase中存在Minor Compaction和Major Compaction,也就是对HFile进行合并,所谓合并就是I/O读写,大量的HFile进行肯定会带来I/O开销,甚至是I/O风暴,所以为了避免这种不受控制的意外发生,建议关闭自动Compaction,在闲时进行compaction
批量数据写入时采用BulkLoad
如果通过HBase-Shell或者JavaAPI的put来实现大量数据的写入,那么性能差是肯定并且还可能带来一些意想不到的问题,所以当需要写入大量离线数据时建议使用BulkLoad
10.3、 减少数据量
虽然我们是在进行大数据开发,但是如果可以通过某些方式在保证数据准确性同时减少数据量,何乐而不为呢?
(1)开启过滤,提高查询速度
开启BloomFilter,BloomFilter是列族级别的过滤,在生成一个StoreFile同时会生成一个MetaBlock,用于查询时过滤数据
(2)使用压缩:一般推荐使用Snappy和LZO压缩
10.4、 合理设计
在一张HBase表格中RowKey和ColumnFamily的设计是非常重要,好的设计能够提高性能和保证数据的准确性
RowKey设计:应该具备以下几个属性
散列性:散列性能够保证相同相似的rowkey聚合,相异的rowkey分散,有利于查询。
简短性:rowkey作为key的一部分存储在HFile中,如果为了可读性将rowKey设计得过长,那么将会增加存储压力。
唯一性:rowKey必须具备明显的区别性。
业务性:举例来说:
假如我的查询条件比较多,而且不是针对列的条件,那么rowKey的设计就应该支持多条件查询
如果我的查询要求是最近插入的数据优先,那么rowKey则可以采用叫上Long.Max-时间戳的方式,这样rowKey就是递减排列
列族的设计
列族的设计需要看应用场景,多列族设计的优劣
优势:HBase中数据时按列进行存储的,那么查询某一列族的某一列时就不需要全盘扫描,只需要扫描某一列族,减少了读I/O;其实多列族设计对减少的作用不是很明显,适用于读多写少的场景
劣势:降低了写的I/O性能。原因如下:数据写到store以后是先缓存在memstore中,同一个region中存在多个列族则存在多个store,每个store都一个memstore,当其实memstore进行flush时,属于同一个region的store中的memstore都会进行flush,增加I/O开销
11、HBase中RowFilter和BloomFilter原理?
11.1、RowFilter原理简析
RowFilter顾名思义就是对rowkey进行过滤,那么rowkey的过滤无非就是相等(EQUAL)、大于(GREATER)、小于(LESS),大于等于(GREATER_OR_EQUAL),小于等于(LESS_OR_EQUAL)和不等于(NOT_EQUAL)几种过滤方式。Hbase中的RowFilter采用比较符结合比较器的方式来进行过滤
比较器的类型如下:
BinaryComparator BinaryPrefixComparator NullComparator BitComparator RegexStringComparator SubStringComparator
例子:
Filter rowFilter = new RowFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes(rowKeyValue))); Scan scan = new Scan(); scan.setFilter(rowFilter) …
在上面例子中,比较符为EQUAL,比较器为BinaryComparator
11.2、BloomFilter优缺点
1)主要功能:提供随机读的性能
2)存储开销:BloomFilter是列族级别的配置,一旦表格中开启BloomFilter,那么在生成StoreFile时同时会生成一份包含BloomFilter结构的文件MetaBlock,所以会增加一定的存储开销和内存开销3)粒度控制:ROW和ROWCOL
4) 简单说一下BloomFilter原理:
① 内部是一个bit数组,初始值均为0
② 插入元素时对元素进行hash并且映射到数组中的某一个index,将其置为1,再进行多次不同的hash算法,将映射到的index置为1,同一个index只需要置1次
③ 查询时使用跟插入时相同的hash算法,如果在对应的index的值都为1,那么就可以认为该元素可能存在,注意,只是可能存在
④ 所以BlomFilter只能保证过滤掉不包含的元素,而不能保证误判包含
5)设置:在建表时对某一列设置BloomFilter即可
12 HBase 热点问题
刚创建 HBase 表的时候默认只有一个 Region 由一个 Region Server 管理,在数据量达到一定值的时候会触发分裂 split,这样会不断的分裂出更多的 Region,由不同的 Region Server 管理,每个 Region 管理的是一段连续的 row key,由 start row key 和 end row key 表示,这样会出现两个问题
无法充分利用分布式并发处理的优势,必须等待 Region 自动分裂成多个,这个过程可能会很久
由于每个 Region 管理一段连续的 row key,这样如果数据的读写不够随机,比如有自增 ID,比如大量操作集中在某段 row key,这样有可能导致压力都在同一个 Region 上
12.1 Region 分裂策略
定义在 hbase-site.xml 文件
<name>hbase.regionserver.region.split.policy</name>
<value>org.apache.hadoop.hbase.regionserver.IncreasingToUpperBoundRegionSplitPolicy</value>
<description>
A split policy determines when a region should be split. The various other split policies that
are available currently are ConstantSizeRegionSplitPolicy, DisabledRegionSplitPolicy,
DelimitedKeyPrefixRegionSplitPolicy, KeyPrefixRegionSplitPolicy etc.
</description>
默认策略是 IncreasingToUpperBoundRegionSplitPolicy,在 HBase 1.2 中,这个策略默认表示,当 Region 的大小达到 Region 个数的立方乘以 hbase.hregion.memstore.flush.size(默认 128 MB)再乘以 2 ,或是达到 hbase.hregion.max.filesize (默认 10 GB)时,就对该 Region 做分裂操作
第一次分裂的大小:1^3 * 128MB * 2 = 256MB
第二次分裂的大小:2^3 * 128MB * 2 = 2048MB
第三次分裂的大小:3^3 * 128MB * 2 = 6912MB
第四次分裂的大小:4^3 * 128MB * 2 = 16384MB,超过了 10GB,因此只取 10GB 后面的分裂大小都是 10GB
可以看到如果可以利用的节点比较多的话,那么可能得等很久才能充分利用
12.2 预分区
12.2.1、第一种预分区的方法
hbase org.apache.hadoop.hbase.util.RegionSplitter tablename HexStringSplit -c 10 -f f1:f2:f3
上面的命令表示创建一张名为 tablename 的表,这张表预先分配了 10 个 Region,有三个 CF,分别是 f1、f2、f3,预分区算法是 HexStringSplit,也可以选择 UniformSplit,其中 HexStringSplit 适合 row key 的前缀是十六进制的字符串的,UniformSplit 适合 row key 前缀完全随机的,预分区后,哪怕连续的 row key, HBase 也会通过算法将其分到不同的 Region,实现均匀分布,避免热点
12.2.2、第二种预分区的方法
hbase shell > create 'tablename', 'f1', SPLITS=> ['10', '20', '30', '40']
当可以提前知道 row key 的分布的时候,可以指定每个预分区的 region 的分割点,上面命令创建的表中,有 5 个 Region
Region 1 : row key 的前两位是 min~10
Region 2 : row key 的前两位是 10~20
Region 3 : row key 的前两位是 20~30
Region 4 : row key 的前两位是 30~40
Region 5 : row key 的前两位是 40~max
注意这里不单指数字字符,比如 1a 就会落在 Region 2
12.2.3、对已存在的表可以做强制分裂
hbase shell > split 'table', 'split point'
此外也可以设计自己的分裂方法
12.3、Phoenix 加盐
CREATE TABLE IF NOT EXISTS Product (
id VARCHAR not null,
time VARCHAR not null,
price FLOAT,
sale INTEGER,
inventory INTEGER,
CONSTRAINT pk PRIMARY KEY (id, time)
) COMPRESSION = 'GZ', SALT_BUCKETS = 6
本质上是对 HBase 表的 row key 做了哈希后,对 SALT_BUCKETS 取余数,并将结果(上面的例子中是 0~5)作为 byte 插入到 row key 的第一位,根据这个数值将数据分到不同 Region 中,由于是作为 byte 存储,所以 SALT_BUCKETS 能取的最大值是 256,拥有相同 salt byte 的 row 会被分到相同的 region server,所以通常取 region server 的数量作为 SALT_BUCKETS
由于加了盐的数据最前面多了一位,这样默认情况下,从不同 region server 取出来的数据无法按原来的 row key 排序,如果需要保证排序,需要改一个配置
phoenix.query.force.rowkeyorder = true
13、HRegionServer宕机如何处理?
1)ZooKeeper会监控HRegionServer的上下线情况,当ZK发现某个HRegionServer宕机之后会通知HMaster进行失效备援
2)该HRegionServer会停止对外提供服务,就是它所负责的region暂时停止对外提供服务
3)HMaster会将该HRegionServer所负责的region转移到其他HRegionServer上,并且会对HRegionServer上存在memstore中还未持久化到磁盘中的数据进行恢复
4) 这个恢复的工作是由WAL重播来完成,这个过程如下:
wal实际上就是一个文件,存在/hbase/WAL/对应RegionServer路径下
宕机发生时,读取该RegionServer所对应的路径下的wal文件,然后根据不同的region切分成不同的临时文件recover.edits
当region被分配到新的RegionServer中,RegionServer读取region时会进行是否存在recover.edits,如果有则进行恢复
14、HBase读写流程?
14.1、读流程:
1)HRegionServer保存着meta表以及表数据,要访问表数据,首先Client先去访问zookeeper,从zookeeper里面获取meta表所在的位置信息,即找到这个meta表在哪个HRegionServer上保存着
2)接着Client通过刚才获取到的HRegionServer的IP来访问Meta表所在的HRegionServer,从而读取到Meta,进而获取到Meta表中存放的元数据
3)Client通过元数据中存储的信息,访问对应的HRegionServer,然后扫描所在HRegionServer的Memstore和Storefile来查询数据
4)最后HRegionServer把查询到的数据响应给Client
14.2、写流程:
1)Client先访问zookeeper,找到Meta表,并获取Meta表元数据
2)确定当前将要写入的数据所对应的HRegion和HRegionServer服务器
3)Client向该HRegionServer服务器发起写入数据请求,然后HRegionServer收到请求并响应
4)Client先把数据写入到HLog,以防止数据丢失
5)然后将数据写入到Memstore
6)如果HLog和Memstore均写入成功,则这条数据写入成功
7)如果Memstore达到阈值,会把Memstore中的数据flush到Storefile中
8)当Storefile越来越多,会触发Compact合并操作,把过多的Storefile合并成一个大的Storefile
9)当Storefile越来越大,Region也会越来越大,达到阈值后,会触发Split操作,将Region一分为二
15、HBase内部机制是什么?
Hbase是一个能适应联机业务的数据库系统
物理存储:hbase的持久化数据是将数据存储在HDFS上
存储管理:一个表是划分为很多region的,这些region分布式地存放在很多regionserver上Region内部还可以划分为store,store内部有memstore和storefile
版本管理:hbase中的数据更新本质上是不断追加新的版本,通过compact操作来做版本间的文件合并,Region的split
集群管理:ZooKeeper + HMaster + HRegionServer
16、HTable API有没有线程安全问题
在程序中是单例还是多例?
在单线程环境下使用hbase的htable是没有问题,但是突然高并发多线程情况下就可能出现问题
以下为Htable的API说明:
This class is not thread safe for updates; the underlying write buffer can be corrupted if multiple threads contend over a single HTable instance.
当有多个线程竞争时可能把当前正在写的线程corrupted,那么原因是什么呢?
根据Htable的源码:
public HTable(final byte [] tableName) throws IOException {
this(HBaseConfiguration.create(), tableName);
}
public static Configuration create() {
Configuration conf = new Configuration();
return addHbaseResources(conf);
}
从上面我们可以看到每一个HTable的实例化过程都要创建一个新的conf,我们甚至可以认为一个conf对应的是一个HTable的connection,因此如果客户端对于同一个表,每次新new 一个configuration对象的话,那么意味着这两个HTable虽然操作的是同一个table,但是建立的是两条链接connection,它们的socket不是共用的,在多线程的情况下,经常会有new Htable的情况发生,而每一次的new都可能是一个新的connection,而我们知道zk上的链接是有限制的如果链接达到一定阈值的话,那么新建立的链接很有可能挤掉原先的connection,而导致线程不安全
因此hbase官方文档建议我们:HTable不是线程安全的。建议使用同一个HBaseConfiguration实例来创建HTable实例,这样可以共享ZooKeeper和socket实例。例如,最好这样做:
HBaseConfiguration conf = HBaseConfiguration.create();
HTable table1 = new HTable(conf, “myTable”);
HTable table2 = new HTable(conf, “myTable”);
而不是这样:
HBaseConfiguration conf1 = HBaseConfiguration.create();
HTable table1 = new HTable(conf1, “myTable”);
HBaseConfiguration conf2 = HBaseConfiguration.create();
HTable table2 = new HTable(conf2, “myTable”);
当然最方便的方法就是使用HTablepool了,维持一个线程安全的map里面存放的是tablename和其引用的映射,可以认为是一个简单的计数器,当需要new 一个HTable实例时直接从该pool中取,用完放回
17、Hbase中的memstore是用来做什么的?
hbase为了保证随机读取的性能,所以hfile里面的rowkey是有序的。当客户端的请求在到达regionserver之后,为了保证写入rowkey的有序性,所以不能将数据立刻写入到hfile中,而是将每个变更操作保存在内存中,也就是memstore中。memstore能够很方便的支持操作的随机插入,并保证所有的操作在内存中是有序的。当memstore达到一定的量之后,会将memstore里面的数据flush到hfile中,这样能充分利用hadoop写入大文件的性能优势,提高写入性能
由于memstore是存放在内存中,如果regionserver因为某种原因死了,会导致内存中数据丢失。所有为了保证数据不丢失,hbase将更新操作在写入memstore之前会写入到一个write ahead log(WAL)中。WAL文件是追加、顺序写入的,WAL每个regionserver只有一个,同一个regionserver上所有region写入同一个的WAL文件。这样当某个regionserver失败时,可以通过WAL文件,将所有的操作顺序重新加载到memstore中
18、HBase在进行模型设计时重点在什么地方?
一张表中定义多少个Column Family最合适?为什么?Column Family的个数具体看表的数据,一般来说划分标准是根据数据访问频度,如一张表里有些列访问相对频繁,而另一些列访问很少,这时可以把这张表划分成两个列族,分开存储,提高访问效率
19、如何提高HBase客户端的读写性能?
请举例说明
1)开启bloomfilter过滤器,开启bloomfilter比没开启要快3、4倍
2)Hbase对于内存有特别的需求,在硬件允许的情况下配足够多的内存给它
3)通过修改hbase-env.sh中的
export HBASE_HEAPSIZE=3000 #这里默认为1000m
4)增大RPC数量
通过修改hbase-site.xml中的hbase.regionserver.handler.count属性,可以适当的放大RPC数量,默认值为10有点小。
20、HBase集群安装注意事项?
1)HBase需要HDFS的支持,因此安装HBase前确保Hadoop集群安装完成
2)HBase需要ZooKeeper集群的支持,因此安装HBase前确保ZooKeeper集群安装完成
3)注意HBase与Hadoop的版本兼容性
4)注意hbase-env.sh配置文件和hbase-site.xml配置文件的正确配置
5)注意regionservers配置文件的修改
注意集群中的各个节点的时间必须同步,否则启动HBase集群将会报错
21、直接将时间戳作为行健
在写入单个region 时候会发生热点问题,为什么呢?
region中的rowkey是有序存储,若时间比较集中。就会存储到一个region中,这样一个region的数据变多,其它的region数据很少,加载数据就会很慢, 直到region分裂,此问题才会得到缓解。
22、region太小和region太大带来的冲突?
Region过大会发生多次compaction,将数据读一遍并重写一遍到hdfs 上,占用io,region过小会造成多次split,region 会下线,影响访问服务, 最佳的解决方法是调整hbase.hregion. max.filesize 为256m。
默认值:256M
说明:Maximum HStoreFile size. If any one of a column families’ HStoreFiles has grown to exceed this value, the hosting HRegion is split in two.
HStoreFile的最大值。如果任何一个Column Family(或者说HStore)的HStoreFiles的大小超过这个值,那么,其所属的HRegion就会Split成两个。
调优:
hbase中hfile的默认最大值(hbase.hregion.max.filesize)是256MB,而google的bigtable论文中对tablet的最大值也推荐为100-200MB,这个大小有什么秘密呢?
众所周知hbase中数据一开始会写入memstore,当memstore满64MB以后,会flush到disk上而成为storefile。当storefile数量超过3时,会启动compaction过程将它们合并为一个storefile。这个过程中会删除一些timestamp过期的数据,比如update的数据。而当合并后的storefile大小大于hfile默认最大值时,会触发split动作,将它切分成两个region。
持续insert压力测试,并设置了不同的hbase.hregion.max.filesize,根据结果得到如下结论:值越小,平均吞吐量越大,但吞吐量越不稳定;值越大,平均吞吐量越小,吞吐量不稳定的时间相对更小。
为什么会这样呢?推论如下:
a 当hbase.hregion.max.filesize比较小时,触发split的机率更大,而split的时候会将region offline,因此在split结束的时间前,访问该region的请求将被block住,客户端自我block的时间默认为1s。当大量的region同时发生split时,系统的整体访问服务将大受影响。因此容易出现吞吐量及响应时间的不稳定现象
b 当hbase.hregion.max.filesize比较大时,单个region中触发split的机率较小,大量region同时触发split的机率也较小,因此吞吐量较之小hfile尺寸更加稳定些。但是由于长期得不到split,因此同一个region内发生多次compaction的机会增加了。compaction的原理是将原有数据读一遍并重写一遍到hdfs上,然后再删除原有数据。无疑这种行为会降低以io为瓶颈的系统的速度,因此平均吞吐量会受到一些影响而下降。
综合以上两种情况,hbase.hregion.max.filesize不宜过大或过小,256MB或许是一个更理想的经验参数。对于离线型的应用,调整为128MB会更加合适一些,而在线应用除非对split机制进行改造,否则不应该低于256MB
23、autoflush=false的影响
无论是官方还是很多blog都提倡为了提高hbase的写入速度而在应用代码中设置autoflush=false,然后lz认为在在线应用中应该谨慎进行该设置。原因如下:
a autoflush=false的原理是当客户端提交delete或put请求时,将该请求在客户端缓存,直到数据超过2M(hbase.client.write.buffer决定)或用户执行了hbase.flushcommits()时才向regionserver提交请求。因此即使htable.put()执行返回成功,也并非说明请求真的成功了。假如还没有达到该缓存而client崩溃,该部分数据将由于未发送到regionserver而丢失。这对于零容忍的在线服务是不可接受的。
b autoflush=true虽然会让写入速度下降2-3倍,但是对于很多在线应用来说这都是必须打开的,也正是hbase为什么让它默认值为true的原因。当该值为true时,每次请求都会发往regionserver,而regionserver接收到请求后第一件事就是写hlog,因此对io的要求是非常高的,为了提高hbase的写入速度,应该尽可能高地提高io吞吐量,比如增加磁盘、使用raid卡、减少replication因子数等
24、从性能的角度谈table中family和qualifier的设置
对于传统关系型数据库中的一张table,在业务转换到hbase上建模时,从性能的角度应该如何设置family和qualifier呢?
最极端的,①每一列都设置成一个family,②一个表仅有一个family,所有列都是其中的一个qualifier,那么有什么区别呢?
从读的方面考虑:
family越多,那么获取每一个cell数据的优势越明显,因为io和网络都减少了。
如果只有一个family,那么每一次读都会读取当前rowkey的所有数据,网络和io上会有一些损失。
当然如果要获取的是固定的几列数据,那么把这几列写到一个family中比分别设置family要更好,因为只需一次请求就能拿回所有数据。
从写的角度考虑:
首先,内存方面来说,对于一个Region,会为每一个表的每一个Family分配一个Store,而每一个Store,都会分配一个MemStore,所以更多的family会消耗更多的内存。
其次,从flush和compaction方面说,目前版本的hbase,在flush和compaction都是以region为单位的,也就是说当一个family达到flush条件时,该region的所有family所属的memstore都会flush一次,即使memstore中只有很少的数据也会触发flush而生成小文件。这样就增加了compaction发生的机率,而compaction也是以region为单位的,这样就很容易发生compaction风暴从而降低系统的整体吞吐量。
第三,从split方面考虑,由于hfile是以family为单位的,因此对于多个family来说,数据被分散到了更多的hfile中,减小了split发生的机率。这是把双刃剑。更少的split会导致该region的体积比较大,由于balance是以region的数目而不是大小为单位来进行的,因此可能会导致balance失效。而从好的方面来说,更少的split会让系统提供更加稳定的在线服务。而坏处我们可以通过在请求的低谷时间进行人工的split和balance来避免掉。
因此对于写比较多的系统,如果是离线应该,我们尽量只用一个family好了,但如果是在线应用,那还是应该根据应用的情况合理地分配family。
25、hbase.regionserver.handler.count
RegionServer端开启的RPC监听器实例个数,也即RegionServer能够处理的IO请求线程数。默认是10.
此参数与内存息息相关。该值设置的时候,以监控内存为主要参考。
对于 单次请求内存消耗较高的Big PUT场景(大容量单次PUT或设置了较大cache的scan,均属于Big PUT)或ReigonServer的内存比较紧张的场景,可以设置的相对较小。
对于 单次请求内存消耗低,TPS(TransactionPerSecond,每秒事务处理量)要求非常高的场景,可以设置的相对大些。