Hbase是一个分布式,可扩展,支持海量数据存储的noSQL数据库
支持大量的数据存储
易拓展
自动切片,自动故
障转移
可以使用Java API编程
没有高级查询语句
延迟较MySql等关系型数据库大
需要分布式运行,需要一定的机器数量
namespace 类似于mysql database
rowkey:每条数据的唯一标识,hbase中的数据按照rowkey字典序排序的(1 10 100 101 11 20 21)
region:hbase分布式存储的基础,,建表时由split语句可以各region所包含startkey和stopkey,进而划分为不同的region分到不同的服务器中。
列簇:一组列的集合,同一个表中,拥有相同数量的列簇,列簇中可以拥有不同数量的列,列簇中列的宽度是动态的,在插入数据时指定。
store:每个region的每个列簇存储成为一个store,作为基础文件单元
version :Hbase对数据的修改实际上是put(时间戳,new val),这个修改不会删除旧值,而是将新值的数据加入到表中,建立列簇时可以指定version的值,Hbase最多保存该值数量的最新历史数据。
cell:由rowkey,列簇,列名,时间戳 唯一确定的单元
1.负责客户端的数据操作请求(DML)
2.region split,store file, compact
1.监听Region Server服务器状态
2.在其故障时启动故障转移机制:在其他服务器上创建新的region,将元数据与实际存储在HDFS上的数据关联
3.负责Region Server的负载均衡
4.负责表结构的操作(DDL)
为Hbase提供底层的数据存储服务,为Hbase提供高可用的支持
1.Master的高可用
2.Region Server 的状态监听
hbase:meta这个表中存储了所有表的元数据,包括所有region所在的服务器,region的名称,以及时间戳。
以下触发条件都会触发flush,但flush的最小单位是region,即每次触发flush都会flush该region的所有Store的MemStore,所以region的列簇不适合设置过多,工作中一般指定为1-2个
1.MemStore存储数据达到128M
2.region中所有的MemStore数据大小达到512M
3.RegionServer所有region的所有MemStore的数据总大小达到java_heap * 40% * 95%,此时会将当前RegionServer下的所有Region根据其所有MemStore占用大小排序,优先flush占用空间大的region
4.region距离上次flush已经达到一个小时
5.当WAL文件数量已经达到32个时
6.手动flush
Hbase数据存储于HDFS上,并不能像关系型数据库一样随意删改,实际上的删除只是标记删除,使该值不可见,等到下次major Compact时才会真正的删除。
考虑:标记删除一个范围内的数据,然后又添加了该范围内的数据,新增数据会被删除吗?
触发条件:小文件数达到3个时,会触发文件的合并
合并过程:合并时只是单纯的将多个小文件合并成一个大文件,不会删除或过滤过期版本。
合并结果:出现多个大文件
触发条件:7天一次
合并过程:将StoreFile的所有文件合并成一个大文件,会删除或过滤过期版本
合并结果:StoreFile下只有一个文件
默认创建表时只有一个Region,而一个Region只能存在于一个RegionServer上,是不支持分布式的。Hbase的RegionServer会在Region数据达到一定时,自动切分,分裂Region,再由Master分配到不同的Server上。
0.9-2.0
分裂标准:min(10G(默认), memStore flush.size * RegionNum^2) RegionNum是该服务器下的该表的Region个数
Hbase保存在HDFS上具体存储数据的物理文件,每个store每次flush内存区都会产生一个,以Hfile格式保存
写入的缓存,写入数据时先存入MemStore内存区,并在区中排序,触发flush后将区中数据落盘
Write-Ahead logfile,预写日志。为了防止MemStore内存区中的数据丢失,写入时会先数据将写到预写日志,内存区flush完成后会删除预写日志中的相应内容。也便于故障转移后的恢复。
1)Client先访问Zookeeper,获取hbase:meta表位于哪个RegionServer。
2)访问对应的RegionServer,获取hbase:meta表,根据读请求的namespace:table/rowkey,查询出目标数据位于哪个RegionServer中的哪个Region中。并将该table的region信息以及meta表的位置信息缓存在客户端的meta cache,方便下次访问(当触发负载平衡或故障转移后,需要重新缓存meta表)。
3)与目标RegionServer进行通讯;
4)将数据顺序写入(追加)到WAL;
5)将数据写入对应的MemStore,数据会在MemStore进行排序;
6)向客户端发送ack;
7)等达到MemStore的刷写时机后,将数据刷写到HFile。
1.向zk发起请求,找到meta表所在regionserver
2.zk返回meta所在的regionserver
3.向meta所在的regionserver请求读取meta
4.将读取到meta表缓存在内存中,方便下次读取,并从meta表中找到要读取数据所在的regionserver
5.从读缓存中查找数据
6.从memstore中查找数据
7.从StoreFile中查找数据
由RegionServer负责缓存热数据,便于访问
create_namespace 创建一个namespace
list_namespace 显示所有namespace
list_namespace_tables 查看一个namespace下的所有表
describe 查看一个表的属性
disable 禁用一个表
enable 启用一个禁用的表
create 创建一个表
drop 删除一个空namespace 或一个禁用的表
scan 扫描一个表
get 获取一个cell的值
put 添加行或修改行
delete 删除一个cell
deleteall 删除一行多个cell
count 统计表行数
truncate清空表
conf = HBaseConfiguration.create();//创建conf配置对象 conf.set("hbase.zookeeper.quorum", "hadoop106,hadoop107,hadoop108");//设置zk地址 connection = ConnectionFactory.createConnection(conf);//建立连接 admin = connection.getAdmin();//注册用户
DDL语言是admin对象来操作的
DML语言是table对象来操作的,获取table对象方法:
connection.getTable(TableName.valueOf("ns1_name:t1_name"));
table.close(); admin.close(); connection.close();
默认创建表时,只会产生一个Region,此时读写都从这个Region负载,性能表现较差,我们可以通过预分区的方式,结合rowkey 的设计和数据的平衡,预分区出合适的分区,并尽量将这些负载压力平分到这些Region上(而不是由框架自动分区)
长度限制:太长的Rowkey会占用大量的空间
唯一原则:Rowkey必须是唯一确定的,不可重复
散列原则:单调递增的Rowkey是不够优化的,不论对于预分区来说,还是对于系统自动分区来说,大部分写负载都只在一个Region上进行。Rowkey必须是散列的,才能均匀分布于各个Region上。
Rowkey常用设计方案:
1.生成随机数前缀 (就是加盐salting)
2.取hash值做Rowkey(不能对可能重复值进行此操作)
3.时间戳反转
适量增加RS占用的内存,能提高缓存能力,增加读写速度
Phoenix 的口号是:把SQl语句带给NOSQL数据库
它是apache 的一个开源框架,基于HBase,可以使用 JDBC API代替Hbase客户端API,以达到使用SQL操作Hbase的目的,Phoenix也支持二级索引。
hbase作为nosql关系型数据库在存储数据时拥有能够海量存储的特点,但也丧失了关系型数据库通过索引来快速查找到所需数据的优点。使用Phoenix给数据建立索引可以达到通过索引快速查找数据的作用。
1.安装Phoenix
2.将安装目录下的phoenix-5.0.0-HBase-2.0-server.jar拷贝到Hbase的lib目录下
3.启动
bin/sqlline.py #启动客户端 bin/queryserver.py#启动服务器
与SQL类似,具体查看
Grammar | Apache Phoenix
1.添加依赖
<dependencies> <dependency> <groupId>org.apache.phoenix</groupId> <artifactId>phoenix-queryserver-client</artifactId> <version>5.0.0-HBase-2.0</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.6</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty</artifactId> <version>1.10.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies>
2.加载对应JDBC
Class.forName("org.apache.phoenix.queryserver.client.Driver");
3.建立连接
connection = DriverManager.getConnection(url)
4.预定义语句
statement = connection.prepareStatement("upsert into person1 values(?,?)")//example statement.setString(1,"100"+i); statement.setString(2,"1100"+i);//填入参数 statement.execute();//直接执行 statement.executeBatch();//执行批处理 statement.executeUpdate();//执行更新 statement.executeQuery();//执行查询语句返回结果集 connection.commit();//事务提交
5.关闭资源
statement.close(); connection.close();
create table person2(id varchar primary key , name varchar ) salt_buckets=4;
自动加盐,如果没有预分区,指定多少个bucket就会有多少个region
全局二级索引是在原表外新建了一个表,用来建立索引字段与其他字段(rowkey,可以包括其他,默认索引表rowkey为col_name_rowkey)的连接,当使用该索引字段进行查询时,会自动使用该索引表,但如果所建立的索引字段中不包括其他字段,仍会进行全表扫描。如建立了person表中name 的索引表,当查询字段包括age,而索引表中没有包括age,仍会进行全局扫描。(可以通过先从索引表找出rowkey,再从rowkey获取age?)
create index my_index on table_name(col_name)[include(col1_name)]#根据字段col_name创建全局二级索引 include 字段col1_name
本地二级索引是在原表中插入一行对每一行源数据的映射,格式为__columnvalue__rowkey,索引表会被分到同一个region中,这样在读写时的效率都会很高,当根据索引查找字段时,会现根据索引找到rowkey 的值,再根据rowkey的值去找对应行的数据,所以可以查询出所有字段的值。
create local index my_index on table_name(col_name)# 根据字段col_name创建本地二级索引
Hbase只提供了row级别的事务等级,Phoenix提供了跨表和跨行的事务
具体参见
Transactions (beta) | Apache Phoenix
User-defined functions(UDFs) | Apache Phoenix