- 浏览: 248125 次
- 性别:
文章分类
最新评论
第一章 介绍
HBase的历史和使用场景
关系型数据库的问题
1.访问量过大必须主从分离
2.主写从读,写压力仍然很大前端只能加上缓存
3.水平分区
一致性模型
1.强一致性 所有的改变都会原子性的立刻生效
2.顺序一致性 每个客户端都会看到对统一数据的顺序操作,但不一定是实时的
3.因果关系一致性 对于存在因果关系的操作会看到相同的顺序,无因果关系的则会并行进行
4.最终一致性 当没有数据更新时,所有的数据通过网络传播复制使得最终达到一致
5.弱一致性 对于数据更新传播没有包含,使得各客户端看到的数据不一致
Nosql的维度
1.数据模型
2.存储模型
3.一致模型
4.物理模型
5.读写性能
6.二级索引
7.失败处理
8.压缩
9.负载均衡
10.原子的读修改写
11.锁等待和死锁
构建块
table,rows,columns,cell,类似这种结构:
SortedMap<RowKey, List<SortedMap<Column, List<Value,Timestamp>>>>
自动分片,存储API
HBase:Hadoop数据库
hbase和bigtable对比
HBase Bigtable
region tablet
regionserver tablet server
flush minor compaction
minor compaction merging compaction
major compaction major compaction
write-ahead log commit log
HDFS GFS
mapreduce mapreduce
memstore memtable
HFile SSTable
zookeeper chubby
第二章 安装
需求
最好是商用pc,而不是桌面pc,内存要大,因为region节点需要大量内存,cpu最好4核以上
master和slave机器可以配置相同也可以不同,master要有更高的可靠性
CPU
master 2.5G(4核)
slave 2.5G(4核)
内存
namenode 8G
secondary namenode 8G
job tracker 2G
hbase master 4G
datanode 1G
task tracker 1G
region server 12G
zookeeper 1G
主从机器物理内存最好超过24G
磁盘
master 4 * 1TB STAT,raid0+1
slave 6 * 1TB STAT,jbod
从IOPS考虑,可以将4*1TB换成8*500G,这样可以提高一倍的IOPS
机架
master 1000M带宽,双PSU,1U或2U
slave 1000M带宽,单PSU,1U或2U
操作系统
建议选择CentOS或Red hat
文件系统
有ext3,ext4,XFS,ZFS,建议选择更高的ext4或XFS
SSH可以不用安装
域名服务器 ping -c
时钟同步服务器NTP
文件句柄上线
lsof -p region_server_pid
datanode处理线程
dfs.datanode.max.xcievers 调整到4096
调整交换分区
挂起进程可能会导致zookeeper超时,设置/etc/sysctl.conf
vm.swappiness=5
cat /proc/sys/vm/swappiness
HBase的文件系统
本地文件系统
HDFS
S3
其他,如CloudStore,KFS
安装选择
源码安装
mvn assembly:assembly
mvn -DskipTests assembly:assembly
运行模式
独立模式
伪分布式
完全分布式
name: hbase.rootdir value: hdfs://namenodeip:9000/hbase
name: hbase.cluster.distributed value: true
配置
hbase-site.xml
优先hbase-site.xml
其次hbase-default.xml
再是hadoop相关xml
hbase-env.sh
regionservers
log4j.properties
部署
脚本部署
apache whirr
puppet和chef
第三章 客户端API的基本操作
这一章将要讨论的是HBase提供的客户端API。按照之前的介绍,HBase是用java语言编写的。但是这并不意味着必须用java客户端去访问HBase。事实上,在第六章我们将介绍如何用其他语言访问HBase。
通用性介绍
操作HBase的主要接口在org.apache.hadoop.hbase.client中,使用HTable类
1.在一个客户端的生命周期中使用一个HTalbe使用,因为创建HTable花销比较大,会先扫描.META.表
2.如果要在多线程中使用HTable,推荐使用HTablePool
3.对每一行的更新操作都是原子的
CURD操作
1.Put
用Put这个类做更新操作
有一个工具累Bytes,可以将各种类型的值转换为byte[]
Put#add(byte[] famliy,byte[] qualifier,long ts,byte[] value)
基本的add()操作
另外add()支持放入KeyValue这个对象
KeyValue是HBase API中最低级别的类,这个类的数据格式,就是HBase中存储的字节格式,所以
如果对性能要求很高的话,可以直接对这个类进行操作
has(byte[] family,byte[] qualifier)
这个类似迭代中的检查,检查某一个指定的cell是否存在
Put的父类中,有一些API
getRow()
getRowLock()
getLockId()
getTimeStamp()
heapSize()
通过Configuration创建一个默认配置实例,它会从classpath查找hbase-default.xml和
hbase-site.xml,使用Configuration.create(config)指定一个配置,这样优先级更高
HBaseConfiguration继承并兼容Configuration
可以手动指定一些属性,这样优先级最高,会覆盖配置文件中的属性
KeyValue
字节级别的操作,比对象级别的操作更有效,这是HBase提供的可以访问数据格式内部的类
从构造函数就可以看出,是对底层KeyValue结构的一个封装,可以直接访问byte[]
提供了一些比较类(实现了Comparator接口)
toString()返回当前的KeyVlaue的元信息,格式如下:
<row-key>/<family>:<qualifier>/<version>/<type>/<value-length>
Hbase有一个版本的概念,所有的版本都是按时间降序排列的,所以拿到的就是最新版本
如有一个test表
create 'test','name'
put 'test','row1','name','value1'
put 'test','row1','name','value2'
scan 'test' 执行之后会发现只有一条数据,即第二个put将第一put的值覆盖了
如果执行scan 'test',{VERSIONS=>3}
就看以检索出两个数据,将'value1'和'value2'都检索出来
客户端缓存
HTable#setAutoFlush()可以设置缓存
可以强制刷新缓存flushCommits()
HBase客户端会将缓存的内容排序后,put到相应的region server上
可以设置缓存大小: setWriteBufferSize()
计算服务端的内存使用:
hbase.client.write.buffer * hbase.regionserver.handler.count * region server数量
在没有刷新缓存之前,Htable#put()的内容都在内存中,此时如果去查找,则获取不到值
Put多个值
put<List<Put> 操作
使用HTable#put(List<Put>) 这种方式提交,假设有4个Put对象,其中一个Put对象所对应
的列不存在,则其他的3个Put对象是可以插入到表中的。
可以通过try-catch的方式捕获这些异常,然后在catch中使用flushCommits(),当然不使用
flushCommits()也可以插入成功。
原子更新
checkAndPut(byte[] row,byte[] family,byte[] qualifier,byte[] value,Put put)
服务端实现的原子操作
如果某一列不存在,则制定value为null即可
2.Get
在0.94版本中构造函数 Get(byte[] row,RowLock lock)已经不存在了
Get类的其他方法
setFilter()
GetCacheBlocks()
numFamilies()
Bytes工具类也提供了一些方法,可以将byte[]转换为具体的java类型
Result类
提供了一些方法去检索键值对
KeyValue覆盖了toString()可以方便的dump
Get多个值
和Put不同,当批量Get的时候,如果其中一个Get所对应的列不存在,则整个批量Get就执行失败,不会返回结果
涉及的检索方法
exist()
getRowOrBefore(byte[] row,byte[] family)
根据row key和family返回一个Result,如果指定的row key不存在,则返回这个key的前面的一个值
3.Delete
deleteFamily(byte[] family)
deleteColumn(byte[] family,byte[] qualifier)
deleteColumns(byte[] family,byte[] qualifier)
这些操作可以删除多个列,如果指定了时间戳,则会删除较老的时间戳的多个列
如果指定一个时间戳,但是这个时间戳不存在,则什么都不会发生
Delete多个值
使用HTable#delete(List<Delete>) 这种方式删除,假设有4个Delete对象,其中一个Delete对象所对应
的列不存在,则其他的3个Delete对象是可以从表中删除的。
可以通过try-catch的方式捕获这些异常
原子删除
类似Put的原子更新,值必须存在才可以删除,如果删除的列不存在则抛出一个异常
批量操作
Get,Delete,Put的父类Row,通过方法batch(List<Row> )支持多个不同的操作
但需要注意这些操作之间可能会互相影响
批量操作会返回四种结果:
1.null 比如远程操作失败
2.空结果 如Put和Delete操作成功
3.Result对象,执行Get操作成功,如果没有查找到结果则返回空
4.抛出一个客户端的异常
有两个方法:
batch(List<Row>)
batch(List<Row>,Object[])
另外批量操作还支持回调
行锁(0.94稳定版中已经没有这个类了)
put(),delete(),checkAndPut()都是一些排斥操作,可以显示的指定一个锁
RowLock,并在创建Delete,Put时指定这个锁
当多个客户端同时操作一行时,第一个使用锁的客户端就会占有这行,之后所有的客户端必须等待第一个操作完才可以
继续访问
锁的超时时间可以通过hbase-site.xml配置
Get也有一个锁,但实际上服务端使用 "多并发版本控制" 的方式去实现的
使用行锁要小心死锁的问题
Scans
类似数据库的游标
有以下一些方法:
setStartRow(byte[] startRow)
setStopRow(byte[] stopRow)
setTimeRange(long minStamp, long maxStamp)
setFilter(Filter filter)
ResultScanner
通过HTable#getScanner()可以获得一个ResultScanner对象,它包含三个方法
void close()
Result next()
result[] next(int nbRows) 返回nbRows个Result对象
Scanner可能会返回很多Result对象,操作这些对象的时候需要注意尽可能短的释放他们,调用close()或者在
hbase-site.xml中配置
缓存和批处理
设置cache大了可以提高性能,但是设置过大,每一次调用next()都会获取很多数据,甚至会导致OOM
读取时间过长可能会超时异常,可以在客户端代码中设置
Configuration#setLong(HConstants.HBASE_REGIONSERVER_LEASE_PERIOD_KEY,num)
这个设置只对客户端有效,服务端需要修改hbase-site.xml才可生效
cache是针对行的,而batch是针对列的,比如有17列,batch设置为5,则依次返回5,5,5,2 四次
RPC次数 = (行 * 每行的列) / Min(每行的列,批处理大小) / 缓存数
如果没有设置批处理数,则返回的一个完整的行
其他特性
1.HTable工具方法
close()
getTalbeName()
getTableDescriptor()
getRegionLocatioin(Byte[] row)
getRegionsInfo()
2.Bytes类
提供了java类型到byte[]类型的转换,同时也提供了byte[]转换为java类型
支持String,boolean,short,int,long,float,double
第四章 客户端API的高级特性
Filters
通过客户端定以后的filter,经过RPC调用,在服务端执行
接口Filter,抽象实现FilterBase
有比较的Filter,专用的Filter,装饰的Filter,Filter列表几大类
比较操作:(在类CompareFilter.CompareOp 中定义)
1.LESS
2.LESS_OR_EQUAL
3.EQUAL
4.NOT_EQUAL
5.GREATER_OR_EQUAL
6.EREATER
7.NO_OP
比较的实现类(继承自抽象类ByteArrayComparable)
1.BinaryComparator
2.BinaryPrefixComparator
3.NullComparator
4.BitComparator
5.RegexStringComparator
6.SubstringComparator
需要注意BitComparator,RegexStringComparator,SubstringComparator只能用于EQUAL或者NOT_EQUAL操作,用于其他操作会出错
基于比较的Filters
1.RowFilter
可以用于之前的各种比较实现类,实现二进制比较,正则表达式,子串等
2.FamilyFilter
基于列簇的比较
3.QualifierFilter
基于限定符的比较
4.ValueFilter
基于值的比较
5.DependentColumnFilter
精确定位列簇和限定符
专用的Filters
1.SingleColumnValueFilter
可以设置setFilterIfMissing(true) 返回所有的列,还是返回只查找到的列
2.SingleColumnValueExcludeFilter
和SingleColumnValueFilter类似,只是如果满足条件,则不返回指定的列(无论是返回多列还是单列)
3.PrefixFilter
根据key的前缀查找
4.PageFilter
可以返回指定个数的行,配合startRow可以达到分页的效果
5.KeyOnlyFilter
只返回key,如果指定构造函数true,则返回value的length
6.FirstKeyOnlyFilter
只返回第一列
7.InclusiveStopFilter
通过和startRow配合使用,可以指定一个范围,当扫描到指定的filter就停止
8.TimestampsFilter
限制timestampe范围,可以指定一个List<Long>,包括了需要检索的timestampe,Scanner可以指定检索的范围
9.ColumnCountGetFilter
一次能检索的最大的列数
10.ColumnPaginationFilter
可以从指定的偏移量开始,选择若limit个列,这个filter适合列非常多的表
11.ColumnPrefixFilter
类似PrefixFilter,这个filter是取cloumn的前缀
12.RandomRowFilter
内部使用java.util.Random#nextFloat(),返回一个随机的行
如果值为负数则不返回任何行,如果大于1.0则返回所有行
Decpratomg Filters
装饰filter,配合其他filter一起使用
SkipFilter
如果创建ValueFilter(CompareOp.NOT_EQUAL,new BinaryComparator(Bytes.toBytes(0))
SkipFilter包装了ValueFilter后,如果发现某一行不包含0,则全部跳过,而不是像VluaeFilter
那样,只是把对应的列跳过,SkipFilter跳过的是一整行
WhileMatchFilter
比如RoWFilter选择不匹配第五行,那么最终Scan会得到除第五行以外的其他行
而whileMatchFilter会匹配到第四行之后,发现第五行不匹配就终止了
FilterList
可以包含多个Filter,可以传入List<Filter>,如果传入的是ArrayList,就按照加入的顺序执行各个filter
默认是MUST_PASS_ALL,还有一个是MUST_PASS_ONE,即至少返回一个结果
自定义的Filter
可以实现Filter接口或者继承FilterBase类
内部枚举类RetrunCode定义了一些具体动作
1.INCLUDE
2.SKIP
3.NEXT_CLO 如TimestampFilter
4.NEXT_ROW 如RowFilter
5.SEEK_NEXT_USING_HINT 如ColumnPrefixFilter
具体的一些执行方法:(按照执行先后顺序)
1.filterRowKey(byte[] buffer, int offset, int length)
2.filterKeyValue(KeyValue v)
3.filterRow(List<KeyValue> kvs)
4.filterRow()
5.reset()
6.filterAllRemaining()
首先会执行filterRowKey(),判断row是否满足,RowFilter就是按照这种逻辑去实现的
如果不满足则执行FilterKeyValue(),这回返回一个RetrunCode类型,指明下一步需要的操作
filterRow(List) 根据前面的结果再做进一步过滤
filterRow()会根据结果判断是否停止执行, PageFilter就是用这个方法去实现的
修改后的jar需要部署到服务端才可以执行,可以修改hbase-env.sh指定classpath,或者放到lib目录下
修改后需要重启才能生效
Counters
hbase提供的一种功能,可以原子的更新计数
可以用于广告,数据统计等
创建一张表后,计数器的语法为:
incr 'table','row','column',[increment-value]
获取方法:
get_counter 'table','row','column'
incr可以使用三种值:
1.大于0,用于增加
2.等于0,类似get_counter,可用于检索
3.小于0,用于减去一个值
注意,写操作是有锁保护的,但是读操作可能没有
当多个counter更新相同的行可能会有问题
单行counters
HTable#incrementColumnValue()
多行counters
Increment
Coprocessors
类似关系数据库的存储过程,可以通过实现指定的接口完成任意代码
有这么几种类型:
Observer
1.RegionObserver
2.MasterObserver
3.WALObserver
Endpoint
Coprocesor有两个枚举类型,Priority和State
Priority:(0.94版本做了一些修改,增加了最高和最低级别)
1.SYSTEM
2.USER
系统级别会在用户级别前执行,相同优先级通过定义的数字大小按顺序执行
Coprocessor包含start()和stop()方法,并且包含CoprocessorEnvironment实例
Coprocessor.Stat包含以下状态:
1.UNINSTALLED
2.INSTALLED
3.STARTING
4.ACTIVE
5.STOPPING
6.STOPPED
三个重要的类,Coprocessor,CoporcessorEnvironment,CoprocessorHost
Coprocessor装载
在hbase-site.xml中增加coprocessor相关配置:
1.hbase.coprocessor.region.classes 值就是自定义的class,中间用逗号分隔
2.hbase.coprocessor.master.classes 同上
3.hbase.coprocessor.wal.classes 同上
加载表描述,用于每个region server,但是不能用户WAL和master
格式为:(必须以COPROCESSOR开头)
<path-to-jar>|<classname>|<priority>
如:
alter 'testTable' METHOD=>'table_att',
'COPROCESSOR$1'=>'hdfs://ip:54310/myhbase/test.jar|test.coprocessor.Test|SYSTEM'
RegionObserver class
preGet()方法如下:
preGet(ObserverContext<RegionCoprocessorEnvironment> e,Get get,List<KeyValue> results)
RegionCoprocessorEnvironment 上下文可以获得当前运行时相关的表配置,表信息,RegionServer信息等
更改配置后需要重启
MasterObserver class
类似RegionCoprocessor,也包含了MasterCoporcessorEnvironment
类似数据库中的DDL,在DDL之前之后会有一些触发操作被回调
Endpoint
RegionObserver是在单个server上执行的,也是单个region,但如果要计算表的行数,那么就需要依次对每个region
都操作一遍,代价太大
Endpoint可以对表中的每个region执行一个操作并返回,这个操作是在服务端执行的,所以传输的数据就会小很多
Endpoint类似数据库中的sum()函数
具体部署方式和RegionObserver一样,也需要配置hbase-site.xml
客户端通过回调方式收集所有的region返回的结果,这个有点像微型的MapReduce
回调的接口在org.apache.hadoop.hbase.client.coprocessor.Batch中,是Batch的内部接口
HTablePool
在高并发的环境中创建HTable是一个耗时的操作
HTable也不是线程安全的,最好在每个线程中使用一个HTable实例
HTablePool构造函数中最大数并不是控制最多可以同时获取多少个HTable实例,而是缓存中的HTable实例个数
如果超过了这个数,就新实例化一个并返回,一样可以使用
构造函数中提供了一个HTableInterfaceFactory,自定义的创建HTableInterface实例
注意:在0.94之后又提供了新的构造参数,一个枚举类型,指定池的类型,有三种:
1.Reusable
2.RoundRobin
3.ThreadLocal
连接处理
很多连接都是会被缓存的,如访问zookeeper的连接,这样可以避免重复连接
HTalbe的构造函数支持Configuration对象,这样就可以做到多个表共享连接了
但是缺点就是连接不会主动释放,只有客户端退出了才会释放
可以手动释放连接
第五章 管理特性
模式定义
HTableDescriptor
我们讨论的很多类都有一个无参的构造函数,因为这些类实现了Writable接口,这是为远程序列化考虑的
实现自定义的类需要保证如下:
1.实现Writable接口
2.提供一个无参数的构造函数
表的属性
表的名字必须是拉丁字母的,用正则表达式表示为:
[a-zA-Z_0-9-.]
默认的文件大小是256M,超过就是执行分割
默认是非只读的,可以指定表为只读
memstore flush默认为64M
是否打开预写日志
列簇
严格来说叫HColumnFamilyDescriptor更合适
family的名字必须是可打印的字符,而qualifier可以是任意字符
family不能重命名,只能创建一个新的
最大版本为3,可以查找到以前删除的历史
压缩类型
1.NONE
2.GZ (gzip)
3.LZO
4.SNAPPY
block大小,默认为64K,这个相当于缓存,hadoop的block大小是用指定单个文件大小
block缓存默认是开启的
支持一个版本的TTL,如果超时的话在块合并的时候会被删除
可以支持将列完全放入内存中,当达到JVM内存上限时会丢弃多余的数据
布隆过滤可以大幅度提高检索时间,但是也有误识别的缺点,默认是关闭的,可用key和key+(family+qualifier)的组合
复制范围,默认关闭为0,表示本地复制,设置为1表示集群复制
HBaseAdmin
类似数据库的DML
集群操作
flush()
compact()
majorCompact()
move()
blance()
集群状态信息
通过HBaseAdmin获得ClusterStatus
ServerName 服务端机器信息
HServerLoad 服务状态信息
RegionLoad region相关状态信息
WebUI上可以观察到这些信息
第六章 可用的客户端
交互式的客户端
REST
启动方式./hbase-daemon.sh start rest
文本方式
curl -H "Accept:text/plain" http://IP:8080/ppp/aa1
xml方式(默认格式)
curl http://IP:8080/ppp/aa1
json方式
curl -H "Accept:application/json" http://IP:8080/ppp/aa1
protocol buffer方式
curl -H "Accept:application/x-protobuf" http://IP:8080/ppp/aa1 | hexdump -c
raw binary方式
curl -H "Accept:application/octet-stream" http://IP:8080/ppp/aa1 | hexdump -c
REST java client
Cluster cluster = new Cluster();
cluster.add("IP",8080);
Client client = new Client(cluster);
RemoteHTable table = new RemoteHTable(client,"test");
//..之后的get,delete,put,scanner操作都和普通的方式一致
Thrift
也是跨语言的,有thrift和thrift2两个版本,用C++编写的
启动方式./hbase-daemon.sh start thrift
模式文件定义在 $HBASE_HOME/src/main/resources/org/apache/hadoop/hbase/thrift/下
Hbase.thrift文件
生成php文件支持:(假设已经安装好thrift)
thrift -en php Hbase.thrift 之后产生一个gen-php目录
cp /lib/php/src $DOCUMENT_ROOT/thrift
mkdir $DOCUMENT_ROOT/thrift/packages
cp -r ~/thrift_scr/gen-pp/Hbase $DOCUMENT_ROOT/thrift/packages
HBase目录下的src/examples/thrift目录下有php的列子
Avro
和thrift类似,也是跨语言的
启动方式./hbase-daemon.sh start avro
模式文件定义在 $HBASE_HOME/src/main/java/org/apache.hadoop/hbase/avro/hbase.avpr
其他客户端
jruby
HBql
HBase-DSL
JPA/JPO
PyHBase
AsyncHBase
批处理客户端
HIVE
PIG
Cascading
创建和hbase管理的表
create table hbase_table(key int,value string)stored by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' with serdeproperties("hbase.columns.mapping"=":key,cf1:val")
tableproperties("hbase.table.name"="hbase_table")
关联已经存在的hbase表
create external table hbase_table_2(key int,value string)stored by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' with serdeproperties("hbase.columns.mapping"=":key,cf1:val")
tableproperties("hbase.table.name"="hbase_table_2")
关联外部表时,如果在hive中删除了这张表,外部表不会被删除
Shell
开启debug, 查看是否开启debug?
jruby脚本
org.apache.hadoop.hbase.util.Bytes.toInt("\x00\x01\x06[".to_java_bytes)
java.text.SimpleDateFormate.new(yyyy/MM/dd HH:mm:ss").parse("2012/12/21
20:56:29").getTime()
for i in 'a'..'z' do for j in 'a'..'z' do put 'test',"row-#{i}#{j}","colfam1:#{j}","#{j}" end end
require 'date'
一个xx.rb脚本 ,运行方式./hbase org.jruby.Main xx.rb
include java
import org.apache.hadoop.hbase.HBaseConfiguration
import org.apache.hadoop.hbase.client.HBaseAdmin
conf = HBaseConfiguration.new
admin = HBaseAdmin.new(conf)
tables = admin.listTables()
tables.each {|table| put table.getNameAsString() }
webUI
配置hbase-site.xml可以调整webUI端口
hbase.master.info.port
hbase.regionserver.info.port
masterUI 和 regionUI
通用功能
Local logs
Thread Dump
Log Level
Debug dump
第七章 整合MapReduce
map-reduce可以处理TB级别数据,将大任务分解为多个小任务到多台机器上,执行完结果后再汇总
InputFormat
getSplits()
createRecordReader()
Hbase提供了TableInputFormatBase作为其扩展实现
Mapper的每一个记录使用RecordReader处理map()
Hbase提供了TalbeMapper作为其扩展实现
Reducer阶段汇总Mapper的数据
Hbase提供了TableReducer作为其扩展实现
OutputFormat 最终的数据输出
Hbase提供TableOutputFormat作为其扩展实现,用TableRecordWriter写入到指定表中
TableOutputCommitter需要hadoop类是完成job工作
使用TableMapReduceUtil设置map-reduce工作
hdfs使用三副本的形式,map-reduce选择三台机器之一运行
TableInputFormat#getSplits()等于所有region中的startKey和endKey之间数据,然后做切分
之后TableRecordReader会迭代的对region的startKey和endKey的每行数据进行处理
在MapReduce之上运行HBase
静态导入
需要将hbase的jar导入到hadoop的classpath中,此外要配置mapre-site,需要重启节点
动态导入
使用maven引入需要的jar,使用hadoop jar xx.jar的方式运行
注意guava等jar也需要引入到classpath中来
Data Sink(下沉)
实现Mapper接口,其setup()函数是只调用一次,map()是每行都会被触发调用一次
最终调用TableOutputFormat,它会将map之后分解的数据写入到表中
因为写入的不是中间文件而是hbase的表,所以结果是有序的,这样就不需要reduce了
Data Source(来源)
一个统计单词的例子,从Result中读取每个columns,然后解析作者名字,记录+1并设置到上下文中
之后reduce过程对其+1
最后汇总打印出来
辅助工具类TableMapReduceUtil
自定义过程
可以将输出写到两个不同的表中,在setup()函数中定义A和B两个表
在map()中判断key的内容,如果是aa开头则插入到A表,否则插入到B表
第八章 架构
查找和传输
B+树 一个节点下包含了多个节点,这样可以降低IO读取的次数
但是B+树一个节点超过限制了,就会拆分,从而引起父节点拆分,这样可能引发很多个拆分,而且不同节点可
能分散在磁盘
的不同地方,不是连续的,这样性能就差了
B+树的优化就是将不连续的块重新写一遍,做到连续
LSM(Log-structured merge-tree)是先将数据写入到日志文件中,日志文件是有序的,当日志文件改变时,先
将改变的内容
保存到内存中,优化查询,当内存满了就将数据写到磁盘中,此时内存的数据可以丢弃,经过多次写之后,
会产生多个小文件,后台线程会将多个小文件合并成一个大文件,这样查询就被定位在几个文件中了
存储
当memstore大于hbase.hregion.preclose.flush.size(默认5M)就会刷新到磁盘上
hdfs路径 /hbase/.logs 会包含对应的子region名字的目录,每个region就对应一个子目录,对于一个region
目录是共享的
日志文件大小为0是因为hdfs内置的append机制使用追加写导致的,只有文件达到一定大小才是可见的
hbase.regionserver.logroll.period 设置日志滚动时间(默认60分钟)
当数据被写入到磁盘后,就不需要日志文件了,这样的文件会被放到 /hbase/.oldlogs 目录下,也是一个
region对应
一个目录
参数hbase.master.logcleaner.ttl控制多久之后旧日志被删除(默认10分钟)
参数hbase.master.cleaner.interval控制多长时间检查一次(默认1分钟)
根级文件(在HDFS中/hbase目录下)
/hbase/hbase.id 和 /hbase/hbase.version
这两个文件包含集群的唯一ID和文件格式版本信息
/hbase/splitlog 和 /hbase/.corrupt
第一个是日志拆分产生的中间文件,第二个是损坏的日志
表级文件
每个表都有自己的目录,每个表包含 .tableinfo 顶层文件和.tmp目录
第一个是用来存储表结构信息的,第二个是当新的.tableinfo生成的临时文件就放在.tmp目录下
region文件的总体结构:
<hbase-root-dir>/<tablename>/<encoded-regionname>/<column-family>/<filename>
ncoded-regionname是 表名,起始key,启动时间,md5编码 这些组成的
<hbase-root-dir>/<tablename>/<encoded-regionname> 这个目录下还有一个 .regioninfo文件
拆分
在父region的splitlog目录下拆分log,然后关闭当前region,不接受请求
META表会更新父region,显示SPLIT=>true,之后会出现两个信息
column=info:splitA 和 column=info:splitB
当两个子region初始化好之后META表删除父region,并删除文件,可以由负载均衡将新region移动到其他机
器上
整个过程都在zookeeper中进行跟踪
合并
minor合并和major合并
minor合并 hbase.hstore.compaction.min(默认为3)设置的,这个值最小要大于等于2
minor合并可以处理的最大文数 hbase.hstore.compaction.max来配置(默认为10)
hbase.hstore.compaction.max.size 任何比这个值大的文件都会排除在外
hbase.hstore.compaction.min.size 它是一个阀值而不是限制,它包括任何小于限制的文件,直到每次压
缩允许的
的总文件数量
hbase.hstore.compaction.ratio(默认为1.2即120%) 这个参数确保选择过程中尽可能的包含更多文件
主合并major由CompactionChecker类实现
周期由hbase.server.thread.wakefrequency参数控制(乘以
hbase.server.htread.wakefrequency.multiplier,
设置为1000,使得执行不那么频繁)
除非强制指定
HFile格式
<data blocks(多个)><meta blocks(多个)><fileinfo><data index><meta index><trailer>
Data格式:
<Magic><KeyValue><KeyValue>...
空的HFile格式为:
<fileinfo><trailer>
目前有V1和V2两种HFile格式,最新用的是V2格式,它支持多级索引
HFile中包含若干个块,每个块的大小默认是64K,建议设置到8K--1M之间,较大的块适合顺序访问但不适合
随机访问,
较小的快适合随机访问但是会造成索引过多
HFile块对于HDFS来说是透明的,一个HFile可能会被写入到多个HDFS块中(一个HDFS块默认64M)
这两种块之间没有任何关系
使用命令查看HFile状态
./hbase org.apache.hadoop.hbase.io.hfile.HFile
KeyValue格式
Key长度,Vluae长度,Row长度,Row,column family长度,column family,column qualifer,timestamp,key
type,value
WAL
类似mysql的binlog,在主存储器出现意外的情况下作为恢复使用
HLog类
所有的region都共享一个HLog,它使用追加写的方式
HLogKey
使用region的编码名,表名,long类型的递增序列号,集群的UUID表示key
WALEdit
每一个修改的实例都被封住到一个WALEdit中
LogSyncer(最新版没有这个类)
延迟日志刷新默认为false,意味着每一编辑被发送到服务器时,都会调用写日志的sync(),但这意味着
一对N的管道写,datanode是写三分,管道的方式写入,只有最后一个写成功返回才算成功。这样会带来很
大的延迟。可以选择稍微延迟这个调用,但是可能有一定几率造成数据丢失
HDFS以后会支持多路写,即一次向多个datanode写入数据,只要等最慢的一个写入完成就算成功,不过这样
会给占用很多带宽
属性hbase.regionserver.optionallogflushinterval(默认1秒)配置多久之后调用sync()
LogRoller(最新版没有这个类)
在特定的时间间隔内滚动日志,通过配置hbase.regionserver.logroll.period(默认1小时)来控制
hbase.regionserver.hlog.blocksize 设置文件系统的快大小(默认32M)
hbase.regioinserver.logroll.multiplier(默认为0.95)表示当日志大小达到块大小的95%会滚动日志
日志回放
1.只有一份日志,跟bigtable类似,这是为性能考虑的,多个region共享一个日志文件
2.日志的拆分,之前是由master负责的,串行的回复每个日志,同时每个region都会被阻塞
现在使用分布式模式注册到zookeeper上,master指定某个日志是可以被处理的,region就会竞争这个
任务
可以通过hbase.master.distributed.log.splitting来设置关闭新的分布式日志拆分
在非分布式下写入是多线程的,这是通过hbase.regionserver.hlog.splitlog.writer.threads来控制的(
默认是3)
hbase.hlog.split.skip.errors(默认为false)表示当出现错误时是否跳过,不跳过抛出IOException
拆分中的日志被放在splitlog目录下
为了能和其他日志区分开来并发操作,日志路径包含了region名(散列值)和recovered.edits文件夹
错误的不能恢复的日志被放在.corrupt目录下
3.数据恢复
一旦检查到recovered.edits目录便开始
持久性
底层的hadoop需要支持追加写,以前的hadoop是提供API向文件写入,写入之后才可以读取这个文件,并且
这个文件会变成只读,但是对于hbase可能会造成数据丢失
之后hadoop增加了追加写的功能,hbase调用syncFs()或hflush()管道的方式写入数据
读路径
小合并,大合并,控制HFile文件数量
旧的数据作为KeyValue存储在磁盘上,新的数据被刷新后也会被存储,但是不会修改原始文件,而是存储在
新文件中
删除的数据只是做一个墓碑标记,并不会真正删除,只有当合并的时候才会被删除
一个StoreScanner实例代表了一个Sotre,通过这个实例的next()扫描所有的数据并跳过被标记删除的数据
Region查找
Root表-->meta表-->用户表
最坏的情况可能需要六次查找
Region的生命周期
offline region下线
pending open 打开region的请求已经被发送到服务器
opening 服务器已经打开region
open region已经被打开并且完全可以使用
pending close 关闭region的请求被送到服务器
closing 服务器正在处理要关闭的region
closed region服务器已经被关闭了
splitting 服务器开始切分region
split region已经被切分了
zookeeper
./hbase zkcli 启动
ls /hbase 显示hbase的znode节点
get /hbase/hbaseid HDFS上hbase.id的内容
/hbase/master master节点内容
/hbase/root-region-root root表所在的region机器
/hbase/rs 所有的region server
/hbase/shutdown 集群的启动时间
/hbase/splitlog 日志拆分,日志首先是unassigned状态然后被一个region服务器获取
/hbase/table 所有的表
/hbase/unassigned 未打开的region
复制
类似Mysql的bin-log复制,使用异步方式
LogEdit生命周期
1.每次的更新操作都会被记录到WAL中,另一个线程从日志中读取,当缓冲区满了或者到文件末尾了就将
内容发送到从集群
2.如果从集群没有反馈则重试若干次如果还是没有反馈则重新选择一台从集群备份
内部机制
1.挑选需要复制的目标服务器(挑选的数目为从集群个数的10%)
2.每个从集群都有对应的znode,每个znode都包含HLog。文件在可用之前被添加到每个集群的znode中,
这样所有的更新操作都会复制到自己的集群中
3.默认情况下会过滤掉分为GLOBAL类的且不属于目录表日至项的KeyValue,之后是限制每个从集群的同步
大小,默认是64M,所以三个从集群就是192M
4.如果没有启用同步复制,master日志清理线程会按照TTL删除旧的日志,如果当前日志在队列中则不会
删除
5.所有的对等服务器都放在
当有服务器宕机了,其他服务器会竞争的创建一个lock的znode,并将这个服务器的内容转移到自己的
目录下
下面假设有3个region,从集群id为2,zonde名字就是HDFS的文件名
/hbase/replication/rs/
1.1.1.1,60020.1234567890/
peers/
2/
1.1.1.1,60020.123
1.1.1.2,60020.1234567890/
(lock)
peers/
2/
1.1.1.2,60020.1214
1.1.1.3,60020.1234567890/
(lock)
peers/
2/
1.1.1.3,60020.1280
将1.1.1.2的内容移到这里
第九章 高级使用
Key的设计
KeyValue
row ColumnFamily ColumnQualifier Timestamp Value
越往后定位的性能就越差
每个column对应一个真实的文件,qualifier是和column存储在同一个文件中的,比如column为ff,quailfier为aa
则存储在实际的文件中的内容是ffaa
高窄的表 VS 短宽的表
1.假设e-mail的消息被放到了quailifer中,row key是用户id,这样如果用户不多,但是用户消息很多,会造成
行不多,但是单行的内容很多(有很多不同的qualifier),这样的设计很不好,因为HBase的split是按照行分割的,
qualifier过多,会造成这行内容过大(甚至超过了region大小),但HBase又不能对其split
2.如果将e-mail中的消息放到row key中,做成userId-message,这样每行的qualifier就是空的,每个row key就只有
一条数据了,但是行很多,这样的好处是HBase可以进行split
局部key扫描
key可以设计成这样的:
userid-date-messageId-attachmentId
因为timestamp是反序排列的,所以可以设计成:
Long.MAX_VALUE - <date-as-long>
时间连续数据
假设有N个region server,设计key为:
byte prefix = (byte)(Long.hashCode(timestamp) % <N of region>
byte[] rowkey = Bytes.add(Bytes.toBytes(prefix),Bytes.toBytes(timestamp);
那么key为:(假设有5个region server)
0myrowkey-1
4myrowkey-2
2myrowkey-3
这种设置的好处是分散了写的压力,可以让key分散到不同region server上,如果要加机器,客户端逻辑需要修改
另外这种方式对读不是很好,尤其是scan需要遍历多个region,可以用多线程处理,这样可以提供IO效率
交换key
前面一个列子,将timestamp和<N of regionserver>互换,这样可以提高写效率
OpenTSDB采用类似的方式<metric-id><base-timestamp> 这种格式作为key
随机key
byte [] rowkey = MD5(timestamp) 可以将key均衡的分散到多个region server上
对于写来说
顺序的key,按region取模分散key,时间顺序话-取摸的key,MD5随机化key 效率依次递减
对于写来说,正好相反,效率依次递增
时间有序
某个列index,可以对某个qualifier主题做降序排列,对某个qualifier发件人做升序排列
时间的降序排列:
Long.MAX_VALUE - <date-as-long> 这样越早的时间值越大,就在前面了
高级模式
跨语言的IDL(interface definition language)
avro
protocol buffers
二级索引
客户端自己管理
1.管理多个表,很灵活,索引存放到另外一张表中,但这种方式可能会造成数据不一致的问题
2.基于列簇的方式,通过列的Qualifier,这样可以保证事务性
这种方式先扫描row key,然后根据qualifier再更小的范围,之后根据version找出结果
ITHBase
IHBase
这两种都是差不多的,扩展了服务端代码,客户端需要使用额外的代码来操作索引
Coporcessor
在服务端做扩展,和IHBase类似,但是不需要替换任何服务端和客户端的代码
查询整合
客户端管理,facebook实现方式
1.每一行对应一条消息
2.列索引消息
3.版本是message id
4.值包含了附加信息,比如文档的位置
Lucene
HBasene
都是基于Lucene,存储的索引放在HBase中
Coprocessor
实现方式类似二级缓存
事务
ITHBase扩展了服务端和客户端的代码,提供了事务功能
Cage也提供了多个lock的功能
zookeeper本身也有两阶段提交,父znode下包含了多个子znode,每个子znode会标记是否成功或失败,客户端可以监控
其状态
Bloom filter
如果执行随机查找,在HFile发现key在数据索引中,则会执行一遍scan,发现没有再下一个HFile,一直到结束,可能会遍历多个HFile中的块,如果有bloom过滤器,可能只需要遍历很少几个HFile中的块即可
bloom过滤器需要原始文件的1/20大小,即文件1G,bloom过滤器占用50M空间
bloom有row和row+column两种类型,优先使用row过滤器
bloom会有判断错误率,默认1%,可以设置
即认为某个key在这些块中,但是实际上不存在的; 但如果认为key不存在,那么肯定是不存在
使用过程
1.判断cell的大小,如果小于阀值则不适用过滤器
2.如果cell数量超过阀值则使用row过滤器
3.检查读取模式如果是row则使用row模式,否则使用row+column模式
版本
需要保证各个region server的时间一致,通过NTP(network time protocol)保证
如果各resion server时间不一致,某台机器时间比其他机器早一个小时,时间戳使用系统时间,当这台机器split,并
将一半的region移动到另一台机器B,之后又执行插入到机器B,此时新插入值的时间反而比旧值的时间晚,这样按照
时间排序取出的版本就是旧值的数据
默认的版本为3,最大版本为2^31-1. 如果一个cell的key,column,quailifer都一样,时间戳不同,那么会按照最新的三个
版本递减排序,最高的在最上面.插入6条数据(其他都一样,只有时间戳为1-6),那么1-3时间戳的数据不会被显示,但是
也不会被马上删除,如果查看硬盘上的HFile文件,数据还是存在的,当执行了一次major_compact之后,这些过期的数据
就会被真正删除
如果删除数据时没有指定版本,默认为当前时间,小于当前时间的所有值都会被删除,如果新增的数据时间戳小于当前
时间,就查询不到了,这样新增不就成功,flush,major_compact之后,新增的旧值就可以插入了
自定义的版本
1.facebook inbox search,根据时间戳排序,这样就可以取到最新的数据了
2.使用一个全局的递增版本
第十章 集群监控
Metrics框架
继承自hadoop的metrics框架,将自身的信息发送给外部监控者
上下文,记录,度量
hbase提供了下列Context:
1.GangliaContext
2.FileContext
3.TimeStampingFileContext
4.CompositeContext
5.NullContext
6.NullContextWithUpdateThread
完整的度量全限定名为:
<context-name>.<record-name>.<metric-name>
度量框架包括以下类型
1.int型(IV)
2.long型(LV)
3.比率(R) 一段时间内的变化
a)比率计算是 操作数/花费时间
b)比率存储的是上一次的值
c)内部的计数器恢复到0
d)下一次的比率值才是当前值
e)计算后的比率值会返回给调用者
4.String型(S)
5.时间变化int型(TVI) 一段时间内的累计值
6.时间变化long型(TVL) 同上
7.时间变化比率(TVR) 计算一段时间完成的操作比列
a)操作个数 上一次执行后的总操作数
b)最小时间 一个时间完成的最短时间
c)最大时间 一个事件完成的最长时间
d)平均时间 事件完成的平均时间
8.持久的时间变化比率
对于长时间运行的处理,可以通过配置 hbase.extendedperiod配置,默认是不过期,如设置一个小时则为3600
master度量
1.集群请求(R) 所有region的请求和
2.重启后分割预写日志的时间(PTVR)
3.预写日志文件的总大小(PTVR)
region server度量
1.块缓存度量 记录总数,大小,空闲,失效的度量,以及命中数和未命中数,以及命中比列
2.压缩度量 压缩大小,压缩时间,压缩队列大小
3.memstore度量 memstore大小度量,flush队列大小,flush时间
4.存储度量 所有存储文件的状态度量
5.I/O度量 读写等待时间度量
6.杂项度量 读请求数度量,写请求数度量,regionserver数度量
压缩度量和memstore度量是在更新之间统计的,所以统计的值对于当前的值是有些延迟的
RPC度量
master和region使用相同的度量
1.RPC处理时间
2.RPC队列时间,RPC将所有的处理放在队列中,在队列中等待直到被取出处理的时间
JVM度量
1.内存使用度量 堆和非堆的使用情况
2.垃圾收集度量 垃圾收集时间,垃圾收集次数
3.线程度量 各种线程的状态
4.系统时间度量 各种子系统的日志信息,比如日志错误度量提供错误级别出现的时间
Info度量
提供各种内部值
1.date Hbase build时间
2.version hbase版本
3.revision 仓库的构建修订版
4.url 仓库的url
5.user 构建hbase的user
6.hdfsDate HDFS的构建时间
7.hdfsVersion 当前使用的hdfs版本
8.hdfsRevision 仓库的修订版构建的hdfs
9.hdfsUrl hdfs仓库url
10.hdfsUser 构建hdfs的user
Ganglia
Ganglia monitoring daemon(gmond) 每个机器都运行一个
Ganglia meta daemon(gmetad) master机器运行
Ganglia PHP web frontend web前端
安装好之后需要设置hadoop-metrics.properties
JMX
在hbase-env.sh中开启
配置hadoop-metrics.properties
hbase.class=org.apache.hadoop.metrics.spi.NullConetextWithUpdateThread
MBean地址:
hadoop:service=<service-name>,name=<mbean-name>
如:
hadoop:service=Master,name=MasterStatistics
hadoop:serivce=HBase,name=RPCStatistics-<port>
JConsole
JMX 远程API
git clone git://github/larsgeorge/jmxtoolkit.git
运行:
java -cp build/hbase-jmxtoolkit.jar org.apache.hadoop.hbase.jmxtoolkit.JMXToolkit -h
可以整合Cacti
Nagios
配合JMXToolkit脚本一起使用
第十一章 性能调优
垃圾收集
GC调优使用CMS垃圾收集器,可以调整垃圾收集的出现阀值(比如70%)
打印详细的日志输出
注意block cache(处理读)和memstore cache(处理写)的大小比列
memstore-local allocation buffer
MSLAB的实现原理(对照Arena Allocation,HBase实现细节):
创建一个2M(默认)的Chunk数组和一个chunk偏移量,默认值为0。
当Memstore有新的KeyValue被插入时,通过KeyValue.getBuffer()取得data bytes数组。将data复制到Chunk数组起始位置为chunk偏移量处,并增加偏移量=偏移量+data.length。
当一个chunk满了以后,再创建一个chunk。
所有操作lock free,基于CMS原语。
优势:
KeyValue原始数据在minor gc时被销毁。
数据存放在2m大小的chunk中,chunk归属于memstore。
flush时,只需要释放多个2m的chunks,chunk未满也强制释放,从而为Heap腾出了多个2M大小的内存区间,减少碎片密集程度。
开启MSLAB
hbase.hregion.memstore.mslab.enabled=true // 开启MSALB
hbase.hregion.memstore.mslab.chunksize=2m // chunk的大小,越大内存连续性越好,但内存平均利用率会降低,要比插入的单元格的数据大一些。
hbase.hregion.memstore.mslab.max.allocation=256K // 通过MSLAB分配的对象不能超过256K,否则直接在Heap上分配,256K够大了。
压缩
GZIP,LZO,Zippy/Snappy
GZIP最慢,Snappy最快
校验压缩是否安装成功
./hbase org.apache.hadoop.hbase.util.CompressionTest <path> <none|gz|lzo|snappy>
配置hbase-site.xml
<property>
<name>hbase.regionserver.codecs</name>
<value>snappy,lzo</value>
</property>
启用压缩
create 'testtable',{NAME=>'clof1',COMPRESS=>'GZ'}
启用压缩后并不是马上生效,因为数据还在buffer中,除非手动触发major_compact
优化切分和压缩
hbase.hregion.max.filesize 单个region的大小(整个集群范围)
或者可以指定某个column
预创建regions
./hbase org.apache.hadoop.hbase.util.RegionSplitter <TABLE>
或者用命令行
create 'testtable' 'colf',{SPLITS=>['row-100','row-200','row-300']}
负载均衡
将各个机器上的region数量平衡
hbase.balancer.period 多长时间间隔执行一次,默认是5分钟
hbase.balancer.max.balancing 每次执行多长时间,默认是上面参数的一般,也就是2分30秒
可以通过HBaseAdmin#balanceSwitch()来控制开关,可以通过shell命令控制
region的合并
./hbase org.apache.hadoop.hbase.util.Merge <table-name> <region-1> <region-2>
客户端API最佳实践
1.关闭自动flush
2.使用scanner缓存
3.设置scanner的范围
4.关闭ResultScanners
5.使用块缓存
6.优化需要加载的key(如使用KeyOnlyFilter)
7.关闭WAL(预写日志)
配置
1.减少zookeeper超时时间
2.增加处理线程hbase.regionserver.handler.count
3.增加堆大小(hbase-env.sh)
4.启用压缩
5.增加region大小(hbase.hregion.max.filesize)
6.调整block缓存大小(perf.hfile.block.cache.size),是一个浮点数(如0.2)
7.调整memstore大上限(hbase.regionserver.global.memstore.upperLimit)默认0.4,
hbase.regionserver.global.memstore.lowerLimit 默认0.35
8.增加blocking存储文件 hbase.hstore.blockingStoreFiles(多余这个数量的hfile时才会合并)
9.增加block multplier hbase.hregion.memstore.block.multiplier
如果memstore有hbase.hregion.memstore.block.multiplier倍数的
hbase.hregion.flush.size的大小,就会阻塞update操作
hbase.hregion.flush.size这个参数的作用是当单个Region内所有的memstore大小总和超过指定值时,
flush该region的所有memstore
负载测试
hbase自带的测试工具
./hbase org.apache.hadoop.hbase.PerformanceEvaluation
YCSB
yahoo cloud serving benchmark
java -cp build/ycsb.jar:db/hbase/lib* com.yahoo.ycsb.Client
第十二章 集群管理
操作任务
关闭一个region server
./hbase-daemon.sh stop regionserver
但是这样做不好,应当使用如下方式:
首先关闭bleance
./hbase shell blance_switch false
之后执行
./graceful_stop.sh HOSTNAME
重启一个region server
./graceful_stop.sh --restart --reload --debug HOSTNAME
增加一个region server
在master机器的conf文件中增加机器的hostname
可以在master机器上执行start-hbase.sh,它会跳过所有已经启动的机器,并启动新增加的机器
或者新增加的机器上单独执行脚本:
./hbase-daemon.sh start regionserver
增加一个备份master(和原先的master在一台机器上,可以创建多个备份)
./local-master-backup.sh start 1
在不同机器上创建备份
./hbase-daemon.sh start master --backup
还可以调用master-backup.sh 脚本完成备份
连接zookeeper,./hbase zkcli 可以观察到已经增加到备份znode上的机器
ls /hbase/backup-masters
数据任务
可以用.hadoop jar直接运行hbase-xxx.jar,运行前需要将$HBASE_HOME/lib下的一些jar拷贝到$HADOOP_HOME/lib下
zookeeper-xxx.jar
guava-xxx.jar
protobuf-java-xxx.jar
如下面一段是将testtable备份到hdfs的/hbase-backup目录下
./hadoop jar $HBASE_HOME/hbase.jar export testtable /hbase-backup/test-table
导入命令如下:
./hadoop jar $HBASE_HOME/hbase.jar import mybak /hbase-backup/test-table
直接运行./hadoop jar $HBASE_HOME/hbase.jar export/import 会有些更详细的参数
export和import是用mapreduce执行的,所以可以通过-D导入一些mapreduce参数
另外export支持start time,end time,prefix,regexp等参数
需要注意,import时指定的表必须先创建,否则会提示找不到表
使用hadoop的命令./hadoop distcp,将一个集群中的数据拷贝到另一个集群中
但是只能用停机备份,运行时备份会有问题
./hadoop jar $HBASE_HOME/hbase.jar copytable --new.name=new-tab old-tab
类似上面两个命令的整合,可以将一个旧表的数据导入新表中,同时还支持集群间的拷贝,
可以将旧表的数据拷贝到其他集群中,注意拷贝时候新指定的表必须存在
importtsv和completebulkload 这两个可以命令可以完成大量数据导入,比直接用API快一个数量级
importtsv可以直接导入,也可以分两步导入,先用importtsv导入到hdfs的临时目录中,生成内部的HFile格式,再用
completebulkload命令move到目标表中,completebulkload只是本地磁盘拷贝非常快
这两个命令需要用到mapreduce,如果没有配置mapred-site.xml则默认使用本地mapreduce,运行时会有问题
在mapred-site.xml中加一段:(注意端口不能和hdfs中定义的port一样)
<name>mapred.job.tracker</name>
<value>hdfs://ip:54311</value>
定义hadoop-env.sh,将hbase.jar,zookeeper.jar,guava.jar,protobuf.jar引入到HADOOP_CLASSPATH中
执行命令
./hadoop jar hbase.jar importtsv -Dimporttsv.bulk.output=/hbase-tmp -Dimporttsv.columns=HBASE_ROW_KEY,col_name
table_name /xx.txt
xx.txt中的数据格式如下:(默认是按\t分割的),可以通过-D参数指定
key-1 xxxxxxxxxx
key-2 xxxxxxxxxx
key-3 xxxxxxxxxx
运行之后/hbase-tmp目录下就有数据了,查看新表已经创建了,但是里面还没有数据,用completebulkload导入
./hadoop jar hbase.jar completebulkload /hbase-tmp table_name
导入很快,导入完成之后表中就有数据了,如果导入数据很大,原表中就有region,导入时候会执行切分工作
可能需要将hbase-site.xml放入hadoop-conf变量中,但我测试的时候没导入也成功了
Replication
需要在hbase-site.xml中增加一个配置hbase.replication=true
通过shell脚本增加一个对等集群(对等集群的规模可以和原集群不同)
add_peer '1','IP-2:2181:/hbase'
启动复制 start_replication
验证复制
./hadoop jar hbase.jar verifyrep
可以比较两个集群的两个表数据是否相等,可以基于时间增量比较
附加的任务
共存的集群
只能用于测试目的
需要修改hbase-site.xml中的下列属性:
hbase.rootdir
hbase.tmp.dir
zookeeper.znode.parent
hbase.master.port
hbase.master.info.port
hbase.regionserver.port
hbase.regionserver.info.port
conf目录和log目录需要另外拷贝一份
启动: $HBASE_HOME_CONF_DIR=conf.2 ./start-hbase.sh
故障排查
日志级别默认是DEBUG,在conf/log4j.properties中调整
./hbase hbck
首先是做一致性检查,检查.META.表中的数据是否对应到具体regionserver中
之后是完整性检查,如果加上了-fix会做一些修正工作,比如将未分配的region分配出去
或者重新分配region等
通过ulimit -n 可以查看datanode当前的处理上线
cat /proc/<PID of JVM>/limits
启用LZO压缩,需要单独安装LZO包,否则会报找不到类错误
zookeeper问题
1.因为页面换入换出导致处理响应变慢,zookeeper超时
通过vmstat 20 和free -m查看
2.GC时间过长导致的超时问题
3.机器负载过高导致超时问题(zookeeper可能和regionserver部署在相同物理机器上)
4.IO压力高导致超时问题
HBase的历史和使用场景
关系型数据库的问题
1.访问量过大必须主从分离
2.主写从读,写压力仍然很大前端只能加上缓存
3.水平分区
一致性模型
1.强一致性 所有的改变都会原子性的立刻生效
2.顺序一致性 每个客户端都会看到对统一数据的顺序操作,但不一定是实时的
3.因果关系一致性 对于存在因果关系的操作会看到相同的顺序,无因果关系的则会并行进行
4.最终一致性 当没有数据更新时,所有的数据通过网络传播复制使得最终达到一致
5.弱一致性 对于数据更新传播没有包含,使得各客户端看到的数据不一致
Nosql的维度
1.数据模型
2.存储模型
3.一致模型
4.物理模型
5.读写性能
6.二级索引
7.失败处理
8.压缩
9.负载均衡
10.原子的读修改写
11.锁等待和死锁
构建块
table,rows,columns,cell,类似这种结构:
SortedMap<RowKey, List<SortedMap<Column, List<Value,Timestamp>>>>
自动分片,存储API
HBase:Hadoop数据库
hbase和bigtable对比
HBase Bigtable
region tablet
regionserver tablet server
flush minor compaction
minor compaction merging compaction
major compaction major compaction
write-ahead log commit log
HDFS GFS
mapreduce mapreduce
memstore memtable
HFile SSTable
zookeeper chubby
第二章 安装
需求
最好是商用pc,而不是桌面pc,内存要大,因为region节点需要大量内存,cpu最好4核以上
master和slave机器可以配置相同也可以不同,master要有更高的可靠性
CPU
master 2.5G(4核)
slave 2.5G(4核)
内存
namenode 8G
secondary namenode 8G
job tracker 2G
hbase master 4G
datanode 1G
task tracker 1G
region server 12G
zookeeper 1G
主从机器物理内存最好超过24G
磁盘
master 4 * 1TB STAT,raid0+1
slave 6 * 1TB STAT,jbod
从IOPS考虑,可以将4*1TB换成8*500G,这样可以提高一倍的IOPS
机架
master 1000M带宽,双PSU,1U或2U
slave 1000M带宽,单PSU,1U或2U
操作系统
建议选择CentOS或Red hat
文件系统
有ext3,ext4,XFS,ZFS,建议选择更高的ext4或XFS
SSH可以不用安装
域名服务器 ping -c
时钟同步服务器NTP
文件句柄上线
lsof -p region_server_pid
datanode处理线程
dfs.datanode.max.xcievers 调整到4096
调整交换分区
挂起进程可能会导致zookeeper超时,设置/etc/sysctl.conf
vm.swappiness=5
cat /proc/sys/vm/swappiness
HBase的文件系统
本地文件系统
HDFS
S3
其他,如CloudStore,KFS
安装选择
源码安装
mvn assembly:assembly
mvn -DskipTests assembly:assembly
运行模式
独立模式
伪分布式
完全分布式
name: hbase.rootdir value: hdfs://namenodeip:9000/hbase
name: hbase.cluster.distributed value: true
配置
hbase-site.xml
优先hbase-site.xml
其次hbase-default.xml
再是hadoop相关xml
hbase-env.sh
regionservers
log4j.properties
部署
脚本部署
apache whirr
puppet和chef
第三章 客户端API的基本操作
这一章将要讨论的是HBase提供的客户端API。按照之前的介绍,HBase是用java语言编写的。但是这并不意味着必须用java客户端去访问HBase。事实上,在第六章我们将介绍如何用其他语言访问HBase。
通用性介绍
操作HBase的主要接口在org.apache.hadoop.hbase.client中,使用HTable类
1.在一个客户端的生命周期中使用一个HTalbe使用,因为创建HTable花销比较大,会先扫描.META.表
2.如果要在多线程中使用HTable,推荐使用HTablePool
3.对每一行的更新操作都是原子的
CURD操作
1.Put
用Put这个类做更新操作
有一个工具累Bytes,可以将各种类型的值转换为byte[]
Put#add(byte[] famliy,byte[] qualifier,long ts,byte[] value)
基本的add()操作
另外add()支持放入KeyValue这个对象
KeyValue是HBase API中最低级别的类,这个类的数据格式,就是HBase中存储的字节格式,所以
如果对性能要求很高的话,可以直接对这个类进行操作
has(byte[] family,byte[] qualifier)
这个类似迭代中的检查,检查某一个指定的cell是否存在
Put的父类中,有一些API
getRow()
getRowLock()
getLockId()
getTimeStamp()
heapSize()
通过Configuration创建一个默认配置实例,它会从classpath查找hbase-default.xml和
hbase-site.xml,使用Configuration.create(config)指定一个配置,这样优先级更高
HBaseConfiguration继承并兼容Configuration
可以手动指定一些属性,这样优先级最高,会覆盖配置文件中的属性
KeyValue
字节级别的操作,比对象级别的操作更有效,这是HBase提供的可以访问数据格式内部的类
从构造函数就可以看出,是对底层KeyValue结构的一个封装,可以直接访问byte[]
提供了一些比较类(实现了Comparator接口)
toString()返回当前的KeyVlaue的元信息,格式如下:
<row-key>/<family>:<qualifier>/<version>/<type>/<value-length>
Hbase有一个版本的概念,所有的版本都是按时间降序排列的,所以拿到的就是最新版本
如有一个test表
create 'test','name'
put 'test','row1','name','value1'
put 'test','row1','name','value2'
scan 'test' 执行之后会发现只有一条数据,即第二个put将第一put的值覆盖了
如果执行scan 'test',{VERSIONS=>3}
就看以检索出两个数据,将'value1'和'value2'都检索出来
客户端缓存
HTable#setAutoFlush()可以设置缓存
可以强制刷新缓存flushCommits()
HBase客户端会将缓存的内容排序后,put到相应的region server上
可以设置缓存大小: setWriteBufferSize()
计算服务端的内存使用:
hbase.client.write.buffer * hbase.regionserver.handler.count * region server数量
在没有刷新缓存之前,Htable#put()的内容都在内存中,此时如果去查找,则获取不到值
Put多个值
put<List<Put> 操作
使用HTable#put(List<Put>) 这种方式提交,假设有4个Put对象,其中一个Put对象所对应
的列不存在,则其他的3个Put对象是可以插入到表中的。
可以通过try-catch的方式捕获这些异常,然后在catch中使用flushCommits(),当然不使用
flushCommits()也可以插入成功。
原子更新
checkAndPut(byte[] row,byte[] family,byte[] qualifier,byte[] value,Put put)
服务端实现的原子操作
如果某一列不存在,则制定value为null即可
2.Get
在0.94版本中构造函数 Get(byte[] row,RowLock lock)已经不存在了
Get类的其他方法
setFilter()
GetCacheBlocks()
numFamilies()
Bytes工具类也提供了一些方法,可以将byte[]转换为具体的java类型
Result类
提供了一些方法去检索键值对
KeyValue覆盖了toString()可以方便的dump
Get多个值
和Put不同,当批量Get的时候,如果其中一个Get所对应的列不存在,则整个批量Get就执行失败,不会返回结果
涉及的检索方法
exist()
getRowOrBefore(byte[] row,byte[] family)
根据row key和family返回一个Result,如果指定的row key不存在,则返回这个key的前面的一个值
3.Delete
deleteFamily(byte[] family)
deleteColumn(byte[] family,byte[] qualifier)
deleteColumns(byte[] family,byte[] qualifier)
这些操作可以删除多个列,如果指定了时间戳,则会删除较老的时间戳的多个列
如果指定一个时间戳,但是这个时间戳不存在,则什么都不会发生
Delete多个值
使用HTable#delete(List<Delete>) 这种方式删除,假设有4个Delete对象,其中一个Delete对象所对应
的列不存在,则其他的3个Delete对象是可以从表中删除的。
可以通过try-catch的方式捕获这些异常
原子删除
类似Put的原子更新,值必须存在才可以删除,如果删除的列不存在则抛出一个异常
批量操作
Get,Delete,Put的父类Row,通过方法batch(List<Row> )支持多个不同的操作
但需要注意这些操作之间可能会互相影响
批量操作会返回四种结果:
1.null 比如远程操作失败
2.空结果 如Put和Delete操作成功
3.Result对象,执行Get操作成功,如果没有查找到结果则返回空
4.抛出一个客户端的异常
有两个方法:
batch(List<Row>)
batch(List<Row>,Object[])
另外批量操作还支持回调
行锁(0.94稳定版中已经没有这个类了)
put(),delete(),checkAndPut()都是一些排斥操作,可以显示的指定一个锁
RowLock,并在创建Delete,Put时指定这个锁
当多个客户端同时操作一行时,第一个使用锁的客户端就会占有这行,之后所有的客户端必须等待第一个操作完才可以
继续访问
锁的超时时间可以通过hbase-site.xml配置
Get也有一个锁,但实际上服务端使用 "多并发版本控制" 的方式去实现的
使用行锁要小心死锁的问题
Scans
类似数据库的游标
有以下一些方法:
setStartRow(byte[] startRow)
setStopRow(byte[] stopRow)
setTimeRange(long minStamp, long maxStamp)
setFilter(Filter filter)
ResultScanner
通过HTable#getScanner()可以获得一个ResultScanner对象,它包含三个方法
void close()
Result next()
result[] next(int nbRows) 返回nbRows个Result对象
Scanner可能会返回很多Result对象,操作这些对象的时候需要注意尽可能短的释放他们,调用close()或者在
hbase-site.xml中配置
缓存和批处理
设置cache大了可以提高性能,但是设置过大,每一次调用next()都会获取很多数据,甚至会导致OOM
读取时间过长可能会超时异常,可以在客户端代码中设置
Configuration#setLong(HConstants.HBASE_REGIONSERVER_LEASE_PERIOD_KEY,num)
这个设置只对客户端有效,服务端需要修改hbase-site.xml才可生效
cache是针对行的,而batch是针对列的,比如有17列,batch设置为5,则依次返回5,5,5,2 四次
RPC次数 = (行 * 每行的列) / Min(每行的列,批处理大小) / 缓存数
如果没有设置批处理数,则返回的一个完整的行
其他特性
1.HTable工具方法
close()
getTalbeName()
getTableDescriptor()
getRegionLocatioin(Byte[] row)
getRegionsInfo()
2.Bytes类
提供了java类型到byte[]类型的转换,同时也提供了byte[]转换为java类型
支持String,boolean,short,int,long,float,double
第四章 客户端API的高级特性
Filters
通过客户端定以后的filter,经过RPC调用,在服务端执行
接口Filter,抽象实现FilterBase
有比较的Filter,专用的Filter,装饰的Filter,Filter列表几大类
比较操作:(在类CompareFilter.CompareOp 中定义)
1.LESS
2.LESS_OR_EQUAL
3.EQUAL
4.NOT_EQUAL
5.GREATER_OR_EQUAL
6.EREATER
7.NO_OP
比较的实现类(继承自抽象类ByteArrayComparable)
1.BinaryComparator
2.BinaryPrefixComparator
3.NullComparator
4.BitComparator
5.RegexStringComparator
6.SubstringComparator
需要注意BitComparator,RegexStringComparator,SubstringComparator只能用于EQUAL或者NOT_EQUAL操作,用于其他操作会出错
基于比较的Filters
1.RowFilter
可以用于之前的各种比较实现类,实现二进制比较,正则表达式,子串等
2.FamilyFilter
基于列簇的比较
3.QualifierFilter
基于限定符的比较
4.ValueFilter
基于值的比较
5.DependentColumnFilter
精确定位列簇和限定符
专用的Filters
1.SingleColumnValueFilter
可以设置setFilterIfMissing(true) 返回所有的列,还是返回只查找到的列
2.SingleColumnValueExcludeFilter
和SingleColumnValueFilter类似,只是如果满足条件,则不返回指定的列(无论是返回多列还是单列)
3.PrefixFilter
根据key的前缀查找
4.PageFilter
可以返回指定个数的行,配合startRow可以达到分页的效果
5.KeyOnlyFilter
只返回key,如果指定构造函数true,则返回value的length
6.FirstKeyOnlyFilter
只返回第一列
7.InclusiveStopFilter
通过和startRow配合使用,可以指定一个范围,当扫描到指定的filter就停止
8.TimestampsFilter
限制timestampe范围,可以指定一个List<Long>,包括了需要检索的timestampe,Scanner可以指定检索的范围
9.ColumnCountGetFilter
一次能检索的最大的列数
10.ColumnPaginationFilter
可以从指定的偏移量开始,选择若limit个列,这个filter适合列非常多的表
11.ColumnPrefixFilter
类似PrefixFilter,这个filter是取cloumn的前缀
12.RandomRowFilter
内部使用java.util.Random#nextFloat(),返回一个随机的行
如果值为负数则不返回任何行,如果大于1.0则返回所有行
Decpratomg Filters
装饰filter,配合其他filter一起使用
SkipFilter
如果创建ValueFilter(CompareOp.NOT_EQUAL,new BinaryComparator(Bytes.toBytes(0))
SkipFilter包装了ValueFilter后,如果发现某一行不包含0,则全部跳过,而不是像VluaeFilter
那样,只是把对应的列跳过,SkipFilter跳过的是一整行
WhileMatchFilter
比如RoWFilter选择不匹配第五行,那么最终Scan会得到除第五行以外的其他行
而whileMatchFilter会匹配到第四行之后,发现第五行不匹配就终止了
FilterList
可以包含多个Filter,可以传入List<Filter>,如果传入的是ArrayList,就按照加入的顺序执行各个filter
默认是MUST_PASS_ALL,还有一个是MUST_PASS_ONE,即至少返回一个结果
自定义的Filter
可以实现Filter接口或者继承FilterBase类
内部枚举类RetrunCode定义了一些具体动作
1.INCLUDE
2.SKIP
3.NEXT_CLO 如TimestampFilter
4.NEXT_ROW 如RowFilter
5.SEEK_NEXT_USING_HINT 如ColumnPrefixFilter
具体的一些执行方法:(按照执行先后顺序)
1.filterRowKey(byte[] buffer, int offset, int length)
2.filterKeyValue(KeyValue v)
3.filterRow(List<KeyValue> kvs)
4.filterRow()
5.reset()
6.filterAllRemaining()
首先会执行filterRowKey(),判断row是否满足,RowFilter就是按照这种逻辑去实现的
如果不满足则执行FilterKeyValue(),这回返回一个RetrunCode类型,指明下一步需要的操作
filterRow(List) 根据前面的结果再做进一步过滤
filterRow()会根据结果判断是否停止执行, PageFilter就是用这个方法去实现的
修改后的jar需要部署到服务端才可以执行,可以修改hbase-env.sh指定classpath,或者放到lib目录下
修改后需要重启才能生效
Counters
hbase提供的一种功能,可以原子的更新计数
可以用于广告,数据统计等
创建一张表后,计数器的语法为:
incr 'table','row','column',[increment-value]
获取方法:
get_counter 'table','row','column'
incr可以使用三种值:
1.大于0,用于增加
2.等于0,类似get_counter,可用于检索
3.小于0,用于减去一个值
注意,写操作是有锁保护的,但是读操作可能没有
当多个counter更新相同的行可能会有问题
单行counters
HTable#incrementColumnValue()
多行counters
Increment
Coprocessors
类似关系数据库的存储过程,可以通过实现指定的接口完成任意代码
有这么几种类型:
Observer
1.RegionObserver
2.MasterObserver
3.WALObserver
Endpoint
Coprocesor有两个枚举类型,Priority和State
Priority:(0.94版本做了一些修改,增加了最高和最低级别)
1.SYSTEM
2.USER
系统级别会在用户级别前执行,相同优先级通过定义的数字大小按顺序执行
Coprocessor包含start()和stop()方法,并且包含CoprocessorEnvironment实例
Coprocessor.Stat包含以下状态:
1.UNINSTALLED
2.INSTALLED
3.STARTING
4.ACTIVE
5.STOPPING
6.STOPPED
三个重要的类,Coprocessor,CoporcessorEnvironment,CoprocessorHost
Coprocessor装载
在hbase-site.xml中增加coprocessor相关配置:
1.hbase.coprocessor.region.classes 值就是自定义的class,中间用逗号分隔
2.hbase.coprocessor.master.classes 同上
3.hbase.coprocessor.wal.classes 同上
加载表描述,用于每个region server,但是不能用户WAL和master
格式为:(必须以COPROCESSOR开头)
<path-to-jar>|<classname>|<priority>
如:
alter 'testTable' METHOD=>'table_att',
'COPROCESSOR$1'=>'hdfs://ip:54310/myhbase/test.jar|test.coprocessor.Test|SYSTEM'
RegionObserver class
preGet()方法如下:
preGet(ObserverContext<RegionCoprocessorEnvironment> e,Get get,List<KeyValue> results)
RegionCoprocessorEnvironment 上下文可以获得当前运行时相关的表配置,表信息,RegionServer信息等
更改配置后需要重启
MasterObserver class
类似RegionCoprocessor,也包含了MasterCoporcessorEnvironment
类似数据库中的DDL,在DDL之前之后会有一些触发操作被回调
Endpoint
RegionObserver是在单个server上执行的,也是单个region,但如果要计算表的行数,那么就需要依次对每个region
都操作一遍,代价太大
Endpoint可以对表中的每个region执行一个操作并返回,这个操作是在服务端执行的,所以传输的数据就会小很多
Endpoint类似数据库中的sum()函数
具体部署方式和RegionObserver一样,也需要配置hbase-site.xml
客户端通过回调方式收集所有的region返回的结果,这个有点像微型的MapReduce
回调的接口在org.apache.hadoop.hbase.client.coprocessor.Batch中,是Batch的内部接口
HTablePool
在高并发的环境中创建HTable是一个耗时的操作
HTable也不是线程安全的,最好在每个线程中使用一个HTable实例
HTablePool构造函数中最大数并不是控制最多可以同时获取多少个HTable实例,而是缓存中的HTable实例个数
如果超过了这个数,就新实例化一个并返回,一样可以使用
构造函数中提供了一个HTableInterfaceFactory,自定义的创建HTableInterface实例
注意:在0.94之后又提供了新的构造参数,一个枚举类型,指定池的类型,有三种:
1.Reusable
2.RoundRobin
3.ThreadLocal
连接处理
很多连接都是会被缓存的,如访问zookeeper的连接,这样可以避免重复连接
HTalbe的构造函数支持Configuration对象,这样就可以做到多个表共享连接了
但是缺点就是连接不会主动释放,只有客户端退出了才会释放
可以手动释放连接
第五章 管理特性
模式定义
HTableDescriptor
我们讨论的很多类都有一个无参的构造函数,因为这些类实现了Writable接口,这是为远程序列化考虑的
实现自定义的类需要保证如下:
1.实现Writable接口
2.提供一个无参数的构造函数
表的属性
表的名字必须是拉丁字母的,用正则表达式表示为:
[a-zA-Z_0-9-.]
默认的文件大小是256M,超过就是执行分割
默认是非只读的,可以指定表为只读
memstore flush默认为64M
是否打开预写日志
列簇
严格来说叫HColumnFamilyDescriptor更合适
family的名字必须是可打印的字符,而qualifier可以是任意字符
family不能重命名,只能创建一个新的
最大版本为3,可以查找到以前删除的历史
压缩类型
1.NONE
2.GZ (gzip)
3.LZO
4.SNAPPY
block大小,默认为64K,这个相当于缓存,hadoop的block大小是用指定单个文件大小
block缓存默认是开启的
支持一个版本的TTL,如果超时的话在块合并的时候会被删除
可以支持将列完全放入内存中,当达到JVM内存上限时会丢弃多余的数据
布隆过滤可以大幅度提高检索时间,但是也有误识别的缺点,默认是关闭的,可用key和key+(family+qualifier)的组合
复制范围,默认关闭为0,表示本地复制,设置为1表示集群复制
HBaseAdmin
类似数据库的DML
集群操作
flush()
compact()
majorCompact()
move()
blance()
集群状态信息
通过HBaseAdmin获得ClusterStatus
ServerName 服务端机器信息
HServerLoad 服务状态信息
RegionLoad region相关状态信息
WebUI上可以观察到这些信息
第六章 可用的客户端
交互式的客户端
REST
启动方式./hbase-daemon.sh start rest
文本方式
curl -H "Accept:text/plain" http://IP:8080/ppp/aa1
xml方式(默认格式)
curl http://IP:8080/ppp/aa1
json方式
curl -H "Accept:application/json" http://IP:8080/ppp/aa1
protocol buffer方式
curl -H "Accept:application/x-protobuf" http://IP:8080/ppp/aa1 | hexdump -c
raw binary方式
curl -H "Accept:application/octet-stream" http://IP:8080/ppp/aa1 | hexdump -c
REST java client
Cluster cluster = new Cluster();
cluster.add("IP",8080);
Client client = new Client(cluster);
RemoteHTable table = new RemoteHTable(client,"test");
//..之后的get,delete,put,scanner操作都和普通的方式一致
Thrift
也是跨语言的,有thrift和thrift2两个版本,用C++编写的
启动方式./hbase-daemon.sh start thrift
模式文件定义在 $HBASE_HOME/src/main/resources/org/apache/hadoop/hbase/thrift/下
Hbase.thrift文件
生成php文件支持:(假设已经安装好thrift)
thrift -en php Hbase.thrift 之后产生一个gen-php目录
cp /lib/php/src $DOCUMENT_ROOT/thrift
mkdir $DOCUMENT_ROOT/thrift/packages
cp -r ~/thrift_scr/gen-pp/Hbase $DOCUMENT_ROOT/thrift/packages
HBase目录下的src/examples/thrift目录下有php的列子
Avro
和thrift类似,也是跨语言的
启动方式./hbase-daemon.sh start avro
模式文件定义在 $HBASE_HOME/src/main/java/org/apache.hadoop/hbase/avro/hbase.avpr
其他客户端
jruby
HBql
HBase-DSL
JPA/JPO
PyHBase
AsyncHBase
批处理客户端
HIVE
PIG
Cascading
创建和hbase管理的表
create table hbase_table(key int,value string)stored by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' with serdeproperties("hbase.columns.mapping"=":key,cf1:val")
tableproperties("hbase.table.name"="hbase_table")
关联已经存在的hbase表
create external table hbase_table_2(key int,value string)stored by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' with serdeproperties("hbase.columns.mapping"=":key,cf1:val")
tableproperties("hbase.table.name"="hbase_table_2")
关联外部表时,如果在hive中删除了这张表,外部表不会被删除
Shell
开启debug, 查看是否开启debug?
jruby脚本
org.apache.hadoop.hbase.util.Bytes.toInt("\x00\x01\x06[".to_java_bytes)
java.text.SimpleDateFormate.new(yyyy/MM/dd HH:mm:ss").parse("2012/12/21
20:56:29").getTime()
for i in 'a'..'z' do for j in 'a'..'z' do put 'test',"row-#{i}#{j}","colfam1:#{j}","#{j}" end end
require 'date'
一个xx.rb脚本 ,运行方式./hbase org.jruby.Main xx.rb
include java
import org.apache.hadoop.hbase.HBaseConfiguration
import org.apache.hadoop.hbase.client.HBaseAdmin
conf = HBaseConfiguration.new
admin = HBaseAdmin.new(conf)
tables = admin.listTables()
tables.each {|table| put table.getNameAsString() }
webUI
配置hbase-site.xml可以调整webUI端口
hbase.master.info.port
hbase.regionserver.info.port
masterUI 和 regionUI
通用功能
Local logs
Thread Dump
Log Level
Debug dump
第七章 整合MapReduce
map-reduce可以处理TB级别数据,将大任务分解为多个小任务到多台机器上,执行完结果后再汇总
InputFormat
getSplits()
createRecordReader()
Hbase提供了TableInputFormatBase作为其扩展实现
Mapper的每一个记录使用RecordReader处理map()
Hbase提供了TalbeMapper作为其扩展实现
Reducer阶段汇总Mapper的数据
Hbase提供了TableReducer作为其扩展实现
OutputFormat 最终的数据输出
Hbase提供TableOutputFormat作为其扩展实现,用TableRecordWriter写入到指定表中
TableOutputCommitter需要hadoop类是完成job工作
使用TableMapReduceUtil设置map-reduce工作
hdfs使用三副本的形式,map-reduce选择三台机器之一运行
TableInputFormat#getSplits()等于所有region中的startKey和endKey之间数据,然后做切分
之后TableRecordReader会迭代的对region的startKey和endKey的每行数据进行处理
在MapReduce之上运行HBase
静态导入
需要将hbase的jar导入到hadoop的classpath中,此外要配置mapre-site,需要重启节点
动态导入
使用maven引入需要的jar,使用hadoop jar xx.jar的方式运行
注意guava等jar也需要引入到classpath中来
Data Sink(下沉)
实现Mapper接口,其setup()函数是只调用一次,map()是每行都会被触发调用一次
最终调用TableOutputFormat,它会将map之后分解的数据写入到表中
因为写入的不是中间文件而是hbase的表,所以结果是有序的,这样就不需要reduce了
Data Source(来源)
一个统计单词的例子,从Result中读取每个columns,然后解析作者名字,记录+1并设置到上下文中
之后reduce过程对其+1
最后汇总打印出来
辅助工具类TableMapReduceUtil
自定义过程
可以将输出写到两个不同的表中,在setup()函数中定义A和B两个表
在map()中判断key的内容,如果是aa开头则插入到A表,否则插入到B表
第八章 架构
查找和传输
B+树 一个节点下包含了多个节点,这样可以降低IO读取的次数
但是B+树一个节点超过限制了,就会拆分,从而引起父节点拆分,这样可能引发很多个拆分,而且不同节点可
能分散在磁盘
的不同地方,不是连续的,这样性能就差了
B+树的优化就是将不连续的块重新写一遍,做到连续
LSM(Log-structured merge-tree)是先将数据写入到日志文件中,日志文件是有序的,当日志文件改变时,先
将改变的内容
保存到内存中,优化查询,当内存满了就将数据写到磁盘中,此时内存的数据可以丢弃,经过多次写之后,
会产生多个小文件,后台线程会将多个小文件合并成一个大文件,这样查询就被定位在几个文件中了
存储
当memstore大于hbase.hregion.preclose.flush.size(默认5M)就会刷新到磁盘上
hdfs路径 /hbase/.logs 会包含对应的子region名字的目录,每个region就对应一个子目录,对于一个region
目录是共享的
日志文件大小为0是因为hdfs内置的append机制使用追加写导致的,只有文件达到一定大小才是可见的
hbase.regionserver.logroll.period 设置日志滚动时间(默认60分钟)
当数据被写入到磁盘后,就不需要日志文件了,这样的文件会被放到 /hbase/.oldlogs 目录下,也是一个
region对应
一个目录
参数hbase.master.logcleaner.ttl控制多久之后旧日志被删除(默认10分钟)
参数hbase.master.cleaner.interval控制多长时间检查一次(默认1分钟)
根级文件(在HDFS中/hbase目录下)
/hbase/hbase.id 和 /hbase/hbase.version
这两个文件包含集群的唯一ID和文件格式版本信息
/hbase/splitlog 和 /hbase/.corrupt
第一个是日志拆分产生的中间文件,第二个是损坏的日志
表级文件
每个表都有自己的目录,每个表包含 .tableinfo 顶层文件和.tmp目录
第一个是用来存储表结构信息的,第二个是当新的.tableinfo生成的临时文件就放在.tmp目录下
region文件的总体结构:
<hbase-root-dir>/<tablename>/<encoded-regionname>/<column-family>/<filename>
ncoded-regionname是 表名,起始key,启动时间,md5编码 这些组成的
<hbase-root-dir>/<tablename>/<encoded-regionname> 这个目录下还有一个 .regioninfo文件
拆分
在父region的splitlog目录下拆分log,然后关闭当前region,不接受请求
META表会更新父region,显示SPLIT=>true,之后会出现两个信息
column=info:splitA 和 column=info:splitB
当两个子region初始化好之后META表删除父region,并删除文件,可以由负载均衡将新region移动到其他机
器上
整个过程都在zookeeper中进行跟踪
合并
minor合并和major合并
minor合并 hbase.hstore.compaction.min(默认为3)设置的,这个值最小要大于等于2
minor合并可以处理的最大文数 hbase.hstore.compaction.max来配置(默认为10)
hbase.hstore.compaction.max.size 任何比这个值大的文件都会排除在外
hbase.hstore.compaction.min.size 它是一个阀值而不是限制,它包括任何小于限制的文件,直到每次压
缩允许的
的总文件数量
hbase.hstore.compaction.ratio(默认为1.2即120%) 这个参数确保选择过程中尽可能的包含更多文件
主合并major由CompactionChecker类实现
周期由hbase.server.thread.wakefrequency参数控制(乘以
hbase.server.htread.wakefrequency.multiplier,
设置为1000,使得执行不那么频繁)
除非强制指定
HFile格式
<data blocks(多个)><meta blocks(多个)><fileinfo><data index><meta index><trailer>
Data格式:
<Magic><KeyValue><KeyValue>...
空的HFile格式为:
<fileinfo><trailer>
目前有V1和V2两种HFile格式,最新用的是V2格式,它支持多级索引
HFile中包含若干个块,每个块的大小默认是64K,建议设置到8K--1M之间,较大的块适合顺序访问但不适合
随机访问,
较小的快适合随机访问但是会造成索引过多
HFile块对于HDFS来说是透明的,一个HFile可能会被写入到多个HDFS块中(一个HDFS块默认64M)
这两种块之间没有任何关系
使用命令查看HFile状态
./hbase org.apache.hadoop.hbase.io.hfile.HFile
KeyValue格式
Key长度,Vluae长度,Row长度,Row,column family长度,column family,column qualifer,timestamp,key
type,value
WAL
类似mysql的binlog,在主存储器出现意外的情况下作为恢复使用
HLog类
所有的region都共享一个HLog,它使用追加写的方式
HLogKey
使用region的编码名,表名,long类型的递增序列号,集群的UUID表示key
WALEdit
每一个修改的实例都被封住到一个WALEdit中
LogSyncer(最新版没有这个类)
延迟日志刷新默认为false,意味着每一编辑被发送到服务器时,都会调用写日志的sync(),但这意味着
一对N的管道写,datanode是写三分,管道的方式写入,只有最后一个写成功返回才算成功。这样会带来很
大的延迟。可以选择稍微延迟这个调用,但是可能有一定几率造成数据丢失
HDFS以后会支持多路写,即一次向多个datanode写入数据,只要等最慢的一个写入完成就算成功,不过这样
会给占用很多带宽
属性hbase.regionserver.optionallogflushinterval(默认1秒)配置多久之后调用sync()
LogRoller(最新版没有这个类)
在特定的时间间隔内滚动日志,通过配置hbase.regionserver.logroll.period(默认1小时)来控制
hbase.regionserver.hlog.blocksize 设置文件系统的快大小(默认32M)
hbase.regioinserver.logroll.multiplier(默认为0.95)表示当日志大小达到块大小的95%会滚动日志
日志回放
1.只有一份日志,跟bigtable类似,这是为性能考虑的,多个region共享一个日志文件
2.日志的拆分,之前是由master负责的,串行的回复每个日志,同时每个region都会被阻塞
现在使用分布式模式注册到zookeeper上,master指定某个日志是可以被处理的,region就会竞争这个
任务
可以通过hbase.master.distributed.log.splitting来设置关闭新的分布式日志拆分
在非分布式下写入是多线程的,这是通过hbase.regionserver.hlog.splitlog.writer.threads来控制的(
默认是3)
hbase.hlog.split.skip.errors(默认为false)表示当出现错误时是否跳过,不跳过抛出IOException
拆分中的日志被放在splitlog目录下
为了能和其他日志区分开来并发操作,日志路径包含了region名(散列值)和recovered.edits文件夹
错误的不能恢复的日志被放在.corrupt目录下
3.数据恢复
一旦检查到recovered.edits目录便开始
持久性
底层的hadoop需要支持追加写,以前的hadoop是提供API向文件写入,写入之后才可以读取这个文件,并且
这个文件会变成只读,但是对于hbase可能会造成数据丢失
之后hadoop增加了追加写的功能,hbase调用syncFs()或hflush()管道的方式写入数据
读路径
小合并,大合并,控制HFile文件数量
旧的数据作为KeyValue存储在磁盘上,新的数据被刷新后也会被存储,但是不会修改原始文件,而是存储在
新文件中
删除的数据只是做一个墓碑标记,并不会真正删除,只有当合并的时候才会被删除
一个StoreScanner实例代表了一个Sotre,通过这个实例的next()扫描所有的数据并跳过被标记删除的数据
Region查找
Root表-->meta表-->用户表
最坏的情况可能需要六次查找
Region的生命周期
offline region下线
pending open 打开region的请求已经被发送到服务器
opening 服务器已经打开region
open region已经被打开并且完全可以使用
pending close 关闭region的请求被送到服务器
closing 服务器正在处理要关闭的region
closed region服务器已经被关闭了
splitting 服务器开始切分region
split region已经被切分了
zookeeper
./hbase zkcli 启动
ls /hbase 显示hbase的znode节点
get /hbase/hbaseid HDFS上hbase.id的内容
/hbase/master master节点内容
/hbase/root-region-root root表所在的region机器
/hbase/rs 所有的region server
/hbase/shutdown 集群的启动时间
/hbase/splitlog 日志拆分,日志首先是unassigned状态然后被一个region服务器获取
/hbase/table 所有的表
/hbase/unassigned 未打开的region
复制
类似Mysql的bin-log复制,使用异步方式
LogEdit生命周期
1.每次的更新操作都会被记录到WAL中,另一个线程从日志中读取,当缓冲区满了或者到文件末尾了就将
内容发送到从集群
2.如果从集群没有反馈则重试若干次如果还是没有反馈则重新选择一台从集群备份
内部机制
1.挑选需要复制的目标服务器(挑选的数目为从集群个数的10%)
2.每个从集群都有对应的znode,每个znode都包含HLog。文件在可用之前被添加到每个集群的znode中,
这样所有的更新操作都会复制到自己的集群中
3.默认情况下会过滤掉分为GLOBAL类的且不属于目录表日至项的KeyValue,之后是限制每个从集群的同步
大小,默认是64M,所以三个从集群就是192M
4.如果没有启用同步复制,master日志清理线程会按照TTL删除旧的日志,如果当前日志在队列中则不会
删除
5.所有的对等服务器都放在
当有服务器宕机了,其他服务器会竞争的创建一个lock的znode,并将这个服务器的内容转移到自己的
目录下
下面假设有3个region,从集群id为2,zonde名字就是HDFS的文件名
/hbase/replication/rs/
1.1.1.1,60020.1234567890/
peers/
2/
1.1.1.1,60020.123
1.1.1.2,60020.1234567890/
(lock)
peers/
2/
1.1.1.2,60020.1214
1.1.1.3,60020.1234567890/
(lock)
peers/
2/
1.1.1.3,60020.1280
将1.1.1.2的内容移到这里
第九章 高级使用
Key的设计
KeyValue
row ColumnFamily ColumnQualifier Timestamp Value
越往后定位的性能就越差
每个column对应一个真实的文件,qualifier是和column存储在同一个文件中的,比如column为ff,quailfier为aa
则存储在实际的文件中的内容是ffaa
高窄的表 VS 短宽的表
1.假设e-mail的消息被放到了quailifer中,row key是用户id,这样如果用户不多,但是用户消息很多,会造成
行不多,但是单行的内容很多(有很多不同的qualifier),这样的设计很不好,因为HBase的split是按照行分割的,
qualifier过多,会造成这行内容过大(甚至超过了region大小),但HBase又不能对其split
2.如果将e-mail中的消息放到row key中,做成userId-message,这样每行的qualifier就是空的,每个row key就只有
一条数据了,但是行很多,这样的好处是HBase可以进行split
局部key扫描
key可以设计成这样的:
userid-date-messageId-attachmentId
因为timestamp是反序排列的,所以可以设计成:
Long.MAX_VALUE - <date-as-long>
时间连续数据
假设有N个region server,设计key为:
byte prefix = (byte)(Long.hashCode(timestamp) % <N of region>
byte[] rowkey = Bytes.add(Bytes.toBytes(prefix),Bytes.toBytes(timestamp);
那么key为:(假设有5个region server)
0myrowkey-1
4myrowkey-2
2myrowkey-3
这种设置的好处是分散了写的压力,可以让key分散到不同region server上,如果要加机器,客户端逻辑需要修改
另外这种方式对读不是很好,尤其是scan需要遍历多个region,可以用多线程处理,这样可以提供IO效率
交换key
前面一个列子,将timestamp和<N of regionserver>互换,这样可以提高写效率
OpenTSDB采用类似的方式<metric-id><base-timestamp> 这种格式作为key
随机key
byte [] rowkey = MD5(timestamp) 可以将key均衡的分散到多个region server上
对于写来说
顺序的key,按region取模分散key,时间顺序话-取摸的key,MD5随机化key 效率依次递减
对于写来说,正好相反,效率依次递增
时间有序
某个列index,可以对某个qualifier主题做降序排列,对某个qualifier发件人做升序排列
时间的降序排列:
Long.MAX_VALUE - <date-as-long> 这样越早的时间值越大,就在前面了
高级模式
跨语言的IDL(interface definition language)
avro
protocol buffers
二级索引
客户端自己管理
1.管理多个表,很灵活,索引存放到另外一张表中,但这种方式可能会造成数据不一致的问题
2.基于列簇的方式,通过列的Qualifier,这样可以保证事务性
这种方式先扫描row key,然后根据qualifier再更小的范围,之后根据version找出结果
ITHBase
IHBase
这两种都是差不多的,扩展了服务端代码,客户端需要使用额外的代码来操作索引
Coporcessor
在服务端做扩展,和IHBase类似,但是不需要替换任何服务端和客户端的代码
查询整合
客户端管理,facebook实现方式
1.每一行对应一条消息
2.列索引消息
3.版本是message id
4.值包含了附加信息,比如文档的位置
Lucene
HBasene
都是基于Lucene,存储的索引放在HBase中
Coprocessor
实现方式类似二级缓存
事务
ITHBase扩展了服务端和客户端的代码,提供了事务功能
Cage也提供了多个lock的功能
zookeeper本身也有两阶段提交,父znode下包含了多个子znode,每个子znode会标记是否成功或失败,客户端可以监控
其状态
Bloom filter
如果执行随机查找,在HFile发现key在数据索引中,则会执行一遍scan,发现没有再下一个HFile,一直到结束,可能会遍历多个HFile中的块,如果有bloom过滤器,可能只需要遍历很少几个HFile中的块即可
bloom过滤器需要原始文件的1/20大小,即文件1G,bloom过滤器占用50M空间
bloom有row和row+column两种类型,优先使用row过滤器
bloom会有判断错误率,默认1%,可以设置
即认为某个key在这些块中,但是实际上不存在的; 但如果认为key不存在,那么肯定是不存在
使用过程
1.判断cell的大小,如果小于阀值则不适用过滤器
2.如果cell数量超过阀值则使用row过滤器
3.检查读取模式如果是row则使用row模式,否则使用row+column模式
版本
需要保证各个region server的时间一致,通过NTP(network time protocol)保证
如果各resion server时间不一致,某台机器时间比其他机器早一个小时,时间戳使用系统时间,当这台机器split,并
将一半的region移动到另一台机器B,之后又执行插入到机器B,此时新插入值的时间反而比旧值的时间晚,这样按照
时间排序取出的版本就是旧值的数据
默认的版本为3,最大版本为2^31-1. 如果一个cell的key,column,quailifer都一样,时间戳不同,那么会按照最新的三个
版本递减排序,最高的在最上面.插入6条数据(其他都一样,只有时间戳为1-6),那么1-3时间戳的数据不会被显示,但是
也不会被马上删除,如果查看硬盘上的HFile文件,数据还是存在的,当执行了一次major_compact之后,这些过期的数据
就会被真正删除
如果删除数据时没有指定版本,默认为当前时间,小于当前时间的所有值都会被删除,如果新增的数据时间戳小于当前
时间,就查询不到了,这样新增不就成功,flush,major_compact之后,新增的旧值就可以插入了
自定义的版本
1.facebook inbox search,根据时间戳排序,这样就可以取到最新的数据了
2.使用一个全局的递增版本
第十章 集群监控
Metrics框架
继承自hadoop的metrics框架,将自身的信息发送给外部监控者
上下文,记录,度量
hbase提供了下列Context:
1.GangliaContext
2.FileContext
3.TimeStampingFileContext
4.CompositeContext
5.NullContext
6.NullContextWithUpdateThread
完整的度量全限定名为:
<context-name>.<record-name>.<metric-name>
度量框架包括以下类型
1.int型(IV)
2.long型(LV)
3.比率(R) 一段时间内的变化
a)比率计算是 操作数/花费时间
b)比率存储的是上一次的值
c)内部的计数器恢复到0
d)下一次的比率值才是当前值
e)计算后的比率值会返回给调用者
4.String型(S)
5.时间变化int型(TVI) 一段时间内的累计值
6.时间变化long型(TVL) 同上
7.时间变化比率(TVR) 计算一段时间完成的操作比列
a)操作个数 上一次执行后的总操作数
b)最小时间 一个时间完成的最短时间
c)最大时间 一个事件完成的最长时间
d)平均时间 事件完成的平均时间
8.持久的时间变化比率
对于长时间运行的处理,可以通过配置 hbase.extendedperiod配置,默认是不过期,如设置一个小时则为3600
master度量
1.集群请求(R) 所有region的请求和
2.重启后分割预写日志的时间(PTVR)
3.预写日志文件的总大小(PTVR)
region server度量
1.块缓存度量 记录总数,大小,空闲,失效的度量,以及命中数和未命中数,以及命中比列
2.压缩度量 压缩大小,压缩时间,压缩队列大小
3.memstore度量 memstore大小度量,flush队列大小,flush时间
4.存储度量 所有存储文件的状态度量
5.I/O度量 读写等待时间度量
6.杂项度量 读请求数度量,写请求数度量,regionserver数度量
压缩度量和memstore度量是在更新之间统计的,所以统计的值对于当前的值是有些延迟的
RPC度量
master和region使用相同的度量
1.RPC处理时间
2.RPC队列时间,RPC将所有的处理放在队列中,在队列中等待直到被取出处理的时间
JVM度量
1.内存使用度量 堆和非堆的使用情况
2.垃圾收集度量 垃圾收集时间,垃圾收集次数
3.线程度量 各种线程的状态
4.系统时间度量 各种子系统的日志信息,比如日志错误度量提供错误级别出现的时间
Info度量
提供各种内部值
1.date Hbase build时间
2.version hbase版本
3.revision 仓库的构建修订版
4.url 仓库的url
5.user 构建hbase的user
6.hdfsDate HDFS的构建时间
7.hdfsVersion 当前使用的hdfs版本
8.hdfsRevision 仓库的修订版构建的hdfs
9.hdfsUrl hdfs仓库url
10.hdfsUser 构建hdfs的user
Ganglia
Ganglia monitoring daemon(gmond) 每个机器都运行一个
Ganglia meta daemon(gmetad) master机器运行
Ganglia PHP web frontend web前端
安装好之后需要设置hadoop-metrics.properties
JMX
在hbase-env.sh中开启
配置hadoop-metrics.properties
hbase.class=org.apache.hadoop.metrics.spi.NullConetextWithUpdateThread
MBean地址:
hadoop:service=<service-name>,name=<mbean-name>
如:
hadoop:service=Master,name=MasterStatistics
hadoop:serivce=HBase,name=RPCStatistics-<port>
JConsole
JMX 远程API
git clone git://github/larsgeorge/jmxtoolkit.git
运行:
java -cp build/hbase-jmxtoolkit.jar org.apache.hadoop.hbase.jmxtoolkit.JMXToolkit -h
可以整合Cacti
Nagios
配合JMXToolkit脚本一起使用
第十一章 性能调优
垃圾收集
GC调优使用CMS垃圾收集器,可以调整垃圾收集的出现阀值(比如70%)
打印详细的日志输出
注意block cache(处理读)和memstore cache(处理写)的大小比列
memstore-local allocation buffer
MSLAB的实现原理(对照Arena Allocation,HBase实现细节):
创建一个2M(默认)的Chunk数组和一个chunk偏移量,默认值为0。
当Memstore有新的KeyValue被插入时,通过KeyValue.getBuffer()取得data bytes数组。将data复制到Chunk数组起始位置为chunk偏移量处,并增加偏移量=偏移量+data.length。
当一个chunk满了以后,再创建一个chunk。
所有操作lock free,基于CMS原语。
优势:
KeyValue原始数据在minor gc时被销毁。
数据存放在2m大小的chunk中,chunk归属于memstore。
flush时,只需要释放多个2m的chunks,chunk未满也强制释放,从而为Heap腾出了多个2M大小的内存区间,减少碎片密集程度。
开启MSLAB
hbase.hregion.memstore.mslab.enabled=true // 开启MSALB
hbase.hregion.memstore.mslab.chunksize=2m // chunk的大小,越大内存连续性越好,但内存平均利用率会降低,要比插入的单元格的数据大一些。
hbase.hregion.memstore.mslab.max.allocation=256K // 通过MSLAB分配的对象不能超过256K,否则直接在Heap上分配,256K够大了。
压缩
GZIP,LZO,Zippy/Snappy
GZIP最慢,Snappy最快
校验压缩是否安装成功
./hbase org.apache.hadoop.hbase.util.CompressionTest <path> <none|gz|lzo|snappy>
配置hbase-site.xml
<property>
<name>hbase.regionserver.codecs</name>
<value>snappy,lzo</value>
</property>
启用压缩
create 'testtable',{NAME=>'clof1',COMPRESS=>'GZ'}
启用压缩后并不是马上生效,因为数据还在buffer中,除非手动触发major_compact
优化切分和压缩
hbase.hregion.max.filesize 单个region的大小(整个集群范围)
或者可以指定某个column
预创建regions
./hbase org.apache.hadoop.hbase.util.RegionSplitter <TABLE>
或者用命令行
create 'testtable' 'colf',{SPLITS=>['row-100','row-200','row-300']}
负载均衡
将各个机器上的region数量平衡
hbase.balancer.period 多长时间间隔执行一次,默认是5分钟
hbase.balancer.max.balancing 每次执行多长时间,默认是上面参数的一般,也就是2分30秒
可以通过HBaseAdmin#balanceSwitch()来控制开关,可以通过shell命令控制
region的合并
./hbase org.apache.hadoop.hbase.util.Merge <table-name> <region-1> <region-2>
客户端API最佳实践
1.关闭自动flush
2.使用scanner缓存
3.设置scanner的范围
4.关闭ResultScanners
5.使用块缓存
6.优化需要加载的key(如使用KeyOnlyFilter)
7.关闭WAL(预写日志)
配置
1.减少zookeeper超时时间
2.增加处理线程hbase.regionserver.handler.count
3.增加堆大小(hbase-env.sh)
4.启用压缩
5.增加region大小(hbase.hregion.max.filesize)
6.调整block缓存大小(perf.hfile.block.cache.size),是一个浮点数(如0.2)
7.调整memstore大上限(hbase.regionserver.global.memstore.upperLimit)默认0.4,
hbase.regionserver.global.memstore.lowerLimit 默认0.35
8.增加blocking存储文件 hbase.hstore.blockingStoreFiles(多余这个数量的hfile时才会合并)
9.增加block multplier hbase.hregion.memstore.block.multiplier
如果memstore有hbase.hregion.memstore.block.multiplier倍数的
hbase.hregion.flush.size的大小,就会阻塞update操作
hbase.hregion.flush.size这个参数的作用是当单个Region内所有的memstore大小总和超过指定值时,
flush该region的所有memstore
负载测试
hbase自带的测试工具
./hbase org.apache.hadoop.hbase.PerformanceEvaluation
YCSB
yahoo cloud serving benchmark
java -cp build/ycsb.jar:db/hbase/lib* com.yahoo.ycsb.Client
第十二章 集群管理
操作任务
关闭一个region server
./hbase-daemon.sh stop regionserver
但是这样做不好,应当使用如下方式:
首先关闭bleance
./hbase shell blance_switch false
之后执行
./graceful_stop.sh HOSTNAME
重启一个region server
./graceful_stop.sh --restart --reload --debug HOSTNAME
增加一个region server
在master机器的conf文件中增加机器的hostname
可以在master机器上执行start-hbase.sh,它会跳过所有已经启动的机器,并启动新增加的机器
或者新增加的机器上单独执行脚本:
./hbase-daemon.sh start regionserver
增加一个备份master(和原先的master在一台机器上,可以创建多个备份)
./local-master-backup.sh start 1
在不同机器上创建备份
./hbase-daemon.sh start master --backup
还可以调用master-backup.sh 脚本完成备份
连接zookeeper,./hbase zkcli 可以观察到已经增加到备份znode上的机器
ls /hbase/backup-masters
数据任务
可以用.hadoop jar直接运行hbase-xxx.jar,运行前需要将$HBASE_HOME/lib下的一些jar拷贝到$HADOOP_HOME/lib下
zookeeper-xxx.jar
guava-xxx.jar
protobuf-java-xxx.jar
如下面一段是将testtable备份到hdfs的/hbase-backup目录下
./hadoop jar $HBASE_HOME/hbase.jar export testtable /hbase-backup/test-table
导入命令如下:
./hadoop jar $HBASE_HOME/hbase.jar import mybak /hbase-backup/test-table
直接运行./hadoop jar $HBASE_HOME/hbase.jar export/import 会有些更详细的参数
export和import是用mapreduce执行的,所以可以通过-D导入一些mapreduce参数
另外export支持start time,end time,prefix,regexp等参数
需要注意,import时指定的表必须先创建,否则会提示找不到表
使用hadoop的命令./hadoop distcp,将一个集群中的数据拷贝到另一个集群中
但是只能用停机备份,运行时备份会有问题
./hadoop jar $HBASE_HOME/hbase.jar copytable --new.name=new-tab old-tab
类似上面两个命令的整合,可以将一个旧表的数据导入新表中,同时还支持集群间的拷贝,
可以将旧表的数据拷贝到其他集群中,注意拷贝时候新指定的表必须存在
importtsv和completebulkload 这两个可以命令可以完成大量数据导入,比直接用API快一个数量级
importtsv可以直接导入,也可以分两步导入,先用importtsv导入到hdfs的临时目录中,生成内部的HFile格式,再用
completebulkload命令move到目标表中,completebulkload只是本地磁盘拷贝非常快
这两个命令需要用到mapreduce,如果没有配置mapred-site.xml则默认使用本地mapreduce,运行时会有问题
在mapred-site.xml中加一段:(注意端口不能和hdfs中定义的port一样)
<name>mapred.job.tracker</name>
<value>hdfs://ip:54311</value>
定义hadoop-env.sh,将hbase.jar,zookeeper.jar,guava.jar,protobuf.jar引入到HADOOP_CLASSPATH中
执行命令
./hadoop jar hbase.jar importtsv -Dimporttsv.bulk.output=/hbase-tmp -Dimporttsv.columns=HBASE_ROW_KEY,col_name
table_name /xx.txt
xx.txt中的数据格式如下:(默认是按\t分割的),可以通过-D参数指定
key-1 xxxxxxxxxx
key-2 xxxxxxxxxx
key-3 xxxxxxxxxx
运行之后/hbase-tmp目录下就有数据了,查看新表已经创建了,但是里面还没有数据,用completebulkload导入
./hadoop jar hbase.jar completebulkload /hbase-tmp table_name
导入很快,导入完成之后表中就有数据了,如果导入数据很大,原表中就有region,导入时候会执行切分工作
可能需要将hbase-site.xml放入hadoop-conf变量中,但我测试的时候没导入也成功了
Replication
需要在hbase-site.xml中增加一个配置hbase.replication=true
通过shell脚本增加一个对等集群(对等集群的规模可以和原集群不同)
add_peer '1','IP-2:2181:/hbase'
启动复制 start_replication
验证复制
./hadoop jar hbase.jar verifyrep
可以比较两个集群的两个表数据是否相等,可以基于时间增量比较
附加的任务
共存的集群
只能用于测试目的
需要修改hbase-site.xml中的下列属性:
hbase.rootdir
hbase.tmp.dir
zookeeper.znode.parent
hbase.master.port
hbase.master.info.port
hbase.regionserver.port
hbase.regionserver.info.port
conf目录和log目录需要另外拷贝一份
启动: $HBASE_HOME_CONF_DIR=conf.2 ./start-hbase.sh
故障排查
日志级别默认是DEBUG,在conf/log4j.properties中调整
./hbase hbck
首先是做一致性检查,检查.META.表中的数据是否对应到具体regionserver中
之后是完整性检查,如果加上了-fix会做一些修正工作,比如将未分配的region分配出去
或者重新分配region等
通过ulimit -n 可以查看datanode当前的处理上线
cat /proc/<PID of JVM>/limits
启用LZO压缩,需要单独安装LZO包,否则会报找不到类错误
zookeeper问题
1.因为页面换入换出导致处理响应变慢,zookeeper超时
通过vmstat 20 和free -m查看
2.GC时间过长导致的超时问题
3.机器负载过高导致超时问题(zookeeper可能和regionserver部署在相同物理机器上)
4.IO压力高导致超时问题
发表评论
-
Hadoop技术内幕 深入解析MapReduce架构设计与实现原理
2017-05-03 15:14 844Hadoop的一些重大改进 1.append,HD ... -
MySql Innodb存储引擎--表
2017-04-27 11:26 408表,页,段,记录 页的格式分析 ... -
MySql Innodb存储引擎--备份和优化
2017-04-24 17:07 793备份的目的 做灾难恢复:对损坏的数据进行恢复和还原 ... -
MySql Innodb存储引擎--锁和事务
2017-04-21 17:20 905lock和latch的比较 ... -
MySql Innodb存储引擎--文件和索引
2017-03-27 18:03 405MySql架构图 M ... -
MySql Innodb存储引擎--架构和引擎介绍
2017-03-23 10:40 654Mysql架构图 1 Con ... -
计算机程序的构造和解释
2017-02-15 18:02 534创建一个有理数 (define (ma ... -
破坏之王
2016-03-30 21:44 344不同阶段DDos攻击事件的特点 时期 ... -
UNIX网络编程 广播
2015-12-09 13:29 0........ -
UNIX网络编程 密钥管理套接字
2015-12-09 13:28 576... -
UNIX网络编程 路由套接字
2015-12-09 13:27 599.. -
UNIX网络编程 ioctl操作
2015-12-09 13:25 774.............. -
UNIX网络编程 非阻塞式I/O
2015-12-09 13:25 621....... -
UNIX网络编程 高级IO函数
2015-12-02 15:13 597套接字超时 在设计套接字的IO操作上设置超时的方法 ... -
UNIX网络编程 守护进程和inetd超级服务器
2015-11-24 20:37 538守护进程(daemon)是在后台运行且不与任何控制终 ... -
UNIX网络编程 名字与地址转换
2015-11-24 20:12 544...... ........ -
UNIX网络编程 基本UDP套接字编程
2015-11-24 20:05 1025..... ......... -
UNIX网络编程 套接字选项
2015-11-17 12:38 1417影响套接字选项的函数 getsockop 和 se ... -
UNIX网络编程 I/O复用 select和poll函数
2015-11-17 12:14 450................ -
UNIX网络编程 TCP客户/服务器程序示例
2015-11-17 12:13 452...............
相关推荐
hbase权威指南中文完整版pdf 12章+6附录
### HBase权威指南知识点概述 #### 一、引言与背景 - **大数据时代的来临**:随着互联网技术的发展,人类社会产生了前所未为的数据量。这些数据不仅数量巨大,而且种类繁多,传统的数据库系统难以应对这样的挑战。 ...
带有详细目录的 HBase权威指南中文版.pdf
《HBase权威指南》探讨了如何通过使用与HBase高度集成的Hadoop将HBase的可伸缩性变得简单;把大型数据集分布到相对廉价的商业服务器集群中;使用本地Java客户端,或者通过提供了REST、Avro和Thrift应用编程接口的...
《HBase权威指南》是一本深入探讨分布式大数据存储系统HBase的专业书籍,旨在为读者提供全面、详尽的HBase知识。这本书涵盖了从基础概念到高级应用,包括HBase的架构设计、数据模型、表设计策略、性能优化、监控与...
Hbase权威指南 随书源代码 源码包 绝对完整版 maven工程,带pom文件,可以直接作为一个完整工程导入eclipse等ide。
《HBase权威指南》是HBase领域的经典著作,旨在深入解析这款分布式大数据存储系统的方方面面。随书提供的源代码是理解书中理论与实践结合的关键。在本文中,我们将围绕HBase的核心概念、架构以及如何通过源代码学习...
《HBase权威指南》是Hadoop生态中关于分布式列式数据库HBase的重要参考资料,它深入浅出地介绍了如何利用HBase构建大规模数据存储系统。这本书与《Hadoop权威指南》一起,构成了理解大数据处理和存储的关键知识体系...
《HBase权威指南》是一本深入探讨分布式列式数据库HBase的专业书籍,其配套源码提供了书中所提及的示例代码和实践案例,方便读者更好地理解和应用HBase。以下将详细解析HBase的相关知识点。 HBase是建立在Apache ...
《HBase权威指南》是一本深入探讨分布式大数据存储系统HBase的专业书籍,其源代码的提供为读者提供了更直观的学习材料。HBase是基于Apache Hadoop的非关系型数据库(NoSQL),它在大规模数据存储方面表现卓越,尤其...
《HBase权威指南》是一本深入探讨Apache HBase这一分布式列式数据库的著作,源码则是作者为了辅助读者理解书中理论知识而提供的实践材料。HBase是构建在Hadoop文件系统(HDFS)之上的开源数据库,专为处理大规模数据...
《HBase权威指南》探讨了如何通过使用与HBase高度集成的Hadoop将HBase的可伸缩性变得简单;把大型数据集分布到相对廉价的商业服务器集群中;使用本地Java客户端,或者通过提供了REST、Avro和Thrift应用编程接口的...
HBase权威指南(中文版) 高清指南,想要的可以看看,~~~
《HBase权威指南》是一本深入探讨分布式列式数据库HBase的专业书籍,旨在帮助读者全面理解和掌握这一强大的大数据存储系统。HBase是构建在Apache Hadoop之上,专门为处理大规模数据而设计的非关系型数据库(NoSQL)...
HBase权威指南(中文版),最新最全介绍HBASE的。