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 -07 T20: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 -07 T20:40 :52.334 , value = date_update cf:name timestamp = 1970 -01 -20 T08: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 通过 list 或 list <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 ENABLEDtest 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 中来进行转换:
import java.text.SimpleDateFormat
import java.text.ParsePosition
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 -07 T20: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 -07 T20:14 :24.144 , type= Delete row1 column = cf:name, timestamp = 2021 -12 -07 T20:14 :24.144 , value = hello,world! row2 column = cf:name, timestamp = 2021 -12 -07 T20:40 :52.334 , value = date_update row2 column = cf:name, timestamp = 1970 -01 -20 T08:00 :45.613 , type= Delete row2 column = cf:name, timestamp = 1970 -01 -20 T08:00 :45.613 , value = data_timestamp row2 column = cf:name, timestamp = 1970 -01 -20 T00:00 :45 , type= Delete row2 column = cf:name, timestamp = 1970 -01 -19 T23:15 :09.613 , type= Delete row2 column = cf:name, timestamp = 1970 -01 -19 T23:14 :40.852 , type= Delete row2 column = cf:name, timestamp = 1970 -01 -01 T00:27 :21.645 , type= Delete row2 column = cf:name, timestamp = 1970 -01 -01 T00: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 -07 T20: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 + CELL0 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 + CELLorg.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