梦入琼楼寒有月,行过石树冻无烟

HBase 基础语法

CURD

Create

Create and Table of Coumn familu or Column


Hbase 的表都是由列和族(Column Family)所组成的,列是列族的子项,而每个列或多个列会形成一行(row)。在 HBase 中可以使用 create 创建一个表。

在创建的途中可能会遇到 Hadoop 集群的报错: “util.NativeCodeLoader: Unable to load native-hadoop library for your platform… using builtin-java classes where applicable” 来导致最终 create 语法无法成功插入的报错,此问题的解决就是清除 Hadoop 除去 myid 和 version 相关的集群数据目录,然后重启并格式化即可,在重新启动下 Hbase 集群即可。

1
2
3
4
create 'test', 'cf'
Created table test
Took 11.8491 seconds
=> Hbase::Table - test

通过 create 所创建的,一个是表(table),另一个则是列族(column family),而列(Column)定义是在插入第一条数据的时候才会创建,也就是单元格(call)。所创建的所有数据都定义在列族上,而表的本身则是存放列族和列。

Create Table of Column family

通过 describe 'table' 可以看之前所创建的 cf 列族,而实际上 describe 大部分都是列族的属性,这体现在 alter 'text', 'cf2' 中:

1
2
3
4
5
6
7
8
9
10
11
describe 'test'
Table test is ENABLED
test
COLUMN FAMILIES DESCRIPTION
{NAME => 'cf', BLOOMFILTER => 'ROW', IN_MEMORY => 'false', VERSIONS => '1', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', COMPRESSION => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}

{NAME => 'cf2', BLOOMFILTER => 'ROW', IN_MEMORY => 'false', VERSIONS => '1', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', COMPRESSION => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}

2 row(s)
Quota is disabled
Took 0.0608 seconds

可以看出 describe 所列出的表属性大多数都是根据列族而进行输出的,涉及到表的属性很少。

Put

Hbase 的插入数据也体现在出了高离散的特点,其格式也是 表, 行, 列族:列名, 数据值,也就是必须指定数据值要插入到那一行,那一列当中,对应的命令是: put 'test', 'row1', 'cf:name', 'hello,world!',即使用 put 向 test 表中的第一行,列族为 cf 插入了列明为 name 的 hello,world 数据值。之后通过 scan 'test' 可扫描出:

1
2
3
4
5
> scan 'test'
ROW COLUMN+CELL
row1 column=cf:name, timestamp=2021-12-07T20:14:24.144, value=hello,world!
1 row(s)
Took 0.2802 seconds

通过 scan 可以扫描出该表的所有数据,可以看出对照关系是:

ROW COLUMN+CELL
rowkey column=列族:列名, timestamp=时间戳, value=数据值

值得注意的是单元格可以通过 timestamp 来多个版本的值,通俗来点的说他也起到了版本号(version)的作用,而 HBase 会取版本号最大的数据版本进行输出,但同时在默认情况下 timestamp 是由系统自动生成的,当然我们也可以自行定义时间戳。

在自定义时间戳之前我们需要通过 alter 'test',{NAME=>'cf',VERSIONS=>5} 来修改版本数。

这样一来就算在一个单元格无论修改多少次,HBase 也会保存最后一个版本,如:

1
2
put 'test','row2,'cf:name','date_time',1638909613
put 'test', 'row2', 'cf:name','date_update'

那么通过 scan 的方式肯定会现实时间戳最高的一位进行输出,也就是 date_update,这是因为 date_time 的时间戳转换后是 1970-01-20T08:00:45.613 而,date_update 的时间戳是 2021-12-07T20:40:52.334,所以没有输出 date_time

1
2
3
4
5
6
scan 'test'
ROW COLUMN+CELL
row1 column=cf:name, timestamp=2021-12-07T20:14:24.144, value=hello,world!
row2 column=cf:name, timestamp=2021-12-07T20:40:52.334, value=date_update
2 row(s)
Took 0.0128 seconds

不过我们如果真的需要查询 date_time 的话,可以通过 get 的表达式来获取单元格的数据,也就是 get 'test','row2',{COLUMN=>'cf:name', VERSIONS=>3}

1
2
3
4
5
get 'test','row2',{COLUMN=>'cf:name', VERSIONS=>3}
COLUMN CELL
cf:name timestamp=2021-12-07T20:40:52.334, value=date_update
cf:name timestamp=1970-01-20T08:00:45.613, value=data_timestamp
1 row(s)

对于上述这种表达式,同样的 scan 以及大部分的 hbase 语法都支持,比如我们可以通过 scan 起始行(STARTROW)和结束行(ENDROW)的表达式来进行筛选:

1
2
3
4
5
6
scan 'test',{STARTROW=>'row1',ENDROW=>'row2'}
ROW COLUMN+CELL
row1 column=cf:name, timestamp=2021-12-07T20:14:24.144, value=hello,world!
row2 column=cf:name, timestamp=2021-12-07T20:40:52.334, value=date_update
2 row(s)
Took 0.0793 seconds

同样的基于这种表达式,也赋予了和 Put 一教高下的能力,比如同样的可以使用表达式来查询出多个单元块的版本数据:

1
2
3
4
5
6
7
scan 'test',{VERSIONS=>5}
ROW COLUMN+CELL
row1 column=cf:name, timestamp=2021-12-07T20:14:24.144, value=hello,world!
row2 column=cf:name, timestamp=2021-12-07T20:40:52.334, value=date_update
row2 column=cf:name, timestamp=1970-01-20T08:00:45.613, value=data_timestamp
2 row(s)
Took 0.0773 seconds

Read

list

通过 listlist <table_name> 的方式来查询当前库中有多少个表以及查询单个表,列出刚刚所创建的表是否存在

1
2
3
4
5
6
list 'test'
TABLE
test
1 row(s)
Took 0.3364 seconds
=> ["test"]

describe

查看表和列族的详细信息,实际上有很大一部分的信息输出都是针对列族的,主要的原因是 HBase 中的表上只有几个属性,大部分的属性都在列族上而已。

1
2
3
4
5
6
7
8
9
10
describe 'test'
Table test is ENABLED
test
COLUMN FAMILIES DESCRIPTION
{NAME => 'cf', BLOOMFILTER => 'ROW', IN_MEMORY => 'false', VERSIONS => '1', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', COMPRESSION => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICAT
ION_SCOPE => '0'}

1 row(s)
Quota is disabled
Took 2.1830 seconds
Name Name_cn Info
NAME Name family 名 Name_family 的名
BLOOMFILTER 布隆过滤 在 1970 年由 Burton Howard Bloom 所提出,用于测试一个元素是否在特定集中。
ROW 提高随机读取性能
IN_MEMORY 是否在内存中 HBase 所提供的缓存选择,默认为 “false”
VERSIONS 版本数 记录单元格所插入的数据,只保留最后一个版本并记录所有版本的数据
KEEP_DELETED_CELLS 删除标记 是否保留已经删除的单元格,默认为 “false”
DATA_BLOCK_ENCODING 数据块编码 数据块编码,可自行进行选择,默认 “none”
COMPRESSION 压缩算法 决定是否启用压缩算法,默认 “none”
TTL 生存时间 制定后可以达到过期事件后自动删除行,默认为 “FOREVER”
MIN_VERSIONS 最小版本 存储列中的最小版本号,默认为 “0”,也意味着该功能处于禁用状态
BLOCKCACHE 块缓存 Hbase 提供了 LruBlockCache 和 BucketCache 两种缓存机制,通常是 off-heap
BLOCKSIZE 块大小 HDFS 块,单元格越带,块大小就越大,默认值为 64k
REPLICATION_SCOPE 复制范围 允许使用源集群预写日志来传播更改,使一个集群的状态与零一个集群的状态保持同步

scan

通过 scan 可以扫描出该表的所有数据,可以看出对照关系是:

ROW COLUMN+CELL
rowkey column=列族:列名, timestamp=时间戳, value=数据值

值得注意的是单元格可以通过 timestamp 来多个版本的值,通俗来点的说他也起到了版本号(version)的作用,而 HBase 会取版本号最大的数据版本进行输出,但同时在默认情况下 timestamp 是由系统自动生成的,当然我们也可以自行定义时间戳。

在自定义时间戳之前我们需要通过 alter 'test',{NAME=>'cf',VERSIONS=>5} 来修改版本数。

这样一来就算在一个单元格无论修改多少次,HBase 也会保存最后一个版本,如:

1
2
put 'test','row2,'cf:name','date_time',1638909613
put 'test', 'row2', 'cf:name','date_update'

那么通过 scan 的方式肯定会现实时间戳最高的一位进行输出,也就是 date_update,这是因为 date_time 的时间戳转换后是 1970-01-20T08:00:45.613 而,date_update 的时间戳是 2021-12-07T20:40:52.334,所以没有输出 date_time

1
2
3
4
5
6
scan 'test'
ROW COLUMN+CELL
row1 column=cf:name, timestamp=2021-12-07T20:14:24.144, value=hello,world!
row2 column=cf:name, timestamp=2021-12-07T20:40:52.334, value=date_update
2 row(s)
Took 0.0128 seconds

基于表达式的应用,也赋予了和 Put 一教高下的能力,比如同样的可以使用表达式来查询出多个单元块的版本数据:

1
2
3
4
5
6
7
scan 'test',{VERSIONS=>5}
ROW COLUMN+CELL
row1 column=cf:name, timestamp=2021-12-07T20:14:24.144, value=hello,world!
row2 column=cf:name, timestamp=2021-12-07T20:40:52.334, value=date_update
row2 column=cf:name, timestamp=1970-01-20T08:00:45.613, value=data_timestamp
2 row(s)
Took 0.0773 seconds

Delete

Delete of data and tombstone marker

Delete 其如其名就是删除,同样的根据高离散的格式来定义表达式,如 delete 'test','row1','cf:name'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> scan 'test',{VERSIONS=>5}
ROW COLUMN+CELL
row1 column=cf:name, timestamp=2021-12-07T20:14:24.144, value=hello,world!
row2 column=cf:name, timestamp=2021-12-07T20:40:52.334, value=date_update
row2 column=cf:name, timestamp=1970-01-20T08:00:45.613, value=data_timestamp
2 row(s)
Took 0.0181 seconds

> delete 'test','row1','cf:name'
Took 0.0246 seconds

> scan 'test',{VERSIONS=>5}
ROW COLUMN+CELL
row2 column=cf:name, timestamp=2021-12-07T20:40:52.334, value=date_update
row2 column=cf:name, timestamp=1970-01-20T08:00:45.613, value=data_timestamp
1 row(s)
Took 0.0080 seconds

而这个时候就会牵扯到版本记录,如我们将 row2 的时间戳 1641645000 删除,则可以构造表达式 delete 'test','row2','cf:name',1670445613

很多时候 Hbase 的时间戳转换的格式为 yyyy-MM-dd'T'HH:mm:ss.SSS,我们可以直接在 hbase shell 中来进行转换:

  1. import java.text.SimpleDateFormat
  2. import java.text.ParsePosition
  3. SimpleDateFormat.new(“yyyy-MM-dd’T’HH:mm:ss.SSS”).parse(“1970-01-20T08:00:45.613”,ParsePosition.new(0
    )).getTime()
1
2
3
4
5
 scan 'test',{VERSIONS=>5}
ROW COLUMN+CELL
row2 column=cf:name, timestamp=2021-12-07T20:40:52.334, value=date_update
1 row(s)
Took 0.0449 seconds

上述提到的墓碑标记(tombstone marker) 即被删除的数据,他并不是被真正的删除而是被打上了一个墓碑标记,打上后会连同之前的版本也会被标记为不可见。

同时为了性能着想他并不会马上清除,而是定期的去清理这些已经被删除的记录,而所谓的定期则是在 HBase 做自动合并的时候将墓碑合并到一起进行删除,以此来将 HBase 的消耗降到最低,我们可以通过 scan 'test',{RAW=>true,VERSIONS=>5} 来进行查看被打赏墓碑标记的数据 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> scan 'test',{RAW=>true,VERSIONS=>5}
ROW COLUMN+CELL
row1 column=cf:name, timestamp=2021-12-07T20:14:24.144, type=Delete
row1 column=cf:name, timestamp=2021-12-07T20:14:24.144, value=hello,world!
row2 column=cf:name, timestamp=2021-12-07T20:40:52.334, value=date_update
row2 column=cf:name, timestamp=1970-01-20T08:00:45.613, type=Delete
row2 column=cf:name, timestamp=1970-01-20T08:00:45.613, value=data_timestamp
row2 column=cf:name, timestamp=1970-01-20T00:00:45, type=Delete
row2 column=cf:name, timestamp=1970-01-19T23:15:09.613, type=Delete
row2 column=cf:name, timestamp=1970-01-19T23:14:40.852, type=Delete
row2 column=cf:name, timestamp=1970-01-01T00:27:21.645, type=Delete
row2 column=cf:name, timestamp=1970-01-01T00:27:18.911, type=Delete
2 row(s)
Took 0.0579 seconds

deleteall

deteall 与单纯的 detell 的区别是他是删除整行目录的,也就是说他无需指定列族,指定到 rowkey 即可进行删除该 rowkey 下的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
> scan 'test'
ROW COLUMN+CELL
row2 column=cf:name, timestamp=2021-12-07T20:40:52.334, value=date_update
1 row(s)
Took 0.1310 seconds

> deleteall 'test','row2'
Took 0.0148 seconds

hbase(main):062:0> scan 'test'
ROW COLUMN+CELL
0 row(s)
Took 0.0095 seconds

disable

disable 的命令作用是停用表,也就是更加真实应对分布式数据库高并发高和高性能下所提供的一个命令,在删除 HBase 表的时候强烈建议西安 disable 在 drop 删除表。

如果 disable 目标表下线的时候很快则表明这个表的依赖度不高,但如果假设依赖度很高的话则会很慢,因为他会通知所有 RegisionServer 来下线该表,以保证完全不参与任何工作了在进行下线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
hbase(main):063:0> disable 'test'
Took 15.3142 seconds
hbase(main):064:0> scan 'test'
ROW COLUMN+CELL
org.apache.hadoop.hbase.TableNotEnabledException: test is disabled.
at org.apache.hadoop.hbase.client.ConnectionImplementation.relocateRegion(ConnectionImplementation.java:770)
at org.apache.hadoop.hbase.client.RpcRetryingCallerWithReadReplicas.getRegionLocations(RpcRetryingCallerWithReadReplicas.java:330)
at org.apache.hadoop.hbase.client.ScannerCallable.prepare(ScannerCallable.java:139)
at org.apache.hadoop.hbase.client.ScannerCallableWithReplicas$RetryingRPC.prepare(ScannerCallableWithReplicas.java:408)
at org.apache.hadoop.hbase.client.RpcRetryingCallerImpl.callWithRetries(RpcRetryingCallerImpl.java:105)
at org.apache.hadoop.hbase.client.ResultBoundedCompletionService$QueueingFuture.run(ResultBoundedCompletionService.java:80)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

ERROR: Table test is disabled!

For usage try 'help "scan"'

当该表下线后则无法使用 scan 命令进行查询,并会输出错误信息,来表示当前表已经被关闭,无法 scan。

drop

当使用 disable 停用表后,则开始基于 drop 进行删除表,在此之前我们使用 list 还是可以发现 test 表存在的,但如果使用 drop 'test' 后,则真的无法使用 list 查看到 test 表的任何痕迹了。

⬅️ Go back