《HBase 不睡觉》第一章 - 初识 HBase

《HBase 不睡觉书》是一本让人看了不会睡着的HBase技术书籍,写的非常不错,为了加深记忆,决定把书中重要的部分整理成读书笔记,便于后期查阅,同时希望为初学 HBase 的同学带来一些帮助。

 

目录

  • 第一章 - 初识 HBase
  • 第二章 - 让 HBase 跑起来
  • 第三章 - HBase 基本操作
  • 第四章 - 客户端 API 入门
  • 第五章 - HBase 内部探险
  • 第六章 - 客户端 API 的高阶用法
  • 第七章 - 客户端 API 的管理功能
  • 第八章 - 再快一点
  • 第九章 - 当 HBase 遇上 MapReduce

一、基本概念

1、CAP 理论

CAP 全称 是Consistency Availability and Partition tolerance:

  • Consistency(一致性):数据一致更新,所有数据变动都是同步的;
  • Availability(可用性):良好的响应性能;
  • Partition tolerance(分区容错性):可靠性。

任何分布式系统只可同时满足二点,没法三者兼顾。架构师不要将精力浪费在如何设计能满足三者的完美分布式系统,而是应该进行取舍。

 

2、NoSQL

很多人以 为 NoSQL 是非 SQL 的意思,其实它是 Not Only SQL 的缩写,意思是不只是 SQL。与关系型数据库正好相反,非关系型数据库 NoSQL 对事务性的要求并不严格,甚至可以说是相当马虎。

有些数据库是保证最终一致性,信息不会立即同步,而是经过了一段时间才达到一致性。比如你发了一篇文章,你的一部分朋友立马看到了这篇文章,另一部分朋友却要等到 5 分钟之后才能刷出这篇文章。

虽然有延时,但是对于一个娱乐性质的 Web 2.0 网站又有谁会在乎这几分钟的延时呢?如果你用传统关系型数据库,网站可能早就宕掉了。

有些数据库可以在部分机器宕机的情况下依然可以正常运行,其实原理就是把同一份数据复制成了好几份放到了好几个地方,正应了那句老话:不要把鸡蛋同时放在一个篮子里。

 

3、列式存储与行式存储

列式存储(Column-based)是相对于传统关系型数据库的行式存储(Row-based)来说的,简单来说两者的区别就是如何组织表。

将表放入存储系统中有两种方法,而我们绝大部分是采用行存储的。行存储法是将各行放入连续的物理位置,这很像传统的记录和文件系统。列存储法是将数据按照列存储到数据库中,与行存储类似,下图是两种存储方法的图形化解释。

 

应用行式存储的数据库系统称为行式数据库,同理应用列式存储的数据库系统称为列式数据库。

 

列式存储的主要优点之一就是可以大幅降低系统的 I/O,尤其是在海量数据查询时,通过下面这张图,相信我们能够彻底明白这一点。

 

二、什么时候使用 HBase?

HBase 的存储是基于 Hadoop 的,Hadoop 实现了一个分布式文件系统(HDFS)。HDFS 有高容错性的特点,被设计用来部署在低廉的硬件上,而且它提供高吞吐量以访问应用程序的数据,适合那些有着超大数据集的应用程序。基于 Hadoop 意味着 HBase 与生俱来的超强的扩展性和吞吐量

HBase 采用的是 Key/Value 的存储方式,这意味着,即使随着数据量增大,也几乎不会导致查询的性能下降。

HBase 又是一个列式数据库,当你的表字段很多的时候,你甚至可以把其中几个字段放在集群的一部分机器上,而另外几个字段放到另外一部分机器上,充分分散了负载压力。

然而,如此复杂的存储结构和分布式的存储方式带来的代价就是:哪怕只是存储少量数据,它也不会很快。所以我常常跟人说:HBase 并不快,只是当数据量很大的时候它慢的不明显

不适合使用 HBase 的场景:

  • 主要需求是数据分析,比如做报表。
  • 单表数据量不超过千万。

适合使用 HBase 的场景:

  • 单表数据量超千万,而且并发还挺高。
  • 数据分析需求较弱,或者不需要那么灵活或者实时

三、HBase 部署架构

1、部署架构

HBase 有两种服务器:Master 服务器和 RegionServer 服务器,一般一个 HBase 集群有一个 Master 服务器和多个 RegionServer 服务器。

Master 服务器负责维护表结构信息,实际的数据都存储在 RegionServer 服务器上,RegionServer 是直接负责存储数据的服务器,RegionServer 保存的表数据直接存储在 Hadoop 的 HDFS 上。

HBase 有一点很特殊:客户端获取数据由客户端直连 RegionServer 的,所以当 Master 挂掉之后你依然可以查询数据,只是丧失了表管理相关的能力。

RegionServer 非常依赖 ZooKeeper 服务,可以说没有 ZooKeeper 就没有 HBase。ZooKeeper 管理了 HBase 所有 RegionServer 的信息,包括具体的数据段存放在哪个 RegionServer 上。客户端每次与 HBase 连接,其实都是先与 ZooKeeper 通信,查询出哪个 RegionServer 需要连接,然后再连接 RegionServer。

 

2、Region

Region 就是一段数据的集合,HBase 中的表一般拥有一个到多个 Region,Region 有以下特性:

  • Region 不能跨服务器,一个 RegionServer 上有一个或者多个 Region;
  • 数据量小的时候,一个 Region 足以存储所有数据;但是,当数据量大的时候,HBase 会拆分 Region;
  • 当 HBase 在进行负载均衡的时候,也有可能会从一台 RegionServer 上把 Region 移动到另一台 RegionServer 上;
  • Region 是基于 HDFS 的,它的所有数据存取操作都是调用了 HDFS 的客户端接口来实现的。

3、RegionServer

RegionServer 就是存放 Region 的容器,直观上说就是服务器上的一个服务。一般来说,一个服务器只会安装一个 RegionServer 服务。当客户端从 ZooKeeper 获取 RegionServer 的地址后,它会直接从 RegionServer 获取数据。

4、Master

HBase 中 Master 的角色不像领导,更像是打杂的。客户端从 ZooKeeper 获取了 RegionServer 的地址后,会直接从 RegionServer 获取数据。其实不光是获取数据,包括插入、删除等所有的数据操作都是直接操作 RegionServer,而不需要经过 Master。

Master 只负责各种协调工作,比如建表、删表、移动 Region、合并等操作。它们的共性就是需要跨 RegionServer,这些操作由哪个 RegionServer 来执行都不合适,所以 HBase 就将这些操作放到了Master上了。

这种结构的好处是大大降低了集群对 Master 的依赖,而 Master 节点一般只有一个到两个,一旦宕机,如果集群对 Master 的依赖度很大,那么就会产生单点故障问题。在 HBase 中,即使 Master 宕机了,集群依然可以正常地运行,依然可以存储和删除数据。

四、HBase 存储架构

1、存储架构

最基本的存储单位是列(column),一个列或者多个列形成一行(row)。传统数据库是严格的行列对齐,比如这行有三个列 a、b、c,下一行肯定也有三个列 a、b、c。而在 HBase 中,这一行有三个列 a、b、c,下一个行也许是有 4 个列 a、e、f、g。

在 HBase 中,行跟行的列可以完全不一样,这个行的数据跟另外一个行的数据也可以存储在不同的机器上,甚至同一行内的列也可以存储在完全不同的机器上!每个行(row)都拥有唯一的行键(row key)来标定这个行的唯一性。每个列都有多个版本,多个版本的值存储在单元格(cell)中,若干个列又可以被归类为一个列族

 

2、行键(rowkey)

rowkey 是由用户指定的一串不重复的字符串,rowkey 会直接决定这个 row 的存储位置的。HBase 中无法根据某个 column 来排序,系统永远是根据 rowkey 来排序的(根据字典排序),rowkey 就是决定 row 存储顺序的唯一凭证。

如果插入 HBase 的时候,不小心用了之前已经存在的 rowkey,这会把之前存在的那个 row 更新掉。之前已经存在的值会被放到这个单元格的历史记录里面,并不会丢掉,只是你需要带上版本参数才可以找到这个值。一个列上可以存储多个版本的单元格(cell),单元格就是数据存储的最小单元。

3、列族(column family)

若干列可以组成列族(column family),建表的时候有几个列族是一开始就定好的。表的很多属性,比如过期时间、数据块缓存以及是否压缩等都是定义在列族上,而不是定义在表上或者列上。

同一个表里的不同列族可以有完全不同的属性配置,但是同一个列族内的所有列都会有相同的属性,因为他们都在一个列族里面,而属性都是定义在列族上的。一个没有列族的表是没有意义的,因为列必须依赖列族而存在。

列名称的规范是列族:列名,比如 brother:age、brother:name。列族存在的意义是:HBase 会把相同列族的列尽量放在同一台机器上,如果想让某几个列被放到一起,你就给他们定义相同的列族。

一个表要设置多少个列族比较合适?官方的建议是:越少越好(一般来说一个就够用了),因为 HBase 并不希望大家指定太多的列族。列族太多会极大程度地降低数据库性能;此外,列族定得太多,容易出 BUG。

4、单元格(cell)

一个列上可以存储多个版本的值,多个版本的值被存储在多个单元格里面,多个版本之间用版本号(Version)来区分,唯一确定一条结果的表达式应该是:行键:列族:列:版本号(rowkey:column family:column:version)。不过,版本号是可以省略的,如果你不写版本号,HBase 默认返回最后一个版本的数据。

 

一个 Region 就是多个行(Row)的集合,在 Region 中行的排序按照行键(rowkey)字典排序。

五、总结

1、HBase 与关系型数据库的功能对比

 

2、主要概念回顾

  • Maser
  • RegionServer
  • HDFS
  • Client
  • ZooKeeper
  • Region
  • Row
  • Rowkey
  • Column Family
  • Column
  • Cell

如果看到这些词语,你能回想起它们之间的关系,那么,恭喜你已迈入 HBase 的大门。

3、参考文档

  • 《HBase 不睡觉书》
  • 一分钟搞懂列式与行式数据库
  • blog.51cto.com/flyfish225/…

 

 

 

《HBase 不睡觉》第二章 - 让 HBase 跑起来

《HBase 不睡觉书》是一本让人看了不会睡着的 HBase 技术书籍,写的非常不错,为了加深记忆,决定把书中重要的部分整理成读书笔记,便于后期查阅,同时希望为初学 HBase 的同学带来一些帮助。

目录

  • 第一章 - 初识 HBase
  • 第二章 - 让 HBase 跑起来
  • 第三章 - HBase 基本操作
  • 第四章 - 客户端 API 入门
  • 第五章 - HBase 内部探险
  • 第六章 - 客户端 API 的高阶用法
  • 第七章 - 客户端 API 的管理功能
  • 第八章 - 再快一点
  • 第九章 - 当 HBase 遇上 MapReduce

本文不会详细介绍 HBase 的按照过程,主要介绍一些安装的注意事项。

一、小技巧

1、添加 hadoop 用户并分配 sudo 权限

(1)切换到root用户,然后建立hadoop用户。

# useradd hadoop# passwd hadoop复制代码

(2)添加 hadoop 到sudoers 列表。

# chmod u+w /etc/sudoers# vi u+w /etc/sudoers

-- 添加下面的代码 --

hadoop ALL=NOPASSWD:ALL复制代码

2、Hadoop 环境变量设置

切换到 hadoop 用户,并编辑 ~/.bashrc 文件,添加以下环境变量:

export HADOOP_HOME=/usr/local/hadoopexport HADOOP_PREFIX=$HADOOP_HOMEexport HADOOP_MAPRED_HOME=$HADOOP_HOME

expOrt HADOOP_COMMON_HOME=$HADOOP_HOMEexport HADOOP_HDES_HOME=$HADOOP_HOME

eXpOrt YARN_HOME=$HADOOP_HOMEexport HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/nativeexport PATH=$PATH:$HADOOP_HOME/sbin:$HADOOP_HOME/binexport HADOOP_INSTALL=$HADOOP_HOME复制代码

有的教程提到配置 HADOOP_HOME,而官方教程说是配置 HADOOP_PREFIX,那么究竟 Hadoop 是用哪个环境变量来标定 Hadoop 的程序文件夹位置?实际上,早期 Hadoop 主要用 HADOOP_HOME 来标定程序文件夹位置,后来改成了 HADOOP_PREFIX,所以为了兼容性,干脆都设置上,并且保持一样的值吧。

3、配置 hadoop-env.sh

编辑 hadoop 的 $HADOOP_PREFIX/etc/hadoop/hadoop-env.sh 文件,在文件开头添加以下变量:

export HADOOP_NAMENODE_OPTS="-Xms1024m -Xmx1024m -XX:+UseParallelGC"export HADOOP_DATANODE_OPTS="-Xms1024m-Xmx1024m"export HADOOP_LOG_DIR=/data/1ogs/hadoop复制代码

  • JVM 运行的内存如果不设定占用大小的话,要么不够,要么就把机器的内存都占满了。只要用到 JVM 的地方都加入内存参数,至少内存多少自己心里有数,让情况可控。
  • 日志文件路径如果不设定的话,多半后期会遇到放日志的分区满了,各种奇怪故障层出不穷。

4、把 hbase 添加到 supergroup 组

由于在伪分布式和完全分布式的情况下 HBase 会直接在 HDFS 的根目录下建立 /hbase 文件夹,在根目录下要建立文件夹需要超级用户组权限。超级用户组权限由 hdfs-site.xml 中的 dfs.permissions.supergroup 来定义,如果你不设定这个参数,默认的超级用户组组名是 supergroup。假定大家都没有设定 dfs.permissions.supergroup 属性,现在需要把 hbase 添加到 Linux 的 supergroup 组去。CentOS 系统可执行下面的语句:

# groupadd supergroup# groupmems -g supergroup -a hbase复制代码

5、特别注意

HBase 自带了一个 ZooKeeper,而且会默认启动自己的 ZooKeeper,如果 HBase 用的是自己的 ZooKeeper,那你在 jps 中看到的 ZooKeeper 名字是 HQuorumPeer。如果你使用的是外部的 ZooKeeper 集群,那么它的名字叫 QuorumPeer 或者 QuorumPeerMain。

是否开启自带的 ZooKeeper 由 conf/hbase-env.sh 中定义的 HBASE_MANAGES_ZK 变量定义。这个变量默认为 true,如果不想使用自带的 ZK 你可以将值改为 false。

# Tell HBase whether it should manage it's own instance of Zookeeper or not.export HBASE_MANAGES_ZK=false复制代码

6、HBase 读取到 HDFS 的配置有三种方式

  • 把 HADOOP_CONF_DIR 添加到 HBASE_CLASSPATH 中(推荐);
  • 把 HDFS 的配置文件复制一份到 HBase 的 conf 文件夹下,或者直接建一个 hdfs-site.xml 的软链接到 hbase/conf下;
  • 把 HDFS 的几个配置项直接写到 hbase-site.xml 文件里面去。

二、HBase 的基本架构

  • HBase 中有一个 Master 用来管理元数据,它就像 Hadoop 中的 namenode;
  • RegionServer 是用来存储数据的,相当于 Hadoop 中的 datanode;
  • ZooKeeper 负责维护 HBase 的所有节点,如果 ZooKeeper 宕掉了,你一个节点都连不上;
  • 生产环境下的完全部署模式是基于 HDFS 的,使用 HDFS 来存储数据,但是在单机模式下 HBase 可以直接使用普通文件系统来存储数据;
  • 在使用中就算把 Master 关掉了,依旧可以从 HBase 中读取数据和写入数据,只是不能建表或者修改表。这是因为客户端读取数据的时候只是跟 ZooKeeper 和 RegionServer 交互,所以,ZooKeeper 甚至比 Master 还重要。

 

三、启用数据块编码

1、数据块编码

数据块编码主要是针对 Key/Value 中的 Key 进行编码,减少 Key 存储所占用的空间,因为很多 Key 的前缀都是重复的。

假设有这样一个表,它的行键(Rowkey)、列族(Column Family)、列(Column)的定义规则是:行键以 myrow 前缀打头,后面跟上数字来组成行键,比如 myrow001、myrow002、myrow003 等,拥有一个列族叫 mycf,mycf 列族中有 5 个列,分别名叫 col1、col2、col3、col4、col5,它们的存储结构如下所示。

 

可以看到这么多行的 Key 其实有很大一部分的字符是重复的,如果我们只存储递进值,就可以避免存储重复的前缀,这就是前缀编码(Prefix)。

2、前缀编码(Prefix)

如果使用前缀编码作为数据块编码方式,那么它只会存储第一个 Key 的完整字符串,后面的 key 只存储跟第一个 key 的差异字符,重新编码过的数据如下所示。

 

可以看到 Key 的存储空间极大地缩小了,编码后的 Key 总存储空间只用了 37 个字符,而未编码前是 180 个字符,空间占用减少了 79%。

3、差异编码(Diff)

差异编码(Diff)比前缀编码更进一步,差异编码甚至把以下字段也一起进行了差异化的编码。

  • 键长度(KeyLen);
  • 值长度(ValueLen);
  • 时间戳(Timestamp),也即是Version;
  • 类型(Type),也即是键类型。

采用了差异编码后的 KeyValue 结构为:

  • 1 byte:标志位;
  • 1-5 bytes:Key 长度(KeyLen);
  • 1-5 bytes:Value 长度(ValLen);
  • 1-5 bytes:前缀长度(Prefix Len);
  • ... bytes:剩余的部分;
  • ... bytes:真正的 Key 或者只是有差异的 key 后缀部分;
  • 1-8 bytes:时间戳(timestamp)或者时间戳的差异部分;
  • 1 byte:Key 类型(type);
  • ... bytes:值(value)。

前缀长度(Prefix Len)字段表示当前的 Key 跟与之相比的 Key 的相同前缀的长度。

 

标志位(Flag)

它是一个二进制数。比如,5=11,7=111。它的作用就是记录当前这个 KeyValue 跟上一个 KeyValue 之间有哪几个字段有差异,以下是产生标志位的部分规则:

  • 如果当前 KeyValue 中的 KeyLen(Key的长度)跟上一个 KeyValue相等,则标志码为 1。
  • 如果当前 KeyValue 中的 ValLen(Value长度)跟上一个 ValLen 相等,则标志码为10。
  • 如果当前 KeyValue 中的 Type 跟上一个 Type 相等,则标志码为100。

只需要把 flag 跟标志码做一个与(&)计算就可以快速地知道这个字段跟上一个字段的差异在哪里,即相同的位置标记为 1

这样编码几乎是最大程度地对数据进行了编码压缩,但是这个编码方式默认是不启用的。为什么?因为太慢了,每条数据都要这样计算一下,获取数据的速度很慢。除非你要追求极致的压缩比,但是不考虑读取性能的时候可以使用它,比如你想要把这部分数据当作归档数据的时候,可以考虑使用差异编码。

4、快速差异编码(Fast Diff)

快速差异编码(Fast Diff)借鉴了 Diff 编码的思路,也考虑到了差异编码速度慢的致命缺陷。快速差异编码的 KeyValue 结构跟差异编码一模一样,只有 Flag 的存储规则不一样,并且优化了 Timestamp 的计算。Fast Diff 的实现比 Diff 更快,也是比较推荐的算法。

如果你想用差异算法来压缩你的数据,那么最好用快速差异编码,不过这个“快速”只是相对本来的差异算法而言的,由于还是有很多计算过程存在,所以快速差异算法的速度依然属于比较慢的

5、前缀树编码(Prefix Tree)

前缀树编码(Prefix Tree)是前缀算法的变体,它是 0.96 版本之后才加入的特性。前缀树编码最大的作用就是提高了随机读的能力,但是其复杂的算法相对地降低了写入的速度,消耗了更多的 CPU 资源,使用时需要在资源的消耗和随机读的性能之间进行取舍。

综上,前缀编码与快速差异编码(Kylin 默认使用该方式)应该算是比较常用的两种数据块编码方式了。

四、启用压缩器

1、压缩器

压缩器的作用是可以把 HBase 的数据按压缩的格式存储,这样可以更节省磁盘空间。当然这完全是可选的,不过推荐大家还是安装 Snappy 压缩器,这是 HBase 官方目前排名比较高的压缩器。

可以通过修改列族描述启用压缩器:

hbase> alter 'mytable',{NAME =>'mycf',COMPRESSION=>'snappy'}复制代码

2、共享 Hadoop 内置的压缩器

由于 Hadoop 的共享库(shared Library)拥有很多资源,包括压缩器,所以可以直接将它们用在 HBase中。可以通过以下命令检查 Hadoop 目前有用的压缩器:

$ hbase --config $HBASE_HOME/conf org.apache.hadoop.util.NativeLibraryChecker复制代码

如果遇到下面的报错信息,则表示 NativeLibraryChecker 无法读取到 Hadoop 的 native 库。

util.NativeCodeLoader: Unable to load native-hadoop library for your platform...
using builtin-java classes where applicable
    Native library checking:
    hadoop: false
    zlib: false
    snappy: false
    1z4: false
    bzip2: false

复制代码

常规的解决方法是在 hbase-env.sh 加入下面的语句:

export HBASE_LIBRARY_PATH=Hadoop的Native包所在路径复制代码

3、Snappy 压缩器

Snappy 是 Google 开发的压缩器,有以下特点:

  • 快速:压缩速度达到 250MB/s;
  • 稳定:已经用于 Google 多个产品长达数年;
  • 健壮:Snappy 的解压器可以保证在数据被损坏的时候也不会太糟;
  • 免费开源。

安装完成后,需要在 hbase-env.sh 加入下面的语句:

export HBASE_LIBRARY_PATH=编码器so文件所在路径:$HBASE_LIBRARY_PATH复制代码

4、GZ 压缩器

一般情况下如果不是对速度要求很低的归档文件,一般不建议使用GZ压缩器,GZ 压缩器的特点:

  • GZ 压缩器拥有最高的压缩比;
  • 速度较慢,占用较多CPU;
  • 安装简单。

Java 已经自带了一个 GZ 压缩器,所以 GZ 压缩器虽然不是性能最好的,但是却是最容易使用的,你什么都不需要设置,只需要直接修改列族的 COMPRESSION 属性为 GZ 即可。

alter test1',{NAME=>'mycf',COMPRESSION=>'GZ'}复制代码

5、LZO 压缩器

在 Snappy 推出之前,LZO 是 HBase 官方推荐的压缩算法。主要原因是 GZ 压缩的速度太慢了,而 LZO 正好就是专注于速度,所以相比起来使用 LZO 会比 GZ 更好,不过自从 Snappy 出了之后,LZO 就没有什么优势了。

6、LZ4 压缩器

LZ4 的特点:

  • 拥有低丢失率;
  • 速度很快,可以达到400M/s每核。

LZ4 比 Snappy 更快,LZ4 压缩器已经集成在 libhadoop.so 中,所以只需要让 HBase 加载Hadoop 自带的原生库即可。

五、总结

使用数据块编码还是压缩器取决于你存储的数据中是限定符占的空间较大还是值占的空间较大。

  • 如果是限定符占的空间较大,建议使用数据块编码。
  • 如果是值占的空间较大,建议使用编码器。

最开始学习 HBase 的时候,大多都是直接使用 Java API 去进行表操作,很少去关注 HBase 安装相关的内容;通过上述的介绍,至少数据块编码和压缩器在以后建表时候还是可以考虑的,官方推荐的 Snappy 压缩器以及前缀编码都是即简单又有效的调优方法。

 

 

《HBase 不睡觉》第三章 - HBase 基本操作

《HBase 不睡觉书》是一本让人看了不会睡着的 HBase 技术书籍,写的非常不错,为了加深记忆,决定把书中重要的部分整理成读书笔记,便于后期查阅,同时希望为初学 HBase 的同学带来一些帮助。

目录

  • 第一章 - 初识 HBase
  • 第二章 - 让 HBase 跑起来
  • 第三章 - HBase 基本操作
  • 第四章 - 客户端 API 入门
  • 第五章 - HBase 内部探险
  • 第六章 - 客户端 API 的高阶用法
  • 第七章 - 客户端 API 的管理功能
  • 第八章 - 再快一点
  • 第九章 - 当 HBase 遇上 MapReduce

本文主要介绍 hbase shell 的使用。

一、表操作(DDL)

1、启动 HBase Shell

一般的数据库都有命令行工具,HBase 也自带了一个用 JRuby(JRuby 是用 Java 写的 Ruby 解释器)写的 shell 命令行工具,执行以下命令来进入 HBase 的 shell:

# 一般集群安装好可以直接使用 hbase shell 启动

$ HBASE_HOME/bin/hbase shell复制代码

 

2、新建表(create)

新建表需要注意的几点:

  • HBase 的表都是由列族(Column Family)组成的;
  • 没有列族的表是没有意义的;
  • 列并不是依附于表上,而是依附于列族上。

 

可用通过下面的命令新建一个表:

# 新建一个表 'test',包含了一个列族 'cf'# HBase 新建表时,至少需要一个列族

create 'test', 'cf'复制代码

3、查看数据库表(list)

用list命令可以看到整个库中有哪些表:

hbase(main):010:0> list
TABLE                                                                           test                                                                            test1                                                                           test3                                                                           
3 row(s)
Took 0.0048 seconds                                                             
=> ["test", "test1", "test3"]

复制代码

4、查看表属性(describe)

用 describe 命令查看表的元信息:

hbase(main):018:0> describe "test"
Table test is ENABLEDtest
COLUMN FAMILIES DESCRIPTION
{
    NAME => 'cf',
    VERSIONS => '1',
    EVICT_BLOCKS_ON_CLOSE => 'false',
    NEW_VERSION_BEHAVIOR => 'false',
    KEEP_DELETED_CELLS => 'FALSE',
    CACHE_DATA_ON_WRITE => 'false',
    DATA_BLOCK_ENCODING => 'NONE',
    TTL => 'FOREVER',
    MIN_VERSIONS => '0',
    REPLICATION_SCOPE => '0',
    BLOOMFILTER => 'ROW',
    CACHE_INDEX_ON_WRITE => 'false',
    IN_MEMORY => 'false',
    CACHE_BLOOMS_ON_WRITE => 'false',
    PREFETCH_BLOCKS_ON_OPEN => 'false',
    COMPRESSION => 'NONE',
    BLOCKCACHE => 'true',
    BLOCKSIZE => '65536'
}
1 row(s)
Took 0.0504 seconds

复制代码

可以看到,默认是没有设置压缩和数据块编码。

5、删除表(drop)

在删除 HBase 表之前的时候,必须先执行停用(disable)命令,因为可能有很多客户端现在正好连着,而且也有可能 HBase 正在做合并或者分裂操作。如果你这时删除了表,会造成无法恢复的错误,HBase 也不会让你直接就删除表,而是需要先做一个 disable 操作,意思是把这个表停用掉,并且下线。

hbase(main):019:0> disable "test"
Took 0.8052 seconds                                                             
hbase(main):020:0> drop "test"
Took 0.4512 seconds                                                             
hbase(main):021:0>

复制代码

在没有什么数据或者没有什么人使用的情况下 disable 命令执行得很快,但如果在系统已经上线了,并且负载很大的情况下 disable 命令会执行得很慢,因为 disable 要通知所有的 RegionServer 来下线这个表,并且有很多涉及该表的操作需要被停用掉,以保证该表真的已经完全不参与任何工作了。

6、修改表(alter)

可以使用 alter 命令对表进行修改,修改时无需禁用表,但是强烈建议在生产环境下执行这个命令之前,最好先停用(disable)这个表。因为对列族的所有操作都会同步到 所有拥有这个表的 RegionServer 上,当有很多客户端都在连着的时候,直接新增一个列族对性能的影响较大(还有可能出现意外的问题)。

# 修改多个属性

alter 't1', 'f1', {NAME => 'f2', IN_MEMORY => true}, {NAME => 'f3', VERSIONS => 5}# 新增列族

alter 't1', 'cf2'# 删除列族

alter 't1', NAME => 'f1', METHOD => 'delete'复制代码

二、数据操作(DML)

1、插入(put)

HBase 中行的每一个列都存储在不同的位置,插入数据时必须指定要存储在哪个单元格;而单元格需要根据表、行、列这几个维度来定位,因此插入数据的时候必须指定把数据插入到哪个表的哪个列族的哪个行的哪个列,例如:

hbase(main):024:0> put 'test1','row1','cf:name','jack'
Took 0.0838 seconds                                                             
hbase(main):025:0> scan 'test1'
ROW                   COLUMN+CELL                                               
 row1                 column=cf:name, timestamp=1543161899520, value=jack       
1 row(s)
Took 0.0301 seconds

复制代码

向 test 表插入一个单元格,这个单元格的 rowkey 为 row1,该单元格的列族为 cf,该单元格的列名为 name,数据值为 jack。

插入成功后,使用 scan 命令查看表中数据,可以看到表中有一条记录,ROW 列显示的就是 rowkey,COLUMN+CELL 显示的就是这个记录的具体列族(column 里面冒号前面的 部分)、列(colum 里面冒号后面的部分)、时间戳(timestamp)、值(value)信息。

  • 时间戳:每一个单元格都可以存储多个版本(version)的值,HBase 的单元格并没有 version 这个属性,它用 timestamp 来存储该条记录的时间戳,这个时间戳就用来当版本号使用;这个 timestamp 虽然说是时间的标定,其实你可以输入任意的数字,比如 1、2、3 都可以存储进去。
  • 列族和列的标识:HBase 并没有专门的一个列族的栏来显示列族这个属性,它总是把列族和列用 “列族:列” 的组合方式来一起显示,无论是 put 存储还是 scan 的查询使用的列定义,都是 列族:列” 的格式。

2、获取单条数据(get)

get 只能查询一个单元格的记录,在表的数据很大的时候,get 查询的速度远远高于 scan。

get 'test','row7',{COLUMN=>'cf:name',VERSIONS=>5}
 
COLUMN        CELL
cf:name       timestamp=3, value=wangwu
cf:name       timestamp=2, value=lisi
cf:name       timestamp=1, value=zhangsan

复制代码

3、查询多条数据(scan)

Scan 是最常用的查询表数据的命令,这个命令相当于传统数据库的 select。在 HBase 中我们用起始行(STARTROW)和结束行(ENDROW)来限制显示记录的条数。

STARTROW 和 ENDROW 都是可选的参数,可以不输入。如果 ENDROW 不输入的话,就从 STARTROW 开始一直显示下去直到表的结尾;如果 STARTROW 不输入的话,就从表头一直显示到 ENDROW 为止。

scan 'test',{STARTROW=>'row3'}
ROW         COLUMN+CELL
row3        column=cf:name, timestamp=1471112677398, value=alex
row4        column=cf:name, timestamp=1471112686290, value=jim
 
scan 'test',{ENDROW=>'row4'}
ROW         COLUMN+CELL
row2        column=cf:name, timestamp=2222222222222, value=billy
row3        column=cf:name, timestamp=1471112677398, value=alex

复制代码

4、删除单元格数据(delete)

删除表数据可以使用 delete 命令:

# 删除某一单元格数据

delete 'test','row4','cf:name'

# 根据版本删除数据(删除这个版本之前的所有版本)

delete't1','r1','c1',ts复制代码

HBase 删除记录并不是真的删除了数据,而是放置了一个墓碑标记(tombstone marker),把这个版本连同之前的版本都标记为不可见了。这是为了性能着想,这样 HBase 就可以定期去清理这些已经被删除的记录,而不用每次都进行删除操作。

“定期” 的时间点是在 HBase 做自动合并(compaction,HBase整理存储文件时的一个操作,会把多个文件块合并成一个文件)的时候,这样删除操作对于 HBase 的性能影响被降到了最低,就算在很高的并发负载下大量删除记录也不怕了!

在记录被真正删除之前还是可以查询到的,只需要在 scan 命令后跟上 RAW=>true 参数和适当的 VERSIONS 参数就可以看到被打上墓碑标记(tombstone marker)的记录,跟上 RAW 就是查询到表的所有未经过过滤的原始记录。

5、删除整行数据(deleteall)

如果一个行有很多列,用 delete 来删除记录会把人累死,可以 deleteall 命令来删除整行记录。

# 只需要明确到 rowkey 即可
deleteall 'test','row3'

复制代码

三、获取帮助

HBase 还有很多表相关的操作,这里不一一列出,在 shell 控制台可以输入 help 命令获得帮助信息;如果希望查看某个命令的帮助信息,可以执行 help '指令'。

hbase(main):026:0> help
HBase Shell, version 2.0.0-cdh6.0.1, rUnknown, Wed Sep 19 09:14:00 PDT 2018
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.