HBase

HBASE简介

HBase是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文《Bigtable》一个结构化数据的“分布式存储系统”。HBase是Apache的Hadoop项目的子项目,具有低延迟的数据查询能力,且是NoSQL数据库,即非关系型数据库。

HBase特点总结:

  1. 非关系型数据库,可以存储海量数据(基于Hadoop的HDFS)
  2. 提供了低延迟的数据查询能力。低延迟指的是能够在秒级甚至毫秒级给出响应。
  3. 面向列存储的数据库。(mysql,oracle都是面向行存储的)

行存储VS列存储

目前大数据存储有两种方案可供选择:行存储(Row-Based)和列存储(Column-Based)。业界对两种存储方案有很多争持,集中焦点是:谁能够更有效地处理海量数据,且兼顾安全、可靠、完整性。从目前发展情况看,关系数据库已经不适应这种巨大的存储量和计算要求,基本是淘汰出局。在已知的几种大数据处理软件中,Hadoop的HBase采用列存储,MongoDB是文档型的行存储,Lexst是二进制型的行存储。
在这里插入图片描述

在数据写入上的对比

  1. 行存储的写入是一次完成。如果这种写入建立在操作系统的文件系统上,可以保证写入过程的成功或者失败,数据的完整性因此可以确定。
  2. 列存储由于需要把一行记录拆分成单列保存,写入次数明显比行存储多(意味着磁头调度次数多,而磁头调度是需要时间的,一般在1ms~10ms),再加上磁头需要在盘片上移动和定位花费的时间,实际时间消耗会更大。所以,行存储在写入上占有很大的优势。
  3. 还有数据修改,这实际也是一次写入过程。不同的是,数据修改是对磁盘上的记录做删除标记。行存储是在指定位置写入一次,列存储是将磁盘定位到多个列上分别写入,这个过程仍是行存储的列数倍。所以,数据修改也是以行存储占优。

在数据读取上的对比

  1. 数据读取时,行存储通常将一行数据完全读出,如果只需要其中几列数据的情况,就会存在冗余列,出于缩短处理时间的考量,消除冗余列的过程通常是在内存中进行的。
  2. 列存储每次读取的数据是集合的一段或者全部,不存在冗余性问题。
  3. 两种存储的数据分布。由于列存储的每一列数据类型是同质的,不存在二义性问题。比如说某列数据类型为整型(int),那么它的数据集合一定是整型数据。这种情况使数据解析变得十分容易。相比之下,行存储则要复杂得多,因为在一行记录中保存了多种类型的数据,数据解析需要在多种数据类型之间频繁转换,这个操作很消耗CPU,增加了解析的时间。所以,列存储的解析过程更有利于分析大数据。

传统行式数据库的特性如下:
①数据是按行存储的。
②没有索引的查询使用大量I/O。比如一般的数据库表都会建立索引,通过索引加快查询效率。
③建立索引和物化视图需要花费大量的时间和资源。
④面对查询需求,数据库必须被大量膨胀才能满足需求。

列式数据库的特性如下:
①数据按列存储,即每一列单独存放。
②数据即索引。
③只访问查询涉及的列,可以大量降低系统I/O。
④每一列由一个线程来处理,即查询的并发处理性能高。
⑤数据类型一致,数据特征相似,可以高效压缩。比如有增量压缩、前缀压缩算法都是基于列存储的类型定制的,所以可以大幅度提高压缩比,有利于存储和网络输出数据带宽的消耗。

HBase基本概念

xxx
1)Row Key
hbase本质上也是一种Key-Value存储系统。Key相当于RowKey,Value相当于列族数据的集合。
Row key行键 (Row key)可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),在hbase内部,row key保存为字节数组。
存储时,数据按照Row key的字典序(byte order)排序存储。设计key时,要充分排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性)

2)列族(列簇)
hbase表中的每个列,都归属与某个列族。列族是表的schema的一部分(而列不是),列族必须在使用表之前定义。列名都以列族作为前缀。例如courses:history , courses:math 都属于 courses 这个列族。
访问控制、磁盘和内存的使用统计都是在列族层面进行的。实际应用中,列族上的控制权限能 帮助我们管理不同类型的应用:我们允许一些应用可以添加新的基本数据、一些应用可以读取基本数据并创建继承的列族、一些应用则只允许浏览数据(甚至可能因 为隐私的原因不能浏览所有数据)。

3)Cell与时间戳
由{row key, column( = + < label>), version} 唯一确定的单元。cell中的数据是没有类型的,全部是字节码形式存贮。
每个 cell都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64位整型。时间戳可以由hbase(在数据写入时自动 )赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个 cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。
为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,hbase提供了两种数据版本回收方式。一是保存数据的最后n个版本,二是保存最近一段时间内的版本(比如最近七天)。用户可以针对每个列族进行设置。

HBase完全分布式安装

  1. 1.准备三台虚拟机,01作为主节点,02、03作为从节点。(把每台虚拟机防火墙都关掉,配置免密码登录,配置每台的主机名和hosts文件)。
  2. 01节点上安装和配置:Hadoop+Hbase+JDK+Zookeeper
  3. 02、03节点上安装和配置:Hbase+JDK+Zookeeper
  4. 修改conf目录下的hbase-env.sh
    a.配置27行的JAVA_HOME:export JAVA_HOME=xxxx
    b.修改Zookeeper和Hbase的协调模式,hbase默认使用自带的zookeeper,如果需要使用外部zookeeper,需要先关闭。在122行:export HBASE_MANAGES_ZK=false
  5. 修改hbase-site.xml,配置开启完全分布式模式
<property>
	<name>hbase.rootdir</name>
	<value>hdfs://hadoop01:9000/hbase</value>
</property> 
<property>
	<name>hbase.cluster.distributed</name>
	<value>true</value>
</property>
#配置Zookeeper的连接地址与端口号
<property>
	<name>hbase.zookeeper.quorum</name>
	<value>hadoop01:2181,hadoop02:2181,hadoop03:2181</value>
</property>
  1. 配置region服务器,修改conf/regionservers文件,每个主机名独占一行,hbase启动或关闭时会按照该配置顺序启动或关闭主机中的hbase
    hadoop01
    hadoop02
    hadoop03
  2. 将01节点配置好的hbase远程拷贝到02和03节点
  3. 在01,02,03的zookeeper目录下的bin下启动服务:sh zkServer.sh start
  4. 在01节点上启动hadoop:start-all.sh
  5. 在01节点上hbase的bin目录下启动hbase:sh start-hbase.sh
  6. 通过jps指令查看各节点进程是否启动成功
  7. 通过浏览器访问http://ip地址:60010来访问web界面,通过web见面管理hbase
  8. 关闭Hmaster需进入到hbase安装目录下的bin目录执行:stop-hbase.sh
  9. 关闭regionserver,进入到hbase安装目录下的bin目录执行:sh hbase-daemon.sh stop regionserver

HBase基础指令

在这里插入图片描述

补充说明:
hbase命令行下不能使用删除,
可以使用 ctrl+删除键 来进行删除
修改xshell配置:
文件->属性->终端->键盘
->delete键序列[VT220Del]
->backspace键序列[ASCII127]

HBase API

  1. 创建一个java项目
  2. 导入hbase安装目录下的lib文件夹,导入依赖jar包
创建表
	public void createTbale() throws Exception{
		//获取HBase的环境参数对象
		Configuration conf = HBaseConfiguration.create();
		conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
		HBaseAdmin admin = new HBaseAdmin(conf);
		
		HTableDescriptor table =new HTableDescriptor(TableName.valueOf("tb2"));
		HColumnDescriptor cf1 = new HColumnDescriptor("cf1".getBytes());
		HColumnDescriptor cf2 = new HColumnDescriptor("cf2".getBytes());
		//将指定的列族和表绑定
		table.addFamily(cf1);
		table.addFamily(cf2);
		
		//执行创建表
		admin.createTable(table);
		admin.close();
	}
插入数据
public void insertData() throws Exception{
		Configuration conf  = HBaseConfiguration.create();
		conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
		
		//创建表对象,并指定操作的表名
		HTable table = new HTable(conf,"tb1");
		//创建行对象,并指定行键
		Put put = new Put("row1".getBytes());
		//--1参:列族名,2参:列名,3参:列值
		put.add("cf1".getBytes(),"name".getBytes(),"tom".getBytes());
		put.add("cf1".getBytes(),"age".getBytes(),"30".getBytes());
		put.add("cf2".getBytes(),"city".getBytes(),"cd".getBytes());
		
		//执行插入
		table.put(put);
		table.close();
	}
批量插入数据

将数据存入LIist中,提高性能

public void batchInsert() throws Exception{
		Configuration conf = HBaseConfiguration.create();
		conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
		
		HTable table = new HTable(conf,"tb1");
		List<Put> puts=new ArrayList<>();
		for(int i=0;i<100;i++){
			Put put = new Put(("row"+i).getBytes());
			put.add("cf1".getBytes(),"num".getBytes(),(i+"").getBytes());
			puts.add(put);
		}
		//执行批量插入
		table.put(puts);
		table.close();
	}
获取数据
public void getData() throws Exception{
		Configuration conf = HBaseConfiguration.create();
		conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
		
		HTable table = new HTable(conf,"tb1");
		//通过行键去指定读取的数据
		Get get = new Get("row1".getBytes());
		//执行查询,把结果集封装到result中
		Result result = table.get(get);
		//通过列族名和列名获取值
		byte[] name = result.getValue("cf1".getBytes(),"name".getBytes());
		byte[] age = result.getValue("cf1".getBytes(),"age".getBytes());
		byte[] city = result.getValue("cf2".getBytes(),"city".getBytes());
		System.out.println(new String(name)+":"+new String(age)+":"+new String(city));
		table.close();
	}
整表扫描
public void scanTable() throws IOException{
		Configuration conf = HBaseConfiguration.create();
		conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
		
		HTable table = new HTable(conf,"tb1");
		//创建扫描对象,可以通过对象扫描整表数据
		Scan scan = new Scan();
		
		//指定扫描的起始行列
		scan.setStartRow("row10".getBytes());
		scan.setStopRow("row19".getBytes());
		
		//将整表数据封装到结果集(包含多行数据)
		ResultScanner rs = table.getScanner(scan);
		//获取行数据的迭代器
		Iterator<Result> it = rs.iterator();
		while(it.hasNext()){
			//每迭代一次就获取一行数据
			Result result = it.next();
			byte[] num = result.getValue("cf1".getBytes(), "num".getBytes());
			System.out.println(new String(num));
		}
		
		table.close();
	}
删除数据
public void delete() throws IOException{
		Configuration conf = HBaseConfiguration.create();
		conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
		
		HTable table = new HTable(conf,"tb1");
		
		//根据指定行键删除
		Delete delete = new Delete("row0".getBytes());
		table.delete(delete);
		//还可以通过List<Delete> 来实现批量删除
		table.close();
	}
删除表

需要先将表置为disable

Configuration conf = HBaseConfiguration.create();
		conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
		
		HBaseAdmin admin = new HBaseAdmin(conf);
		//先禁用
		admin.disableTable("tb2");
		//再删除
		admin.deleteTable("tb2");
		
		admin.close();
过滤器
public void rowFilter() throws IOException{
		Configuration conf = HBaseConfiguration.create();
		conf.set("hbase.zookeeper.quorum", "hadoop01:2181,hadoop02:2181,hadoop03:2181");
		
		HTable table = new HTable(conf,"tb1");
		Scan scan = new Scan();
		//指定扫描范围
		//scan.setStartRow("row1".getBytes());
		//scan.setStopRow("row30".getBytes());
		
		//正则过滤器
		//Filter filter = new RowFilter(CompareOp.EQUAL, new RegexStringComparator("^.*3.*$"));
		
		//行键比较过滤器
		//Filter filter = new RowFilter(CompareOp.LESS_OR_EQUAL,new BinaryComparator("row30".getBytes()));
		
		//行键前缀过滤器
		//Filter filter = new PrefixFilter("row3".getBytes());
		
		//列值过滤器
		Filter filter = new SingleColumnValueFilter("cf1".getBytes(), "name".getBytes(), CompareOp.EQUAL, "tom".getBytes());
		
		//绑定过滤器
		scan.setFilter(filter);
		//执行扫描,会结合过滤器查询
		ResultScanner rs = table.getScanner(scan);
		Iterator<Result> it = rs.iterator();
		while(it.hasNext()){
			Result result = it.next();
			byte[] num = result.getValue("cf1".getBytes(),"num".getBytes());
			System.out.println(new String(num));
		}
		table.close();
	}

HBase物理存储原理

HBase里的一个Table 在行的方向上分割为多个HRegion。即HBase中一个表的数据会被划分成很多的HRegion,HRegion可以动态扩展并且HBase保证HRegion的负载均衡。HRegion实际上是行键排序后的按规则分割的连续的存储空间。每个HRegion的大小可以是1~20GB。这个大小由hbase.hregion.max.filesize指定,默认为10GB。

HRegion由一个或者多个HStore组成,每个Hstore保存一个columns family(列族)。
每个HStore又由一个memStore(写缓存)和0至多个StoreFile组成。StoreFile以HFile格式保存在HDFS上。如图:
在这里插入图片描述
总结:HRegion是分布式的存储最小单位,StoreFile(Hfile)是存储最小单位。

HBase系统

在这里插入图片描述

HBase采用Master/Slave架构搭建集群,它隶属于Hadoop生态系统,由以下类型节点组成:

  1. HMaster节点
  2. HRegionServer节点
  3. ZooKeeper集群
  4. Hbase的数据存储于HDFS中,因而涉及到HDFS的NameNode、DataNode等。RegionServer和DataNode一般会放在相同的Server上实现数据的本地化(避免或减少数据在网络中的传输,节省带宽)。数据本地化并不是总能实现,比如在HRegion移动(如因Split)时,需要等下一次Compact才能继续回到本地化。

HMaster节点

HMaster没有单点故障问题,可以启动多个HMaster,一般2个,通过ZooKeeper的Master Election机制保证同时只有一个HMaster处于Active状态,其他的HMaster则处于热备份状态。

  1. 管理HRegionServer,实现其负载均衡。
  2. 管理和分配HRegion,比如在HRegion split时分配新的HRegion;在HRegionServer退出时迁移其内的HRegion到其他HRegionServer上。
  3. 实现DDL操作(Data Definition Language,namespace和table的增删改,column familiy的增删改等)。
  4. 管理namespace和table的元数据(实际存储在HDFS上)。
  5. 权限控制(ACL)。

HRegionServer

在这里插入图片描述
功能:

  1. 存放和管理本地HRegion。
  2. 读写HDFS,管理Table中的数据。
  3. Client直接通过HRegionServer读写数据(从HMaster中获取元数据,找到RowKey所在的HRegion/HRegionServer后)。
WAL

WAL即Write Ahead Log,在早期版本中称为HLog,它是HDFS上的一个文件,如其名字所表示的,所有写操作都会先保证将写操作写入这个Log文件后,才会真正更新MemStore,最后写入HFile中。采用这种模式,可以保证HRegionServer宕机后,我们依然可以从该Log文件中恢复数据,Replay所有的操作,而不至于数据丢失。

BlockCache

BlockCache是一个读缓存,即“引用局部性”原理(也应用于CPU,分空间局部性和时间局部性,空间局部性是指CPU在某一时刻需要某个数据,那么有很大的概率在一下时刻它需要的数据在其附近;时间局部性是指某个数据在被访问过一次后,它有很大的概率在不久的将来会被再次的访问),将数据预读取到内存中,以提升读的性能。这样设计的目的是为了提高读缓存的命中率。

MemStore

.MemStore是一个写缓存(In Memory Sorted Buffer),所有数据的写在完成WAL日志写后,会写入MemStore中,由MemStore根据一定的算法(LSM-TREE算法 日志合并树算法,这个算法的作用是将数据顺序写磁盘,而不是随机写,减少磁头调度时间,从而提高写入性能) 将数据Flush到底层的HDFS文件中(HFile),通常每个HRegion中的每个 Column Family有一个自己的MemStore。

有以下三种情况可以触发MemStore的Flush动作:

  1. 当一个HRegion中的MemStore的大小超过了:
    hbase.hregion.memstore.flush.size的大小,默认128MB。此时当前的MemStore会Flush到HFile中。

  2. 当RS服务器上所有的MemStore的大小超过了:hbase.regionserver.global.memstore.upperLimit的大小,默认35%的内存使用量。
    比如:一台服务器内存是64GB,留出10GB给操作系,留出4GB给他技术框架,剩余的50GB给HBase用,即当RS服务器上所有的Memstore总内存大小达到 50GB*35%,会触发flush
    此时当前HRegionServer中所有HRegion中的MemStore可能都会Flush。从最大的Memostore开始flush

  3. 当前HRegionServer中WAL的大小超过了 1GB
    hbase.regionserver.hlog.blocksize(32MB) * hbase.regionserver.max.logs(32)的数量,当前HRegionServer中所有HRegion中的MemStore都会Flush
    这里指的是两个参数相乘的大小。
    查代码发现:hbase.regionserver.max.logs默认值是32,而hbase.regionserver.hlog.blocksize是HDFS的默认blocksize,32MB

HFile格式

v1格式:
在这里插入图片描述
V1的HFile由多个Data Block、Meta Block、FileInfo、Data Index、Meta Index、Trailer组成,其中Data Block是HBase的最小存储单元,在前文中提到的BlockCache就是基于Data Block的缓存的。一个Data Block由一个魔数和一系列的KeyValue(Cell)组成,魔数是一个随机的数字,用于表示这是一个Data Block类型,以快速检测这个Data Block的格式,防止数据的破坏。Data Block的大小可以在创建Column Family时设置(HColumnDescriptor.setBlockSize()),默认值是64KB,大号的DadaBlock有利于顺序Scan,小号DataBlock利于随机查询

v2格式是一个多层的类B+树索引,采用这种设计,可以实现查找不需要读取整个文件:
在这里插入图片描述
Data Block中的Cell都是升序排列,每个block都有它自己的Leaf-Index,每个Block的最后一个Key被放入Intermediate-Index中,Root-Index指向Intermediate-Index。在HFile的末尾还有Bloom Filter(布隆过滤)用于快速定位那么没有在某个Data Block中的Row;TimeRange信息用于给那些使用时间查询的参考。在HFile打开时,这些索引信息都被加载并保存在内存中,以增加以后的读取性能。

HFile的Compaction机制

MemStore每次Flush会创建新的HFile,而过多的HFile会引起读的性能问题,那么如何解决这个问题呢?HBase采用Compaction机制来解决这个问题。在HBase中Compaction分为两种:Minor Compaction和Major Compaction

  1. Minor Compaction是指选取一些小的、相邻的StoreFile将他们合并成一个更大的StoreFile,在这个过程中不会处理已经Deleted或Expired的Cell。一次Minor Compaction的结果是更少并且更大的StoreFile。
  2. Major Compaction是指将所有的StoreFile合并成一个StoreFile,在这个过程中,标记为Deleted的Cell会被删除,而那些已经Expired的Cell会被丢弃,那些已经超过最多版本数的Cell会被丢弃。一次Major Compaction的结果是一个HStore只有一个StoreFile存在。 Major Compaction可以手动或自动触发,然而由于它会引起很多的I/O操作而引起性能问题,因而它一般会被安排在周末、凌晨等集群比较闲的时间。

HBase默认使用的是Minor compaction
API实现:
//–minor compact
admin.compact(“tab2”.getBytes());
//–major compact
admin.majorCompact(“tab2”.getBytes());
指令实现:
compact(‘tab2’)
major_compact(‘tab2’)

HBase的第一次读写

客户端在第一次访问用户Table的流程就变成了:

  1. 从ZooKeeper(/hbase/meta-region-server)中的hbase:meta获取要访问表的位置(所有HRegionServer的位置),缓存该位置信息。
  2. 从HRegionServer中查询用户Table对应请求的RowKey所在的HRegionServer,缓存该位置信息。
  3. 从查询到HRegionServer中读取Row

在这里插入图片描述

HBase写流程

当客户端发起一个Put请求时,首先它从hbase:meta表中查出该Put数据最终需要去的HRegionServer。然后客户端将Put请求发送给相应的HRegionServer,在HRegionServer中它首先会将该Put操作写入WAL日志文件中(Flush到磁盘中)。
写完WAL日志文件后,然后会将数据写到Memstore,在Memstore按Rowkey排序,以及用LSM-TREE对数据做合并处理。HRegionServer根据Put中的TableName和RowKey找到对应的HRegion,并根据Column Family找到对应的HStore,并将Put写入到该HStore的MemStore中。此时写成功,并返回通知客户端。

HBase读流程

HBase中扫瞄的顺序依次是:BlockCache、MemStore、StoreFile(HFile)(这个扫描顺序的目的也是为了减少磁盘的I/O次数)。其中StoreFile的扫瞄先会使用Bloom Filter(布隆过滤算法)过滤那些不可能符合条件的DataBlock,然后使用Block Index快速定位Cell,并将其加载到BlockCache中,然后从BlockCache中读取。

HBase调优

硬件和操作系统调优
a. 配置内存
在互联网领域,服务器内存方面的主流配置已经是64GB,所以一定要根据实际的需求和预算配备服务器内存。如果资源很紧张,推荐内存最小在32GB,如果再小会严重影响HBase集群性能。
b. 配置CPU
c.配置硬盘
如果是机械盘,看转速,14000转,一般的是7000转。
可以考虑用SSD固态硬盘,底层是通过电阻器原件构架的,速度接近于内存

垃圾回收器的选择
对于运行HBase相关进程JVM的垃圾回收器,不仅仅关注吞吐量,还关注停顿时间,而且两者之间停顿时间更为重要,因为HBase设计的初衷就是解决大规模数据集下实时访问的问题。那么按照首位是停顿时间短,从这个方面CMS和G1有着非常大的优势。最终选用的垃圾收集器搭配组合是CMS+ParNew(新生代)
配置方式:需要添加到hbase-env.sh文件中
export HBASE_OPTS="-XX:+UseConcMarkSweepGC" -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSCompactAtFullCollection

JVM堆大小设置
堆内存大小参数hbase-env.sh文件中设置,设置的代码如下:export HBASE_HEAPSIZE=16384
在上面代码中指定堆内存大小是16284,单位是MB,即16GB。当然,这个值需要根据节点实际的物理内存来决定。一般不超过实际物理内存的1/2。

调节数据块(data block)的大小
如果mytable表在实际业务中,随机查找业务多,就调小。
如果范围查询(顺序扫描)业务多,就调大。
可以在表实例化时设置数据块大小,代码如下:
hbase(main):002:0> create ‘mytable’,{NAME => ‘colfam1’, BLOCKSIZE => ‘65536’}

适当时机关闭数据块缓存
关闭缓存的原因在于:如果只是执行很多顺序化扫描,会多次使用缓存,并且可能会滥用缓存,从而把应该放进缓存获得性能提升的数据给排挤出去。

数据块缓存默认是打开的。可以在新建表或更改表时关闭数据块缓存属性:hbase(main):002:0> create ‘mytable’, {NAME => ‘colfam1’, BLOCKCACHE => ‘false’}
如果预见到mytable的范围查询(顺序查找)业务较多,这种场景可以将mytable的读缓存机制关掉。

开启布隆过滤器
如果要查找一个很短的行,只在整个数据块的起始行键上建立索引是无法给出更细粒度的索引信息的。布隆过滤器(Bloom Filter)允许对存储在每个数据块的数据做一个反向测验。使用布隆过滤器也不是没有代价,相反,存储这个额外的索引层次占用额外的空间。行级布隆过滤器比列标识符级布隆过滤器占用空间要少。

可以在列族上打开布隆过滤器,代码如下:hbase(main):007:0> create ‘mytable’, {NAME => ‘colfam1’, BLOOMFILTER => ‘ROWCOL’}
布隆过滤器参数的默认值是NONE。另外,还有两个值:ROW表示行级布隆过滤器;ROWCOL表示列标识符级布隆过滤器。行级布隆过滤器在数据块中检查特定行键是否不存在,列标识符级布隆过滤器检查行和列标识符联合体是否不存在。ROWCOL布隆过滤器的空间开销高于ROW布隆过滤器。

开启数据压缩
HFile可以被压缩并存放在HDFS上,这有助于节省硬盘I/O,此外,可以节省带宽。HBase可以使用多种压缩编码,包括LZO、SNAPPY和GZIP,LZO和SNAPPY是其中最流行的两种。

当建表时可以在列族上打开压缩,代码如下:hbase(main):002:0> create ‘mytable’, {NAME => ‘colfam1’, COMPRESSION => ‘SNAPPY’}
注意,数据只在硬盘上是压缩的,在内存中(MemStore或BlockCache)或在网络传输时是没有压缩的。

设置Scan缓存
HBase的Scan查询中可以设置缓存,定义一次交互从服务器端传输到客户端的行数,设置方法是使用Scan类中setCaching()方法,这样能有效地减少服务器端和客户端的交互,更好地提升扫描查询的性能。

显式地指定列
当使用Scan或Get来处理大量的行时,最好确定一下所需要的列。能够很大程度上减少网络I/O的花费,否则会造成很大的资源浪费,且有效地减少网络传输量。使用Scan类中指定列的addColumn()方法。

关闭ResultScanner
ResultScanner类用于存储服务端扫描的最终结果,可以通过遍历该类获取查询结果。但是,如果不关闭该类,可能会出现服务端在一段时间内一直保存连接,资源无法释放,从而导致服务器端某些资源的不可用
代码的最后一行rsScanner.close()就是执行关闭ResultScanner。

使用批量读/写

关闭写WAL日志
在默认情况下,为了保证系统的高可用性,写WAL日志是开启状态。如果应用可以容忍一定的数据丢失的风险,可以尝试在更新数据时,关闭写WAL。关闭写WAL操作通过Put类中的writeToWAL()设置。

设置AutoFlush
HTable有一个属性是AutoFlush,该属性用于支持客户端的批量更新。该属性默认值是true,即客户端每收到一条数据,立刻发送到服务端。如果将该属性设置为false,当客户端提交Put请求时,将该请求在客户端缓存,直到数据达到某个阈值的容量时(该容量由参数hbase.client.write.buffer决定)或执行hbase.flushcommits()时,才向RegionServer提交请求。
这种方式避免了每次跟服务端交互,采用批量提交的方式,所以更高效。

但是,如果还没有达到该缓存而客户端崩溃,该部分数据将由于未发送到RegionServer而丢失。这对于有些零容忍的在线服务是不可接受的。所以,设置该参数的时候要慎重。
table.setAutoFlush(false);
table.setWriteBufferSize(1210241024);
table.flushCommits();

预创建Region
在HBase中创建表时,该表开始只有一个Region,插入该表的所有数据会保存在该Region中。随着数据量不断增加,当该Region大小达到一定阈值时,就会发生分裂(Region Splitting)操作。并且在这个表创建后相当长的一段时间内,针对该表的所有写操作总是集中在某一台或者少数几台机器上,这不仅仅造成局部磁盘和网络资源紧张,同时也是对整个集群资源的浪费。这个问题在初始化表,即批量导入原始数据的时候,特别明显。为了解决这个问题,可以使用预创建Region的方法。

Hbase内部提供了RegionSplitter工具,使用命令如下:
${HBASE_HOME}/bin/hbase org.apache.hadoop.hbase.util.RegionSplitter test2 HexStringSplit -c 10 -f cf1
其中,test2是表名,HexStringSplit表示划分的算法,参数-c 10表示预创建10个Region,-f cf1表示创建一个名字为cf1的列族。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值