在 “Springboot 系列 (24) - Springboot+HBase 大数据存储(二)| 安装配置 Apache HBase 和 Apache Zookeeper” 里我们安装配置了 Apache HBase 和 Apache Zookeeper,本文将介绍 HBase Shell,HBase REST 服务 和 Phoenix 组件。
HBase Shell
HBase Shell 是 HBase 的一套命令行工具,可以使用 shell 命令在 HBase 所在的本机上查询/操作 HBase 的数据。
HBase Shell 相关文档:https://hbase.apache.org/book.html#shell
HBase REST 服务
REST ( Representational State Transfer ) 于 2000 年在 HTTP 规范的主要作者之一 Roy Fielding 的博士论文中引入。
REST 允许通过与 URL 本身绑定的 API 进行客户端 ~ 服务器交互。配置和运行 HBase 附带的 REST 服务器,该服务器将 HBase 表、行、单元格和元数据公开为 URL 指定的资源。
HBase 附带的 REST 服务器可以作为守护进程运行,该守护进程启动嵌入式 Jetty servlet 容器并将 servlet 部署到其中。
HBase REST 相关文档:https://hbase.apache.org/book.html#_rest
Phoenix 组件
Phoenix 是 HBase 的开源 SQL 皮肤。通过 Phoenix 可以使用标准 JDBC API 代替 HBase 客户端 API 来创建表,插入数据和查询 HBase 数据。
Phoenix 会把 SQL 编译成一系列的 Hbase 的 scan 操作,然后把 scan 结果生成标准的 JDBC 结果集,其底层由于使用了 Hbase 的API,协处理器,过滤器。Phoenix 支持的操作:SELECT, FROM、WHERE、GROUP BY、HAVING、ORDER BY 等。
Phoenix 其实就是一个客户端,它处于应用程序和 HBase 之间。Phoenix 客户端分为两种:
(1) 瘦客户端:将用户写好的 SQL 转交给 Phoenix Query 服务,由 Phoenix Query 服务把 SQL 解析成对应的HBase 操作,并交给 HBase 执行,执行完成之后并负责把结果收集回来,并转换成二维表格返回给用户。Phoenix Query 服务只针对瘦客户端。Phoenix Query 服务需要放到 HBase 的 RegionServer 里面。
(2) 胖客户端:不需要依赖 Phoenix Query 服务,客户端本身就可以完成 SQL 的解析和提交操作(连接 Zookeeper)。
建议用胖客户端,少一个需要维护的服务。
Phoenix 与 HBase 连接后会自动创建这些系统表:SYSTEM.CATALOG、SYSTEM.CHILD_LINK、SYSTEM.FUNCTION、SYSTEM.LOG、SYSTEM.MUTEX、SYSTEM.SEQUENCE、SYSTEM.STATS、SYSTEM.TASK,其中 SYSTEM.CATALOG 表用于存放 Phoenix 创建表时的元数据。
Phoenix 创建表时会自动调用 HBase 客户端创建相应的表,并且在 SYSTEM.CATALOG 系统表中记录 Phoenix 创建表时的元数据,其主键的值对应 HBase 的 RowKey,非主键的列对应 HBase 的Column(列族不指定时为0,且列会进行编码)。
通过 Phoenix 创建的表,必须通过 Phoenix 客户端来对表进行操作,因为通过 Phoenix 创建的表其非主键的列会进行编码。
Phoenix 的 SQL 中如果表名、字段名不使用双引号标注那么默认转换成大写,Phoenix 中的字符串使用单引号进行标注。
在 Phoenix 中,默认情况下,库名,表名,字段名等会自动转换为大写,若要小写,使用双引号,如 "ns1",要特别注意引号的使用方式。
Phoenix:https://phoenix.apache.org/
1. 系统环境
操作系统:Ubuntu 20.04
Java 版本:openjdk 11.0.18
Hadoop 版本:3.2.2
Zookeeper 版本:3.6.3
HBase 版本:2.4.4
HBase 所在路径:~/apps/hbase-2.4.4
本文 HBase 在 HBase + Zookeeper (独立的) 模式下运行,Zookeeper 使用端口 2182。
2. HBase Shell
1) 运行 Shell
$ cd ~/apps
$ ./hbase-2.4.4/bin/hbase shell
hbase:001:0> help HBase Shell, version 2.4.4, r20e7ba45b0c3affdc0c06b1a0e5cbddd1b2d8d18, Mon Jun 7 15:31:55 PDT 2021 Type 'help "COMMAND"', (e.g. 'help "get"' -- the quotes are necessary) for help on a specific command. Commands are grouped. Type 'help "COMMAND_GROUP"', (e.g. 'help "general"') for help on a command group. ... hbase:002:0> version 2.4.4, r20e7ba45b0c3affdc0c06b1a0e5cbddd1b2d8d18, Mon Jun 7 15:31:55 PDT 2021 Took 0.0002 seconds hbase:003:0> status 1 active master, 0 backup masters, 1 servers, 0 dead, 1.0000 average load Took 0.4128 seconds
2) Table 操作
# 查看所有表 hbase:004:0> list TABLE 0 row(s) Took 0.3782 seconds => [] # 创建 test 表,一个列族 base_info hbase:005:0> create 'test', 'base_info' Created table test Took 1.2217 seconds => Hbase::Table - test # 查看表描述信息 hbase:006:0> describe 'test' Table test is ENABLED test COLUMN FAMILIES DESCRIPTION {NAME => 'base_info', BLOOMFILTER => 'ROW', IN_MEMORY => 'false', VERSIONS => '1', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODI NG => 'NONE', COMPRESSION => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION _SCOPE => '0'} 1 row(s) Quota is disabled Took 0.1358 seconds # 添加 2 个列族 hbase:007:0> alter 'test', 'adv_info', 'del_info' Updating all regions with the new schema... 1/1 regions updated. Done. Took 2.3449 seconds # 删除 1 个列族 hbase:008:0> alter 'test', {NAME => 'del_info', METHOD => 'delete'} Updating all regions with the new schema... 1/1 regions updated. Done. Took 2.1434 seconds # 检查表是否存在 hbase:009:0> exists 'demo' Table demo does not exist Took 0.0220 seconds => false # 使用 disable 禁用一个表 hbase:010:0> disable 'test' Took 0.3985 seconds # 使用 is_enabled 查看一个表是否被禁用,也可以用 is_disabled 来查看 hbase:011:0> is_enabled 'test' false Took 0.0143 seconds => false # 使用 enable 启用一个表 hbase:010:0> enable 'test' # 删除一个表,删除前要先使用 disable 禁用这个表 hbase:013:0> disable 'demo' hbase:014:0> drop 'demo'
3) 添加/删除数据
可以通过 put 命令来插入数据,上文如我们新建 test 表,它拥有二个列簇 base_info 和 adv_info。列簇下的列不需要提前创建,在需要时通过:来指定即可。
# 添加数据 hbase:015:0> put 'test', 'rowkey_1', 'base_info:name', 'Tom' hbase:016:0> put 'test', 'rowkey_1', 'base_info:age', '12' hbase:017:0> put 'test', 'rowkey_1', 'base_info:job', 'Student' hbase:018:0> put 'test', 'rowkey_2', 'base_info:name', 'Jerry' hbase:019:0> put 'test', 'rowkey_2', 'base_info:salary', '3000' hbase:020:0> put 'test', 'rowkey_2', 'base_info:job', 'Engineer' hbase:021:0> put 'test', 'rowkey_3', 'base_info:name', 'del_user' hbase:022:0> put 'test', 'rowkey_3', 'base_info:age', '20' hbase:023:0> put 'test', 'rowkey_3', 'base_info:salary', '5000' hbase:024:0> put 'test', 'rowkey_3', 'base_info:job', 'Art' hbase:025:0> get 'test', 'rowkey_3' COLUMN CELL base_info:age timestamp=2023-03-22T18:41:51.992, value=20 base_info:job timestamp=2023-03-22T18:42:06.667, value=Art base_info:name timestamp=2023-03-22T18:41:43.650, value=del_user base_info:salary timestamp=2023-03-22T18:41:58.937, value=5000 1 row(s)`` Took 0.0077 seconds # 删除列/数据 hbase:027:0> delete 'test','rowkey_3','base_info:job' hbase:028:0> get 'test', 'rowkey_3' COLUMN CELL base_info:age timestamp=2023-03-22T18:41:51.992, value=20 base_info:name timestamp=2023-03-22T18:41:43.650, value=del_user base_info:salary timestamp=2023-03-22T18:41:58.937, value=5000 1 row(s) Took 0.0099 seconds # 删除整行数据 hbase:029:0> deleteall 'test', 'rowkey_3' hbase:030:0> get 'test', 'rowkey_3' COLUMN CELL 0 row(s) Took 0.0046 seconds # 退出 Shell hbase:031:0> exit # 或 quit
4) 查询数据
# 查询表中有多少行 hbase:001:0> count 'test' 2 row(s) Took 0.3769 seconds => 2 # 获取一个列族的数据 hbase:002:0> get 'test', 'rowkey_1', 'base_info' COLUMN CELL base_info:age timestamp=2023-03-22T18:31:00.415, value=12 base_info:job timestamp=2023-03-22T18:31:08.839, value=Student base_info:name timestamp=2023-03-22T18:30:43.745, value=Tom 1 row(s) Took 0.0126 seconds # 查询整表数据 hbase:003:0> scan 'test' ROW COLUMN+CELL rowkey_1 column=base_info:age, timestamp=2023-03-22T18:31:00.415, value=12 rowkey_1 column=base_info:job, timestamp=2023-03-22T18:31:08.839, value=Student rowkey_1 column=base_info:name, timestamp=2023-03-22T18:30:43.745, value=Tom rowkey_2 column=base_info:job, timestamp=2023-03-22T18:37:11.844, value=Engineer rowkey_2 column=base_info:name, timestamp=2023-03-22T18:36:31.967, value=Jerry rowkey_2 column=base_info:salary, timestamp=2023-03-22T18:36:39.804, value=3000 2 row(s) Took 0.0142 seconds # 扫描一个列簇 hbase:004:0> scan 'test', {COLUMN=>'base_info'} ROW COLUMN+CELL rowkey_1 column=base_info:age, timestamp=2023-03-22T18:31:00.415, value=12 rowkey_1 column=base_info:job, timestamp=2023-03-22T18:31:08.839, value=Student rowkey_1 column=base_info:name, timestamp=2023-03-22T18:30:43.745, value=Tom rowkey_2 column=base_info:job, timestamp=2023-03-22T18:37:11.844, value=Engineer rowkey_2 column=base_info:name, timestamp=2023-03-22T18:36:31.967, value=Jerry rowkey_2 column=base_info:salary, timestamp=2023-03-22T18:36:39.804, value=3000 2 row(s) Took 0.0277 seconds # 扫描一个列 hbase:005:0> scan 'test', {COLUMNS=> 'base_info:name'} ROW COLUMN+CELL rowkey_1 column=base_info:name, timestamp=2023-03-22T18:30:43.745, value=Tom rowkey_2 column=base_info:name, timestamp=2023-03-22T18:36:31.967, value=Jerry 2 row(s) Took 0.0123 seconds # 使用 FILTER 找到某个列的值等于 12 hbase:005:0> scan 'test', FILTER=>"ValueFilter(=,'binary:12')" ROW COLUMN+CELL rowkey_1 column=base_info:age, timestamp=2023-03-22T18:31:00.415, value=12 1 row(s) Took 0.0152 seconds # 使用 FILTER 找到包含 'Tom' hbase:006:0> scan 'test', FILTER=>"ValueFilter(=,'substring:Tom')" Took 0.0070 seconds hbase:013:0> scan 'test', FILTER=>"ValueFilter(=,'substring:Tom')" ROW COLUMN+CELL rowkey_1 column=base_info:name, timestamp=2023-03-22T18:30:43.745, value=Tom 1 row(s) Took 0.0135 seconds
注:除了列(COLUMNS)、FILTER(按条件过滤行)修饰词外,HBase 还支持 Limit(限制查询结果行数),STARTROW (ROWKEY 起始行,会先根据这个key 定位到 region,再向后扫描)、STOPROW (结束行)、TIMERANGE(限定时间戳范围)、VERSIONS(版本)等。
5) Namespace 操作
命名空间,类似于关系型数据库下的 database,每个命名空间下有多个表。
# 创建 Namespace hbase:002:0> create_namespace 'springboot' # 查看 Namespace hbase:003:0> describe_namespace 'springboot' DESCRIPTION {NAME => 'springboot'} Quota is disabled Took 0.0186 seconds # 查看所有 Namespace hbase:004:0> list_namespace NAMESPACE default hbase springboot 3 row(s) Took 0.0151 seconds # 在 springboot 下创建表 tbl_01 hbase:006:0>create 'springboot:tbl_01', 'column_family1' Created table springboot:tbl_01 Took 1.2189 seconds => Hbase::Table - springboot:tbl_01 # 列出 springboot 下的所有 Table hbase:007:0> list_namespace_tables 'springboot' TABLE tbl_01 1 row(s) Took 0.0197 seconds => ["tbl_01"] # 删除 Namespace hbase:008:0> drop_namespace 'springboot' ERROR: org.apache.hadoop.hbase.constraint.ConstraintException: Only empty namespaces can be removed. Namespace springboot has 1 tables ... 注:命名空间 springboot 下有一个表 tbl_01,不能删除非空的命名空间。
3. HBase REST 服务
1) 启动 REST 服务器
$ cd ~/apps
# 前台运行,默认端口为 8080
$ ./hbase-2.4.4/bin/hbase rest start -p 8888
# 后台运行
$ ./hbase-2.4.4/bin/hbase-daemon.sh start rest -p 8888
# 显示所有非系统表
$ curl -X GET -H "Accept: text/plain" "http://localhost:8888"
test
springboot:tbl_01
# 显示 HBase 版本
$ curl -X GET -H "Accept: text/plain" "http://localhost:8888/version/cluster"
2.4.4
# HBase 群集状态
$ curl -X GET -H "Accept: text/plain" "http://localhost:8888/status/cluster"
1 live servers, 0 dead servers, 4.0000 average load
1 live servers
Test-Ubuntu20:16020 1679556060710
requests=16, regions=4
heapSizeMB=41
maxHeapSizeMB=494
...
2) Table 操作
# 创建新表
$ curl -X PUT \
-H "Accept: text/plain" \
-H "Content-Type: text/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?><TableSchema name="demo"><ColumnSchema name="column_family1" /></TableSchema>' \
"http://localhost:8888/demo/schema"
# 显示表的结构信息
$ curl -X GET -H "Accept: text/plain" "http://localhost:8888/demo/schema"
{ NAME=> 'demo', IS_META => 'false', COLUMNS => [ { NAME => 'column_family1', BLOOMFILTER => 'ROW', IN_MEMORY => 'false', VERSIONS => '1', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', COMPRESSION => 'NONE', TTL => '2147483647', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0' } ] }
# 更新表结构
$ curl -X POST \
-H "Accept: text/plain" \
-H "Content-Type: text/xml" \
-d '<?xml version="1.0" encoding="UTF-8"?><TableSchema name="demo"><ColumnSchema name="column_family1" KEEP_DELETED_CELLS="true" /></TableSchema>' \
"http://localhost:8888/demo/schema"
# 显示表分区
$ curl -X GET -H "Accept: text/plain" "http://localhost:8888/demo/regions"
demo,,1679558889289.b7e89a9e95ad237aeb9439cff11d83c2. [
id=1679558889289
startKey=''
endKey=''
location='Test-Ubuntu20:16020'
]
# 删除表
$ curl -X DELETE -H "Accept: text/plain" "http://localhost:8888/demo/schema"
3) 查询数据
# 查询 test 表 rowkey_1 下的数据
$ curl -X GET -H "Accept: text/xml" "http://localhost:8888/test/rowkey_1"
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><CellSet><Row key="cm93a2V5XzE="><Cell column="YmFzZV9pbmZvOmFnZQ==" timestamp="1679481060415">MTI=</Cell><Cell column="YmFzZV9pbmZvOmpvYg==" timestamp="1679481068839">U3R1ZGVudA==</Cell><Cell column="YmFzZV9pbmZvOm5hbWU=" timestamp="1679481043745">VG9t</Cell></Row></CellSet>
注:key、column 和值是被 Base64 编码的,可以运行如下命令解码。
$ echo "cm93a2V5XzE=" | base64 -d
rowkey_1
# 查询 test 表 rowkey_1 下 base_info 列族的 name 列的数据
$ curl -X GET -H "Accept: text/xml" "http://localhost:8888/test/rowkey_1/base_info:name"
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><CellSet><Row key="cm93a2V5XzE="><Cell column="YmFzZV9pbmZvOm5hbWU=" timestamp="1679481043745">VG9t</Cell></Row></CellSet>
$ echo "VG9t" | base64 -d
Tom
4) Namespace
# 查看所有 Namespace
$ curl -X GET -H "Accept: text/xml" "http://localhost:8888/namespaces"
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Namespaces><Namespace>default</Namespace><Namespace>hbase</Namespace><Namespace>springboot</Namespace></Namespaces>
# 查看指定 Namespace 下的表
$ curl -X GET -H "Accept: text/xml" "http://localhost:8888/namespaces/springboot/tables"
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><TableList><table name="tbl_01"/></TableList>
5) 停止 REST 服务器
$ cd ~/apps
$ ./hbase-2.4.4/bin/hbase-daemon.sh stop rest
4. Phoenix 组件
1) 下载 Phoenix
访问 http://archive.apache.org/dist/phoenix/phoenix-5.1.2/phoenix-hbase-2.4-5.1.2-bin.tar.gz,下载 phoenix-hbase-2.4-5.1.2-bin.tar.gz 保存到 ~/apps 目录。
$ cd ~/apps
$ tar -zvxf phoenix-hbase-2.4-5.1.2-bin.tar.gz
$ mv phoenix-hbase-2.4-5.1.2-bin phoenix-5.1.2
$ cd phoenix-5.1.2
$ cp phoenix-server-hbase-2.4-5.1.2.jar ../hbase-2.4.4/lib # HBase 集群的 master 和 slave 都要复制该文件
2) 配置 Phoenix Schema 操作权限
$ cd ~/apps
# 修改 HBase 配置文件,添加如下内容
$ vim ./hbase-2.4.4/conf/hbase-site.xml
<property> <name>phoenix.schema.isNamespaceMappingEnabled</name> <value>true</value> </property> <property> <name>phoenix.schema.mapSystemTablesToNamespace</name> <value>true</value> </property>
# 修改 Phoenix 配置文件,添加如下内容
$ vim ./phoenix-5.1.2/bin/hbase-site.xml
<property> <name>phoenix.schema.isNamespaceMappingEnabled</name> <value>true</value> </property> <property> <name>phoenix.schema.mapSystemTablesToNamespace</name> <value>true</value> </property>
3) 重启 HBase
$ cd ~/apps
# 停止 HBase
$ ./hbase-2.4.4/bin/stop-hbase.sh
# 启动 HBase
$ ./hbase-2.4.4/bin/start-hbase.sh
4) Phoenix Shell (Python)
SQL Support: https://phoenix.apache.org/language/index.html
$ cd ~/apps
$ ./phoenix-5.1.2/bin/sqlline.py localhost:2182 # localhost:2182 是 Zookeeper 的地址和端口
... Connected to: Phoenix (version 5.1) Driver: PhoenixEmbeddedDriver (version 5.1) Autocommit status: true Transaction isolation: TRANSACTION_READ_COMMITTED sqlline version 1.9.0 # 创建 schema (就是 HBase 中的 Namespace,相当于 RDBMS 的数据库) 0: jdbc:phoenix:localhost:2182> CREATE schema IF NOT EXISTS "ns_test"; # 切换 schema,如果不执行 USE "ns_test",那默认操作的是 “default” schema 0: jdbc:phoenix:localhost:2182> USE "ns_test"; # 删除 schema 0: jdbc:phoenix:localhost:2182> drop schema "ns_test"; # 创建表 0: jdbc:phoenix:localhost:2182> CREATE TABLE IF NOT EXISTS user ( . . . . . . . . . . . . . . .)> id varchar PRIMARY KEY, . . . . . . . . . . . . . . .)> name varchar, . . . . . . . . . . . . . . .)> age integer); # 查看 Phoenix 创建的所有表 0: jdbc:phoenix:localhost:2182> !tables +-----------+-------------+------------+--------------+---------+-----------+--- ... | TABLE_CAT | TABLE_SCHEM | TABLE_NAME | TABLE_TYPE | REMARKS | TYPE_NAME | +-----------+-------------+------------+--------------+---------+-----------+--- | | SYSTEM | CATALOG | SYSTEM TABLE | | | | | SYSTEM | CHILD_LINK | SYSTEM TABLE | | | | | SYSTEM | FUNCTION | SYSTEM TABLE | | | | | SYSTEM | LOG | SYSTEM TABLE | | | | | SYSTEM | MUTEX | SYSTEM TABLE | | | | | SYSTEM | SEQUENCE | SYSTEM TABLE | | | | | SYSTEM | STATS | SYSTEM TABLE | | | | | SYSTEM | TASK | SYSTEM TABLE | | | | | ns_test | USER | TABLE | | | +-----------+-------------+------------+--------------+---------+-----------+--- # 插入/更新数据 0: jdbc:phoenix:localhost:2182> UPSERT INTO user VALUES ('1', 'Tom', 12); 0: jdbc:phoenix:localhost:2182> UPSERT INTO user(id,name,age) VALUES ('2', 'Jerry', 10); 注: 如果主键的值重复,那么进行更新操作,否则插入一条新的记录。在使用 UPSERT 时,主键的列不能为空(包括联合主键)。 # 查询数据 0: jdbc:phoenix:localhost:2182> SELECT * FROM user; +----+-------+-----+ | ID | NAME | AGE | +----+-------+-----+ | 1 | Tom | 12 | | 2 | Jerry | 10 | +----+-------+-----+ 2 rows selected (0.066 seconds) 注:查询支持 ORDER BY、GROUP BY、LIMIT、JOIN 等操作,同时 Phoenix 提供了 COUNT()、MAX()、MIN()、SUM() 等函数。
函数列表可以查看:http://phoenix.apache.org/language/functions.html
# 删除表 0: jdbc:phoenix:localhost:2182> DROP TABLE user; # 退出 sqlline,可以运行 !exit 或 !quit 或 !q 0: jdbc:phoenix:localhost:2182> !exit
注:Phoenix 的 Python 开发包,可以运行如下命令安装:
$ sudo apt-get install libkrb5-dev
$ sudo pip install phoenixdb