1.建表限定列数量,无法动态随意扩展。 2.MySQL单表数据库500w左右,数量小。(单表上限) 3.MySQL单表数据量过大,横向切分。(手动开发、复杂、稳定性低、效率低) 4.MySQL中单表的列过多,影响表的CRUD效率。(MySQL单表列最好不要超过30列) 5.MySQL单表的列过多,纵向切分。(手动开发、复杂、稳定性) 6.MySQL数据库允许空值,空值的空间是占用了的(海量数据面前造成空间浪费) 7.MySQL数据库中每个单元格,只能保存一个值(一个版本) 总结:(mysql存在问题) a.海量数据存储问题 b.实时检索问题 Hbase 数据库(海量存储、实时检索)
HBase的原型是Google的BigTable论文,受到了该论文思想的启发,目前作为Hadoop的子项目来开发维护,用于支持结构化的数据存储。
官方网站:http://hbase.apache.org
- 2006年Google发表BigTable白皮书
- 2006年开始开发HBase
- 2008年北京成功开奥运会,程序员默默地将HBase弄成了Hadoop的子项目
- 2010年HBase成为Apache顶级项目
- 现在很多公司基于HBase开发出了定制版,比如阿里云HBase
总结:
HBase是构建在HDFS之上的分布式、面向列的存储系统,在需要实时读写、随机访问的超大规模数据集的时候,可以使用HBase。
1.一个表数据量会超过500w,未来数据量会达到百亿条。 2.实时查询。(秒级 毫秒级读写) 原因: # 海量数据存储 一个表百亿行 百万列;(MySQL实战最大值500万行,30列) # 实时查询 1秒内查询得到结果。
# 1. 容量大 HBase单表百亿行,百万列。 # 2. 面向列 HBase存储是面向列,可以再数据存在以后动态增加新列和数据,并支持列数据的独立操作。 # 3. 多版本 HBase每个数据,可以同时保存多个版本,按照时间去标记。 # 4. 稀疏性 HBase每条数据的增删,并不是要操作所有的列,列可以动态增加,可以存在大量空白单元格,不会占用磁盘空间,这对于海量数据来讲,非常重要。 # 5. 扩展性 底层使用HDFS,存储能力可以横向扩展。 # 6. 高可靠性 底层使用HDFS,拥有replication的数据高可靠性。 # 7. 高性能 表数据达到一定规模,"自动分区",具备主键索引,缓存机制,使得HBase海量数据查询能达到毫秒级。
HBase | 关系型数据库 |
---|---|
数据库以**region **的形式存在 | 数据库以Table的形式存在 |
使用**行键 **(row key) | 支持主键PK |
使用行表示一条数据 | 一条数据用row代表 |
使用列 column、列族 column family | column代表列数据的含义 |
使用HBase shell 命令操作数据 | 使用SQL操作数据 |
数据文件可以基于HDFS,是分布式文件系统, 可以任意扩展,数据总量取决于服务器数量 | 数据总量依赖于单体服务器的配置 |
不支持事务、不支持ACID | 支持事务和ACID |
不支持表连接 | 支持join表连接 |
# namespace 命名空间 管理多个表,相当于Database。 hbase管理表的结构,在HDFS中对应一个文件夹。 # table 表 存储管理数据,相当于table。 hbase管理数据的结构,在HDFS中对应一个文件。 # column 列,每个列对应一个单元格。 # column family 列族 包含多个列,一组拥有相关业务含义列组成1个列族。 表中数据的列,要属于某个列族,所有的列的访问格式(列族:列名) # rowkey 主键 类似于id,用来唯一标识一条数据。 用来标记和检索数据的主键key。 # cell 单元格 由`row key+column family+column+version` 唯一确定的一条数据。 每个单元格可以保存值多个版本,按照时间排序,最新值在最前面。 # timestamp 时间戳 时间戳,每个单元格可以保存多个值,每个值有对应的时间戳,每个cell中,不同版本的数据倒叙排序,排在最前面的是最新数据。
1.主从架构 Zookeeper 1.保存HMaster管理表的元数据。 2.HMaster解决单点故障(两个HMaster--Zookeeper自动处理) 3.保存RegionServer从机状态信息。 HMaster(主) 1. 管理RegionServer及其状态。 2. 管理表的元数据。 3.接受DDL语句,建表、删除表、alter。 4.对HRegionServer做数据分布负载均衡。 HRegionServer(从) 1. 定时向HMaster报告自身节点状态信息。 2.保存表的数据部分。 3.接受并执行DML语句。 (添加-put、删除-delete、查询-get、scan) 没有修改,使用put覆盖代替修改; delete删除,不会立刻操作数据,打标记。 4.维护横向切分后的数据。 HRegion 一段表的数据(横向拆分)。即start key~end key之间的数据一个Region(拆分后的小表数据)。 store 横向拆分后的子表的纵向拆分。 一个Region内部,每个列族数据,单独存放叫做store。 namespace--table--region--store(1对多)
HRegionServer(和DataNode同一节点) 1. 存储表数据部分 2. put delete get scan等针对数据的操作 3. 定时向Master报告自身节点的状态 4. 管理表的数据的Table的数据
HMaster 1. Region Server状态的管理 2. 表的管理:create drop alter 3. 实现HRegionServer的数据负载均衡,平衡HRegion的分布
Zookeeper 1. 解决HMaster的单点故障问题 2. 存放HMaster管理的HRegionServer的状态信息,并通知HMaster 3. 存放HMaster管理的表的元数据信息 表名、列名、key区间等。
HRegion 表的横向切片的物理表现,大表的子表,有(startkey endkey),多行数据。 为了减小单表操作的大小,提高读写效率。
Store 1. 表的纵向切分的物理表现,按照列族作为切分。 2. 按照列族查询,仅需要检索一定范围内的数据,减少全表扫描。
地址:https://mirrors.tuna.tsinghua.edu.cn/apache/hbase/
- 安装并配置hadoop
[root@hbase40 installs]# jps 3440 Jps 3329 SecondaryNameNode 3030 NameNode 3134 DataNode
- 安装并配置单机zookeeper
[root@hbase40 installs]# jps 3329 SecondaryNameNode 3509 QuorumPeerMain 3030 NameNode 3595 Jps 3134 DataNode [root@hbase40 installs]# zkServer.sh status ZooKeeper JMX enabled by default Using config: /opt/installs/zookeeper3.4.14/bin/../conf/zoo.cfg Mode: standalone
- 设置好日期同步
# 确保HBase主机的时间和网络时间一致 # 查看linux系统时间 [root@hbase40 installs]# date # 重启chronyd服务,同步系统时间。 [root@hbase40 installs]# systemctl restart chronyd [root@hbase40 installs]# date 2020年 04月 12日 星期日 22:51:31 CST
# 1. 安装hbase
1. 解压HBase [root@hbase40 modules]# tar zxvf hbase-1.5.0-bin.tar.gz -C /opt/install/ 2. 配置环境变量 #JAVA export JAVA_HOME=/opt/install/jdk1.8 export PATH=$PATH:$JAVA_HOME/bin # HADOOP export HADOOP_HOME=/opt/install/hadoop2.9.2/ export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin # zookeeper export PATH=$PATH:/opt/install/zookeeper3.4.14/bin/ # HBase export HBASE_HOME=/opt/install/hbase1.5 export PATH=$PATH:$HBASE_HOME/bin 3. 加载profile配置 source /etc/profile
# 2. 初始化配置文件 hbase-env.sh hbase-site.xml regioniservers
# 1 -------------------hbase-env.sh-------------------- # 配置Java_home export JAVA_HOME=/opt/installs/jdk1.8 # 注释掉如下2行。 # export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m -XX:ReservedCodeCacheSize=256m" # export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m -XX:ReservedCodeCacheSize=256m" # 禁用内置zookeeper export HBASE_MANAGES_ZK=false
# 2. -------------------hbase-site.xml------------------------- <configuration> <!-- hbase的访问hdfs入口,ns HaHadoop的虚拟命名空间 --> <property> <name>hbase.rootdir</name> <value>hdfs://hbase40:9000/hbase</value> </property> <!-- 使用分布式模式 --> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> <!-- zookeeper集群地址,端口默认2181不需要指定 --> <property> <name>hbase.zookeeper.quorum</name> <value>hbase40</value> </property> <!--zookeeper的默认工作目录,就是data目录,要和zookeeper的一样--> <property> <name>hbase.zookeeper.property.dataDir</name> <value>/opt/install/zookeeper3.4/data/</value> </property> <!--配置hdfs的hdfs的flush方式:否则该版本启动会报错--> <property> <name>hbase.unsafe.stream.capability.enforce</name> <value>false</value> </property> </configuration>
# -------------------配置regionservers(regionserver所在节点的ip) ------------------- hadoop30
# 3. 启动hbase
# 启动顺序 zookeepr->hdfs->hbase # 1. 启动HMaster [root@hbase40 install]# hbase-daemon.sh start master # 关闭 [root@hbase40 install]# hbase-daemon.sh stop master # 2. 启动HRegionServer [root@hbase40 install]# hbase-daemon.sh start regionserver # 关闭 [root@hbase40 install]# hbase-daemon.sh stop master # 3.全部启动 [root@hbase40 install]# start-hbase.sh
# 4. 验证访问 1. java进程查看 [root@hadoop30 installs]# jps 4688 NameNode 5618 HMaster 5730 HRegionServer 4819 DataNode 3509 QuorumPeerMain 6150 Jps 4984 SecondaryNameNode 2. HMaster WebUI查看 http://ip:16010 3. 进入客户端 hbase shell hbase(main):001:0>
注意:命令行中,删除字符用
ctrl+backspace
HBase shell 命令结尾没有“分号”
# 进入客户端: ./hbase shell # 退出客户端命令: quit/exit # 帮助 help
默认存在一个default的namespace
#1. 查看namespace list_namespace #2. 创建namespace create_namespace "命名空间名字"` #3. 删除namespace drop_namespace "命令空间名字"
# 1. 查看所有表 hbase(main):008:0> list (目前没有表) TABLE 0 row(s) in 0.0320 seconds => [] hbase(main):009:0> # 2. 查看某个namespace下的所有表 hbase(main):001:0> list_namespace_tables "test" TABLE user 1 row(s) in 0.2980 seconds # 3. 创建表 (如果不指定namespace,则放在默认default下) 语法:create "test:user","info","edu" hbase(main):004:0> create "test:user","info","edu" 0 row(s) in 2.5580 seconds => Hbase::Table - test:user # 4. 查看表结构(表名、列族相关信息) 语法:desc "namespace:表名字" hbase(main):002:0> desc "test:user" Table test:user is ENABLED test:user COLUMN FAMILIES DESCRIPTION {NAME => 'edu', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => ' true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'} {NAME => 'info', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'} 2 row(s) in 30.3330 seconds hbase(main):003:0> # 5. 删除表和禁用表(HBase对表进行了加锁,若想删除必须先进行禁用) 语法:disable "namespace:表名字" hbase(main):010:0> disable "cyf:user" 0 row(s) in 2.2970 seconds hbase(main):011:0> 语法:drop "namespace:表名" hbase(main):011:0> drop "cyf:user" 0 row(s) in 1.3540 seconds hbase(main):012:0>
1.HBase添加数据,会按照rowkey进行排序(默认字典排序)。
2.HBase查询数据,查询结果按照rowkey排序展示。
# 1. 添加数据(每次只能添加一个列,一个put代表一个数据) # 表 rowkey 列族:列名 值 语法:put "namespace:表","rowkey","列族1:列名1","值" put "test:user","1001","info:name","cyf" put "test:user","1001","info:age","66" put "test:user","1001","info:mobile","110000" # 2. 根据rowkey查找数据(get) 语法:get "namespace:表名","rowkey" get "test:user","1001" hbase(main):006:0> get "test:user","1001" COLUMN CELL info:age timestamp=1624148527287, value=66 info:mobile timestamp=1624148627742, value=110000 info:name timestamp=1624148314733, value=lihao 1 row(s) in 0.0130 seconds hbase(main):007:0> # 3. 根据rowkey和列族查找部分列 语法:get "namespace:表名","rowkey","列族:列" get "test:user","1001","info:name" hbase(main):001:0> get "test:user","1001","info:name" COLUMN CELL info:name timestamp=1624148314733, value=lihao 1 row(s) in 30.3270 seconds hbase(main):002:0> # 4. scan 查询表中所有数据(scan 多行查询) 语法:scan "namespace:表名" scan "test:user" hbase(main):006:0> scan "test:user" ROW COLUMN+CELL 1001 column=info:age, timestamp=1624148527287, value=66 1001 column=info:mobile, timestamp=1624148627742, value=110000 1001 column=info:name, timestamp=1624148314733, value=lihao 1002 column=info:age, timestamp=1624149095675, value=26 1002 column=info:name, timestamp=1624149028019, value=cyf 1003 column=info:age, timestamp=1624149122537, value=15 1003 column=info:name, timestamp=1624149045376, value=fjl 3 row(s) in 0.0640 seconds hbase(main):007:0> # 5.查询表中前2条数据(scan) 语法:scan "namespace:表名",{LIMIT=>2} scan "test:user",{LIMIT=>2} # LIMIT注意事项: LIMIT必须大写 等号:=> hbase(main):007:0> scan "test:user",{LIMIT=>2} ROW COLUMN+CELL 1001 column=info:age, timestamp=1624148527287, value=66 1001 column=info:mobile, timestamp=1624148627742, value=110000 1001 column=info:name, timestamp=1624148314733, value=lihao 1002 column=info:age, timestamp=1624149095675, value=26 1002 column=info:name, timestamp=1624149028019, value=cyf 2 row(s) in 0.0350 seconds hbase(main):008:0> # 6. 使用start row 和 end row 范围查找(1002~1003前闭后开) 语法:scan "namespace:表名",{STARTROW=>"",ENDROW=""} scan "test:user",{STARTROW=>"1002",ENDROW=>"1004"} hbase(main):013:0> scan "test:user",{STARTROW=>"1002",ENDROW=>"1004"} ROW COLUMN+CELL 1002 column=info:age, timestamp=1624149095675, value=26 1002 column=info:name, timestamp=1624149028019, value=cyf 1003 column=info:age, timestamp=1624149122537, value=15 1003 column=info:name, timestamp=1624149045376, value=fjl 2 row(s) in 0.0270 seconds hbase(main):014:0> # 7. 查询rowkey=“1001”并且向后查询三条,使用start row和limit查找 语法:scan "namespace:表",{START=>"10001",LIMIT=>3} scan "test:user",{STARTROW=>"10001",LIMIT=>2} hbase(main):014:0> scan "test:user",{STARTROW=>"10001",LIMIT=>2} ROW COLUMN+CELL 1001 column=info:age, timestamp=1624148527287, value=66 1001 column=info:mobile, timestamp=1624148627742, value=110000 1001 column=info:name, timestamp=1624148314733, value=lihao 1002 column=info:age, timestamp=1624149095675, value=26 1002 column=info:name, timestamp=1624149028019, value=cyf 2 row(s) in 0.0280 seconds hbase(main):015:0> # 8. 修改数据(本质上是覆盖--put) 语法:put "namespace:表","rowkey","列族:列名","值" put "test:user","1001","info:name","yf" hbase(main):005:0> put "test:user","1002","info:name","yf" 0 row(s) in 0.0110 seconds hbase(main):006:0> scan "test:user" ROW COLUMN+CELL 1001 column=info:age, timestamp=1624148527287, value=66 1001 column=info:mobile, timestamp=1624148627742, value=110000 1001 column=info:name, timestamp=1624157416165, value=lihao 1002 column=info:age, timestamp=1624149095675, value=26 1002 column=info:name, timestamp=1624157430474, value=yf 1003 column=info:age, timestamp=1624149122537, value=15 1003 column=info:name, timestamp=1624149045376, value=fjl 3 row(s) in 0.0170 seconds hbase(main):007:0> # 9. 删除数据(删除某个cell) 语法:delete "namespace:表","rowkey","列族:列名" delete "test:user","1001","info:mobile" hbase(main):007:0> delete "test:user","1001","info:mobile" 0 row(s) in 0.0370 seconds hbase(main):008:0> scan "test:user" ROW COLUMN+CELL 1001 column=info:age, timestamp=1624148527287, value=66 1001 column=info:name, timestamp=1624157416165, value=lihao 1002 column=info:age, timestamp=1624149095675, value=26 1002 column=info:name, timestamp=1624157430474, value=yf 1003 column=info:age, timestamp=1624149122537, value=15 1003 column=info:name, timestamp=1624149045376, value=fjl 3 row(s) in 0.0250 seconds hbase(main):009:0> # 10. 删除某个rowkey对应的整行数据(deleteall) 语法:deleteall "namespace:表","rowkey" deleteall "test:user","1001" hbase(main):009:0> deleteall "test:user","1001" 0 row(s) in 0.0170 seconds hbase(main):010:0> scan "test:user" ROW COLUMN+CELL 1002 column=info:age, timestamp=1624149095675, value=26 1002 column=info:name, timestamp=1624157430474, value=yf 1003 column=info:age, timestamp=1624149122537, value=15 1003 column=info:name, timestamp=1624149045376, value=fjl 2 row(s) in 0.0110 seconds hbase(main):011:0> # 11. 统计表中所有数据 # HBase没有外键,也没有表连接 语法:count "namespace:表" count "test:user" hbase(main):011:0> count "test:user" 2 row(s) in 0.0360 seconds => 2 hbase(main):012:0>
# 修改表的指定列族中数据版本数量。 # alter "namespace:表",{NAME=>"info",VERSIONS=>2} # alter "test:user",{NAME=>"info",VERSIONS=>2} hbase(main):012:0> alter "test:user",{NAME=>"info",VERSIONS=>2} Updating all regions with the new schema... 0/1 regions updated. 1/1 regions updated. Done. 0 row(s) in 3.6380 seconds hbase(main):013:0> desc "test:user" Table test:user is ENABLED test:user COLUMN FAMILIES DESCRIPTION {NAME => 'edu', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => ' true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'} {NAME => 'info', BLOOMFILTER => 'ROW', VERSIONS => '2', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'} 2 row(s) in 0.0410 seconds hbase(main):014:0> # 同一个cell添加2次数据。 hbase(main):014:0> put "test:user","1002","info:name","aaa" 0 row(s) in 0.2620 seconds hbase(main):015:0>put "test:user","1002","info:name","bbb" 0 row(s) in 0.0290 seconds # 查看多版本 hbase(main):016:0> get "test:user","1002",{COLUMN=>'info:name',VERSIONS=>2} COLUMN CELL info:name timestamp=1624158383906, value=bbb info:name timestamp=1624158364824, value=aaa 1 row(s) in 0.0360 seconds hbase(main):017:0> # 表的列族的VERSIONS=>2表示的该列族的数据,要保存2个版本。如果put3次,则保留最新的版本。 # 使用get和scan查询,默认查询到的只有最新版本数据 # 查询多个版本 get "namespace:表","rowkey",{COLUMN=>"列族:列",VERSIONS=>数字}
HBase Java API 1.Configuration:访问HBase配置信息。 2.Configuration:hbase数据库连接,操作表的数据。 3.Admin:HBase客户端连接,操作表结构 namespace
HBase 表示数据(数据查询 Delete 表 namespace)模型的API 1.NamespaceDescriptor 表示namespace命名空间。
API | 含义 | 创建 |
---|---|---|
Configuration | 配置文件 | HBaseConfiguration.create(); |
Connection | 连接,用来操作数据 | ConnectionFactory.createConnection(conf); |
Admin | 客户端,用来操作元数据 (namespace和table结构) | conn.getAdmin(); |
NamespaceDescriptor | 命名空间相当于database | NamespaceDescriptor.create(“btrc”).build(); |
TableName | 表名 | TableName.valueOf(“btrc:user”); |
HTableDescriptor | 表 | new HTableDescriptor(tablename); |
HColumnDescriptor | 列族 | new HColumnDescriptor(“info”); |
Put | 添加数据 | new Put(Bytes.toBytes(“1001”)); |
Delete | rowkey的删除条件 | new Delete(Bytes.toBytes(“1001”)); |
Get | scan单行查询器 | new Get(Bytes.toBytes(“1019”)); |
Scan | scan多行查询器 | new Scan(); |
Result | 查询结果集(单条结果) | table.get(get); |
ResultScanner | 查询结果集(N条结果) | table.getScanner(scan); |
依赖
<properties> <!--定义版本号--> <hbase.version>1.5.0</hbase.version> <java.version>1.8</java.version> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-client</artifactId> <version>${hbase.version}</version> </dependency> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-common</artifactId> <version>${hbase.version}</version> </dependency> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-protocol</artifactId> <version>${hbase.version}</version> </dependency> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-server</artifactId> <version>${hbase.version}</version> </dependency>
HBase客户端连接编码步骤
//将hbase中的conf中的 **hbase-site.xml**放到resource配置文件目录中。 public void test2() throws Exception{ //1.初始化配置 Configuration conf = HBaseConfiguration.create(); conf.set("hbase.zookeeper.quorum","192.168.153.40"); //2.创建Hbase连接 Connection conn = ConnectionFactory.createConnection(conf); //3.获得操作表和namespace结构的admin Admin admin = conn.getAdmin(); //4.java中创建namespace对象 NamespaceDescriptor btrc = NamespaceDescriptor.create("btrc").build(); //5.执行创建namespace操作 admin.createNamespace(btrc); //释放资源 admin.close(); }
表相关的API creat "namespace:表",”列族“ 表名字:TableName TableName.valueOf("namespace:”表名") 表:HTableDesscriptor new HTableDesscriptor(”table“); 列族:HColumnDescriptor new HColumnDescriptor("列族名字"); 创建表: ①获得表名字 ②创建Table ③创建列族 ④将列族,装配在table中 ⑤创建表:admin
# 创建namespace //1. 构建namespace信息。 NamespaceDescriptor btrc = NamespaceDescriptor.create("btrc").build(); //2. 创建namespace admin.createNamespace(btrc);
# 创建表 public void test3() throws Exception{ //1.初始化配置文件 Configuration conf = HBaseConfiguration.create(); conf.set("hbase.zookeeper.quorum","192.168.153.40"); //启动显示日志 BasicConfigurator.configure(); //2.获得hbase连接 Connection conn = ConnectionFactory.createConnection(conf); //3.获得admin Admin admin = conn.getAdmin(); //1.创建表名 TableName tableName = TableName.valueOf("btrc:persons"); //2.构造表 HTableDescriptor table = new HTableDescriptor(tableName); //3.构造列族 HColumnDescriptor info = new HColumnDescriptor("info"); HColumnDescriptor edu = new HColumnDescriptor("edu"); //4.将列族装配在table中 table.addFamily(info); table.addFamily(edu); //5.admin创建表 admin.createTable(table); admin.close(); }
判断表是否存在
# 操作表,使用admin //1. 创建表名 TableName tableName = TableName.valueOf("btrc:persons"); //2. 判断表是否存在 boolean b = admin.tableExists(tableName); System.out.println(b?"存在":"不存在");
# 操作数据使用conn **Bytes是HBase提供的进行字节和java数据类型转化的工具类 public void test2() throws Exception{ //0.获得连接 Connection conn = HBaseUtil.getConnection(); //1.获得table表 TableName tableName = TableName.valueOf("btrc:persons"); Table table = conn.getTable(tableName); //2.创建一个put Put put = new Put(Bytes.toBytes("1001")); //3.向put中绑定添加的数据(列族:列,“值”) put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("name"),Bytes.toBytes("liqinghua")); put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("age"),Bytes.toBytes("24")); //4.使用table,添加put的数据---将put放入table中 table.put(put); table.close(); }
public void test5() throws Exception{ //0.获得连接 Connection conn = HBaseUtil.getConnection(); //1.获得table表 TableName tableName = TableName.valueOf("btrc:persons"); Table table = conn.getTable(tableName); //2.创建一个put Put put = new Put(Bytes.toBytes("1001")); //3.向put中绑定添加的数据(列族:列,“值”)修改实质即为重新添加 put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("name"),Bytes.toBytes("wangyuxi")); //4.使用table,添加put的数据---即修改数据 table.put(put); table.close(); }
public void test3() throws Exception{ //0.获得连接 Connection conn = HBaseUtil.getConnection(); //1.先获得table Table table = conn.getTable(TableName.valueOf("btrc:persons")); //3.创建删除Delete Delete delete = new Delete(Bytes.toBytes("10012")); //4.调用table.delete(delete)----执行删除 table.delete(delete); }
根据rowkey单条查询(get)。
public void test6() throws Exception{ //1.获得连接 Connection conn = HBaseUtil.getConnection(); //2.获得表table TableName tableName = TableName.valueOf("btrc:persons"); Table table = conn.getTable(tableName); //3.封装查询操作Get(rowkey) Get get = new Get(Bytes.toBytes("1008")); //4.执行查询,获得查询结果Result Result result = table.get(get); //5.处理结果Result info-name,info-age byte[] names = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")); byte[] ages = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")); //将字节name转换String name //将字节name转换Int age String name = Bytes.toString(names); int age = Bytes.toInt(ages); System.out.println("name"+name); System.out.println("age"+age); }
多条查询(scan-rowkey=1001,查询4条)
scan "btrc:persons",{STARTROW=>"1001",LIMIT=>4} public void test7() throws Exception{ //1.获得连接 Connection conn = HBaseUtil.getConnection(); //2.获得表table TableName tableName = TableName.valueOf("btrc:persons"); Table table = conn.getTable(tableName); //3.创建Scan多行查询扫描器 Scan scan = new Scan(); //4.设置Scan扫描器(列族,起始rowkey,limit) scan.addFamily(Bytes.toBytes("info")); scan.withStartRow(Bytes.toBytes("1001")); scan.setLimit(4); //5.执行扫描--table.getScanner(scan)得到ResultScanner ResultScanner scanner = table.getScanner(scan); //6.处理结果,遍历ResultScanner得到数据 for (Result result : scanner) { byte[] namebyt = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")); byte[] agebyt = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")); String name = Bytes.toString(namebyt); int age = Bytes.toInt(agebyt); System.out.println(name+age); } table.close(); }
# HBase的Region分区的意义 HRegion: 1.HBase中数据存在RegionServer管理的Region中。 2.一旦Region数据量达标到一定规模,拆分。() 3.每个region(startkey---endkey) Meta表(元数据表) 1.表 rowkey区间范围,每条信息范围对应一个Region,对应一个Region所在RegionServer机器。 方便客户端(添加 查询)快速定位到Region。
# 客户端查询数据执行原理 1.客户端访问zookeeper:(目的:获得meta表的位置) HMaster地址 HRegionServer地址 hbase:meta表数据所在regionServer地址 2.查询meta所在机器,确定rowkey和user对应的Region在哪个RegionServer 3.请求Region所在RegionServer,数据检索 4.获得查询结果。
# 客户端写入数据执行原理 1.请求zookeeper 获得meta表位置。 2.请求meta所在RegionServer节点确定表,rowkey并返回对应region所在的RegionServer服务节点。 3.请求RegionServer向里面写入数据。 **注意 ①将写操作记录在日志(HLog、WAL:write ahead log) ②regionserver接受数据写入内存中。写入数据的内存:Memstore。
# Region split的原因 1.随着put操作,Region空间不断增加,导致put delete get需要定位rowkey的时间逐渐增加。(效率降低) # Region split 分区 2.HBase,当Region达到一定条件的时候,会对Region进行切分,并且将切分后的Region交给不同的RegionServer管理。 a.根据rowkey在region定位数据,速度变快 b.Region数据在RegionServer分布管理负载均衡。(合理利用硬件资源) # Region split 分区时机 3.Region Split的时机 hbase.hregion.memstore.flush.size=128M 分区时机: Region数量(平方)*hbase.hregion.memstore.flush.size=Region分区时的大小 Region分区时机: 128M --- 512M --- 1152M---....10GB---10GB---10GB # 默认Region split 方案的缺点 由于HBase的默认分区没有考虑实际业务场景中rowkey规律: a.拆分后的Region的后续增长极其不均衡,导致数据倾斜。 b.访问Region中的数据,访问的概率也不同,访问负载不均衡,数据热点问题。
Region Split 分区
原因(为啥)
提高Region的负载和读写效率。
说明
Region一拆为二(平均)
默认分区机制
Region中数据超过128M、512M、1152M… *Region数量2hbase.hregion.memstore.flush.size … 10G、10G
查看参数
hbase.hregion.memstore.flush.size=128M hbase.hregion.max.filesize=10G
问题
默认分区容易导致数据倾斜,硬件资源无法充分利用。
预分区即创建表的时候,决定分区,Region的个数,每个Region维护的rowkey区间。
目的:防止数据热点,防止数据倾斜。(均衡分布数据到不同Regionc)
rowkey设计核心操作。
为什么
- 增加读写效率。(多个region分布在不同的RegionServer中,可以提高并发效率)
- 尽量保证每个Region中的数据量相当,防止数据倾斜。(合理利用计算资源)
分区的效果
每个Region维护一对StartKey和EndKey,限定维护输入rowkey范围。
添加数据时,讲rowkey放入匹配的region中。
创建表时分区,手动指定(分区方案)
命令:
create "namespace:表","列族",SPLITS=>["100000","200000","300000","400000"]
总结:按照splits中指定的分区key,分成4个区。
效果:
从文件中手动指定。
- 创建分区文件,输入分区key信息。
分区键的文件:(splits.txt)
10000
20000
30000
- 使用文件创建分区
create “btcr:t_users1”,“info”,SPLITS_FILE => “/opt/install/hbase1.5/splits.txt”
java代码分区:
admin.createTable(htable,splitkeys);
RowKey作用
- 读写数据时通过 RowKey 找到对应的 Region;
- Region、Store、Store File中的数据,会按照rowkey的字典顺序排序。
- 通过 get 方式,指定 RowKey 获取唯一一条记录
- 通过 scan 方式,设置 startRow 和 stopRow 参数进行范围匹配
设计原则
- 唯一,不能重复
- Region负载均衡,散列,后添加的数据,总是能均匀的散部在不同的Region中。
- 避免热点数据,避免访问量频繁的数据,分布在同一个或者两个Region中。
- 满足业务查询需求。
技巧
- 进行查询的时候,根据RowKey从前向后匹配
- 结合业务场景特点,选择合适的字段来做为RowKey,并且按照查询频次来放置字段顺序
- 宏观散列,局部排序。
案例
1. 查询某用户在某应用中的操作记录 hash(userid) + appid + timestamp 2. 查询某用户在某应用中的操作记录(优先展现最近的数据) hash(userid) + appid + (Long.Max_Value - timestamp) 3. 查询某用户在某段时间内所有应用的操作记录 hash(userid) + timestamp + appid 4. 查询某用户的基本信息 hash(userid)
1.client不断向RegionServer维护的Region中添加数据。 Region维护的数据存在于内存中。 client向HBase写入数据,Memstore。 说明:Region维护在内存中,目的:写入数据和删除块。 2.问题: Region数据量过大,RegionServer内存不足。 3.解决问题: HBase监控,当Region中数据达到条件值,将Region中Store,分别向HDFS进行刷写flush,将数据写入到HDFS文件中。 列族数量==store数量:每个store对应hdfs中的一个文件夹。 结论:每次达到刷写条件,就将store数据flush到hdfs文件中,随着数据增长,会不断刷写文件。(有很多小的store file) 4.flush时机 a.Region Server整个服务总内存占用达到40%,执行全局刷写:(全局flush:影响客户端操作,该flush会暂停客户端读写操作) hbase.regionserver.global.memstore.size b.当MemStore持续写入1h,触发局部刷写: hbase.regionserver.optionalcacheflushinterval c.当单个Region中的数据文件大小超过128M(局部刷写): hbase.hregion.memstore.flush.size b和c只要达到一个条件就进行刷写。 d.手动flush flush "namespace:表名" HBase刷写到hdfs的存储位置:http://192.168.153.40:50070/explorer.html#/hbase/data/btrc/persons/19a0c4f38481013611b4102c32a1c761/info
说明
简言:持久化,保护数据不丢失。
将RegionServer中内存中的数据Memstore,写入到硬盘中。
图
时机
- Region Server整个服务总内存占用达到40%该flush会暂停客户端读写操作
对应参数:hbase.regionserver.global.memstore.size
- MemStore持续写入1h。
对应参数:hbase.regionserver.optionalcacheflushinterval
- 单个Region中的数据文件大小超过128M。
对应参数:hbase.hregion.memstore.flush.size
手动flush
命令:
flush "namespace:表名"
1.问题: 随着HBase运行,HBase基于HDFS中,会不断的刷写成小的store file文件(几十MB) 总结:大量刷写次数,产生大量的小的刷写文件store file,查询storefile中的数据,需要将region所管理所有storefile遍历才能查到? 问题:大量的小storefile在内存中,遍历所有storefile查询效率。 a.小文件多 b.小文件整体没有顺序。 c.HDFS不适合管理小文件,文件管理效率也会降低。 2.解决方案: store File compaction 合并
原因
storefile小文件过多,查询时,需要遍历所有文件,效率低。
storefile中遍布过期数据,占用空间,且查询效率低。
说明
简言:为提高检索效率,合并store
图
分类和时机
- (局部合并) minor compact
将部分相邻的store合并中等文件。
特点:少量相邻文件的合并。 发生频率较高,不影响性能。手动命令:
compact "namespace:表名"
- (全局合并) majorcompact
将某个表:region:列族:所有storefile进行全局合并,产生一个文件。
特点: 1. 全局的所有store file文件的合并(数据量大)。 2. 去除删除被覆盖的文件。(删除过期版本的数据、delete命令删除过的数据起始还存在着,只不过打了标记) **删除过期数据(Region内存中 小Storefile中数据不会真正删除) 3. 特别消耗RegionServer的性能资源。(重点) 时机:每7天执行一次:参数:hbase.hregion.majorcompaction
一般手动触发。
手动触发命令:
major_compact "namespace:表名"
# 1.Mestore Region内存中 特点: (内存) (数据最新的) (有序) # 2.BlockCache(LRU) HBase缓存中。 缓存策略:LRU(数据淘汰机制):保留最近最新使用多的数据。 # 3.磁盘storeFile(每个小file中rowkey是有序的) 磁盘的检索速度慢是因为寻道。 # 4.磁盘合并大storeFile(减少file数量,可以提高磁盘的检索效率) 1.磁盘storeFile文件数量少,减少遍历。 2.文件内以及文件在磁盘中,rowkey有序,代码检索还是磁盘寻道大大节省了时间
<!--hadoop依赖--> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-common</artifactId> <version>${hadoop.version}</version> </dependency> <!--hdfs依赖--> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-hdfs</artifactId> <version>${hadoop.version}</version> </dependency> <!--mapreduce依赖--> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-mapreduce-client-core</artifactId> <version>${hadoop.version}</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-mapreduce-client-common</artifactId> <version>${hadoop.version}</version> </dependency> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-mapreduce-client-jobclient</artifactId> <version>${hadoop.version}</version> </dependency> <!-- hbase依赖 --> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-client</artifactId> <version>${hbase.version}</version> </dependency> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-common</artifactId> <version>${hbase.version}</version> </dependency> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-protocol</artifactId> <version>${hbase.version}</version> </dependency> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-server</artifactId> <version>${hbase.version}</version> </dependency>
统计
btrc:persons
表中所有人的平均年龄?
package demo3; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.*; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.hadoop.hbase.mapreduce.*; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.junit.Test; import util.HBaseUtil; import java.io.IOException; public class AgeUserJob { public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { //job组装和启动代码 //初始化hdfs入口换成为hbase入口 Configuration conf = HBaseConfiguration.create(); conf.addResource("/hbase-site.xml"); //创建job Job job = Job.getInstance(conf); job.setJarByClass(AgeUserJob.class); //配置map端 //TableInputformat job.setInputFormatClass(TableInputFormat.class); TableMapReduceUtil.initTableMapperJob("btrc:persons",new Scan(),UserMapper.class,Text.class,IntWritable.class,job); //配置reduce端 //TableOutputFormat job.setOutputFormatClass(TableOutputFormat.class); //hbase的表必须创建 TableMapReduceUtil.initTableReducerJob("btrc:pavgage",UserReducer.class,job); //启动job boolean b = job.waitForCompletion(true); System.out.println(b); } //map的类 public static class UserMapper extends TableMapper<Text, IntWritable>{ @Override protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException { //key -rowkey //行数据 resulit byte[] value1 = value.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")); //得到age属性数据 int age = Bytes.toInt(value1); //将age作为value输出 //将"age"作为key 输出, context.write(new Text("age"),new IntWritable(age)); } } //reducer的类 public static class UserReducer extends TableReducer<Text,IntWritable, NullWritable>{ @Override protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { //key:"age" //values:[15,18,57] int sum = 0; int count = 0; for (IntWritable value : values) { sum+=value.get(); count++; } //sum总年龄 count 条数 int ave = sum/count; //输出到HBase表 //封装成put String rowkey = "23点07分_ave"; Put put = new Put(Bytes.toBytes(rowkey)); put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("avgage"),Bytes.toBytes(ave)); /** * context.write(xxx,put); put是一条数据,会被添加在HBase中 */ context.write(NullWritable.get(),put); } } }
/** * 泛型1:Map输出的key * 泛型2:Map输出的value */ public class PersonMapper extends TableMapper<Text, LongWritable> { /** * * @param key 输入的数据的rowkey * @param value rowkey对应的一条结果 * @param context map的输出 */ @Override protected void map(ImmutableBytesWritable key, Result value, Context context) throws IOException, InterruptedException { //1. 获得value中的age byte[] agebyte = value.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")); if (agebyte != null) { //2. 将age的数字输出 // 注意 输出的key,必须一样,这样shuffle才能分组成一组数据,方便reduce进行汇总。 context.write(new Text("age"), new LongWritable(Bytes.toInt(agebyte))); } } }
/** * 输出的结果要进入HBase,所以输出的value必须是Put */ public class PersonReducer extends TableReducer<Text, LongWritable, NullWritable> { /** * * @param key map输出的key输入到reduce * @param values map输出的value输入到reduce * @param context 写入到hbase表中的context */ @Override protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException { // 1. 获得map输出的value,进行遍历,计算平均值。 int count = 0; double sum = 0.0; for (LongWritable p:values){ count++; sum+=p.get(); } double avgAge = sum/count; //2. 创建 Put put = new Put(Bytes.toBytes("ageAge")); //3. 将统计数据封装到Put中。 put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("avg_age"), Bytes.toBytes(avgAge)); //4. 以null为key,put为value输出到HBase中。 context.write(NullWritable.get(),put); } }
//1. 初始化配置文件,方便job操作HBase中的数据。(输入输出) Configuration conf = HBaseConfiguration.create(); conf.addResource("/hbase-site.xml"); //2. 创建job任务 Job job = Job.getInstance(conf); //3. 设置输入输出工具,HBase提供了从操作数据的InputFormat和OutputFormat job.setInputFormatClass(TableInputFormat.class); job.setOutputFormatClass(TableOutputFormat.class); //4. 绑定Map相关信息:读入表,Scan,使用的Mapper,输出key类型,输出Value类型,job任务。 TableMapReduceUtil.initTableMapperJob("baizhins:person", new Scan(), PersonMapper.class, Text.class, LongWritable.class, job); //5. 绑定reduce信息:输出信息表,reduce类型,job任务。 TableMapReduceUtil.initTableReducerJob("baizhins:person_avgAge", PersonReducer.class, job); //6. 启动任务 job.waitForCompletion(true);
集群规划 192.168.153.30: HMaster 192.168.153.31: HRegionServer 192.168.153.32: HRegionServer 192.168.153.33: HRegionServer
# 0 确保HDFS HA已经搭建完毕 [root@hadoop130 ~]# jps 1259 JournalNode 1965 NameNode 1758 DFSZKFailoverController 2110 Jps 1215 QuorumPeerMain
# 1. 安装HBase
1. 解压HBase [root@hadoop30 modules]# tar zxvf hbase-1.5.0-bin.tar.gz -C /opt/install/ 2. 配置环境变量 #JAVA export JAVA_HOME=/opt/installs/jdk1.8 export PATH=$PATH:$JAVA_HOME/bin # HADOOP export HADOOP_HOME=/opt/installs/hadoop2.9.2/ export PATH=$PATH:$HADOOP_HOME/bin:$HADOOP_HOME/sbin # zookeeper export PATH=$PATH:/opt/installs/zookeeper3.4.14/bin/ # HBase export HBASE_HOME=/opt/install/hbase1.5 export PATH=$PATH:$HBASE_HOME/bin 3. 加载profile配置 source /etc/profile
# 2. 初始化HBase 配置文件
# 1 -------------------hbase-env.sh-------------------- # 配置Java_home export JAVA_HOME=/opt/install/jdk1.8 # 注释掉如下2行。 # export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m -XX:ReservedCodeCacheSize=256m" # export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m -XX:ReservedCodeCacheSize=256m" # 禁用内置zookeeper export HBASE_MANAGES_ZK=false
# 2. -------------------hbase-site.xml------------------------- <configuration> <!-- hbase的入口,ns HaHadoop的虚拟命名空间 --> <property> <name>hbase.rootdir</name> <value>hdfs://ns/hbase</value> </property> <!-- 使用分布式模式 --> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> <!-- zookeeper集群地址,端口默认2181不需要指定 --> <property> <name>hbase.zookeeper.quorum</name> <value>hadoop30,hadoop31,hadoop32</value> </property> <!--zookeeper的默认工作目录,就是data目录,要和zookeeper的一样--> <property> <name>hbase.zookeeper.property.dataDir</name> <value>/opt/install/zookeeper3.4.14/data</value> </property> <!--配置hdfs的hflush:否则该版本启动会报错--> <property> <name>hbase.unsafe.stream.capability.enforce</name> <value>false</value> </property> </configuration>
# 3. -------------------regionservers-------------------- hadoop31 hadoop32 hadoop33
# 3. 远程拷贝 1. hbase加入hadoop配置文件 [root@hadoop30 install]# ln -s /opt/install/hadoop2.9.2/etc/hadoop/core-site.xml /opt/install/hbase1.5/conf/core-site.xml [root@hadoop30 install]# ln -s /opt/install/hadoop2.9.2/etc/hadoop/hdfs-site.xml /opt/install/hbase1.5/conf/hdfs-site.xml 2. 拷贝profile文件 [root@hadoop30 install]# scp /etc/profile root@hadoop31:/etc/ [root@hadoop30 install]# scp /etc/profile root@hadoop32:/etc/ [root@hadoop30 install]# scp /etc/profile root@hadoop33:/etc/ 3. 拷贝hbase安装软件和配置文件 [root@hadoop30 install]# scp -r hbase1.5/ root@hadoop31:/opt/install/ [root@hadoop30 install]# scp -r hbase1.5/ root@hadoop32:/opt/install/ [root@hadoop30 install]# scp -r hbase1.5/ root@hadoop33:/opt/install/ 4. 重新加载profile [root@hadoop31 ~]# source /etc/profile [root@hadoop32 ~]# source /etc/profile [root@hadoop33 ~]# source /etc/profile
# 4. 启动HBase # 启动HMaster root@hadoop30 install]# hbase-daemon.sh start master # 启动HRegionServer [root@hadoop31 ~]# hbase-daemon.sh start regionserver [root@hadoop32 ~]# hbase-daemon.sh start regionserver [root@hadoop33 ~]# hbase-daemon.sh start regionserver # 启动后需要等待20s左右,HMaster需要初始化工作。