02.26 HBase基本入門

一、簡介

HBase 是一個開源的、面向列的非關係型分佈式數據庫,目前是Hadoop體系中非常關鍵的一部分。在最初,HBase是基於谷歌的 BigTable 原型實現的,許多技術來自於Fay Chang在2006年所撰寫的Google論文"BigTable"。與 BigTable基於Google文件系統(File System)一樣,HBase則是

基於HDFS(Hadoop的分佈式文件系統)之上而開發的。

HBase 採用 Java 語言實現,在其內部實現了BigTable論文提到的一些壓縮算法、內存操作和布隆過濾器等,這些能力使得HBase 在海量數據存儲、高性能讀寫場景中得到了大量應用,如 Facebook 在 2010年11 月開始便一直選用 HBase來作為消息平臺的存儲層技術。HBase 以 Apache License Version 2.0開源,這是一種對商業應用友好的協議,同時該項目當前也是Apache軟件基金會的頂級項目之一。

有什麼特性

  • 基於列式存儲模型,對於數據實現了高度壓縮,節省存儲成本
  • 採用 LSM 機制而不是B(+)樹,這使得HBase非常適合海量數據實時寫入的場景
  • 高可靠,一個數據會包含多個副本(默認是3副本),這得益於HDFS的複製能力,由RegionServer提供自動故障轉移的功能
  • 高擴展,支持分片擴展能力(基於Region),可實現自動、數據均衡
  • 強一致性讀寫,數據的讀寫都針對主Region上進行,屬於CP型的系統
  • 易操作,HBase提供了Java API、RestAPI/Thrift API等接口
  • 查詢優化,採用Block Cache 和 布隆過濾器來支持海量數據的快速查找

與RDBMS的區別

對於傳統 RDBMS 來說,支持 ACID 事務是數據庫的基本能力,而 HBase 則使用行級鎖來保證寫操作的原子性,但是不支持多行寫操作的事務性,這主要是從靈活性和擴展性上做出的權衡。

ACID 要素包含 原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)以及持久性(Durability)


總體來說, HBase 與傳統關係數據庫的區別,如下表所示:

HBase基本入門

二、數據模型

下面,我們以關係型數據庫的一個數據表來演示 HBase 的不同之處。

先來看下面這張表:

HBase基本入門

這裡記錄的是一些家庭設備上報的狀態數據(DeviceState),其中包括設備名、狀態、時間戳這些字段。

在 HBase 中,數據是按照列族(Column Family,簡稱CF)來存儲的,也就是說對於不同的列會被分開存儲到不同的文件。那麼對於上面的狀態數據表來說,在HBase中會被存儲為兩份:

HBase基本入門

這裡Row-key是唯一定位數據行的ID字段,而Row-key 加上 CF、Column-Key,再加上一個時間戳才可以定位到一個單元格數據。其中

時間戳用來表示數據行的版本, 在HBase中默認會有 3 個時間戳的版本數據,這意味著對同一條數據(同一個Rowkey關聯的數據)進行寫入時,最多可以保存3個版本。

在查詢某一行的數據時,HBase需要同時從兩個列族(文件)中進行查找,最終將結果合併後返回給客戶端。 由此可見如果列族太多,則會影響讀取的性能,在設計時就需要做一些權衡。

由此可見,HBase的使用方式與關係型數據庫是大不相同的,在使用 HBase 時需要拋棄許多關係型數據庫的思維及做法,比如強類型、二級索引、表連接、觸發器等等。

然而 HBase 的靈活性及高度可伸縮性卻是傳統 RDBMS 無法比擬的。

三、安裝HBase

單機環境安裝

1. 準備JDK環境

確保環境上JDK已經裝好,可執行java -version確認:

host:/home/hbase # java -version

openjdk version "1.8.0_201"

OpenJDKRuntimeEnvironment(build 1.8.0_201-Huawei_JDK_V100R001C00SPC060B003-b10)

OpenJDK64-BitServer VM (build 25.201-b10, mixed mode)



2. 下載軟件

官網的下載地址頁面:

http://archive.apache.org/dist/hbase/

選擇合適的版本,比如1.4.10。 下載後解壓:

wget http://archive.apache.org/dist/hbase/2.1.5/hbase-2.1.5-bin.tar.gz

tar -xzvf hbase-2.1.5-bin.tar.gz

mkdir -p /opt/local

mv hbase-2.1.5/opt/local/hbase


配置HBase執行命令路徑:

export HBASE_HOME=/opt/local/hbase

export PATH=$PATH:$HBASE_HOME/bin


3. 配置軟件

vim conf/hbase-env.sh

#JDK安裝目錄

export JAVA_HOME=/usr/local/jre1.8.0_201

#配置hbase自己管理zookeeper

export HBASE_MANAGES_ZK=true


vim conf/hbase-site.xml

<configuration>

<property>

<name>hbase.zookeeper.property.clientPort/<name>

<value>2182/<value>

<property>

<name>hbase.rootdir/<name>

<value>file:///opt/local/hbase/data/<value>

<property>

<name>hbase.zookeeper.property.dataDir/<name>

<value>/opt/local/hbase/data/zookeeper/<value>

<property>

<name>hbase.tmp.dir/<name>

<value>/opt/local/hbase/temp/hbase-${user.name}/<value>


其中 hbase.rootdir 和 hbase.zookeeper.property.dataDir 都用來指定數據存放的目錄,默認情況下hbase會使用/tmp目錄,這顯然是不合適的。配置了這兩個路徑之後,hbase會自動創建相應的目錄。

關於更多的參數設定可參考這裡

4. 啟動軟件

start-hbase.sh


此時查看 logs/hbase-root-master-host-xxx.log,如下:

2019-07-1107:37:23,654 INFO [localhost:33539.activeMasterManager] hbase.MetaMigrationConvertingToPB: hbase:meta doesn't have any entries to update.

2019-07-11 07:37:23,654 INFO [localhost:33539.activeMasterManager] hbase.MetaMigrationConvertingToPB: META already up-to date with PB serialization

2019-07-11 07:37:23,664 INFO [localhost:33539.activeMasterManager] master.AssignmentManager: Clean cluster startup. Assigning user regions

2019-07-11 07:37:23,665 INFO [localhost:33539.activeMasterManager] master.AssignmentManager: Joined the cluster in 11ms, failover=false

2019-07-11 07:37:23,672 INFO [localhost:33539.activeMasterManager] master.TableNamespaceManager: Namespace table not found. Creating...


檢查進程情況,發現進程已經啟動

ps -ef |grep hadoop

root 1104911032207:37 pts/100:00:20/usr/local/jre1.8.0_201/bin/java -Dproc_master-XX:OnOutOfMemoryError=kill -9%p -XX:+UseConcMarkSweepGC-XX:PermSize=128m-XX:MaxPermSize=128m-XX:ReservedCodeCacheSize=256m-Dhbase.log.dir=/opt/local/hbase/logs -Dhbase.log.file=hbase-root-master-host-192-168-138-148.log-Dhbase.home.dir=/opt/local/hbase -Dhbase.id.str=root -Dhbase.root.logger=INFO,RFA -Dhbase.security.logger=INFO,RFAS org.apache.hadoop.hbase.master.HMaster start

root 1890730747007:50 pts/100:00:00 grep --color=auto hadoop


通過JPS(JDK自帶的檢查工具) 可以看到當前啟動的Java進程:

# jps

5701Jps

4826HMaster

1311 jar


查看 data目錄,發現生成了對應的文件:

host:/opt/local/hbase/data # ls -lh .

total 36K

drwx------. 4 root root 4.0KJul1108:08 data

drwx------. 4 root root 4.0KJul1108:08 hbase

-rw-r--r--. 1 root root 42Jul1108:08 hbase.id

-rw-r--r--. 1 root root 7Jul1108:08 hbase.version

drwx------. 2 root root 4.0KJul1108:08MasterProcWALs

drwx------. 2 root root 4.0KJul1108:08 oldWALs

drwx------. 3 root root 4.0KJul1108:08.tmp

drwx------. 3 root root 4.0KJul1108:08WALs

drwx------. 3 root root 4.0KJul1108:08 zookeeper


關於運行模式

HBase啟動時默認會使用單機模式,此時 Zookeeper和 HMaster/RegionServer 會運行在同一個JVM中。以standalone模式啟動的HBase會包含一個HMaster、RegionServer、Zookeeper實例,此時 HBase 會直接使用本地文件系統而不是HDFS。

通過將 conf/hbase-site.xml中的 hbase.cluster.distributed 配置為true,就是集群模式了。在這個模式下,你可以使用分佈式環境進行部署,或者是"偽分佈式"的多進程環境。

<configuration>

<property>

<name>hbase.cluster.distributed/<name>

<value>true/<value>


需要注意的是,如果以standalone啟動的話,HMaster、RegionServer端口都是隨機的,無法通過配置文件指定。

四、基本使用

打開HBase Shell

hbase shell


執行status命令

Version2.1.5, r76ab087819fe82ccf6f531096e18ad1bed079651, WedJun516:48:11 PDT 2019

hbase(main):001:0> status

1 active master, 0 backup masters, 1 servers, 0 dead, 2.0000 average load


這表示有一個Master在運行,一個RegionServer,每個RegionServer包含2個Region。

表操作

  • 創建DeviceState表

hbase(main):002:0> create "DeviceState", "name:c1", "state:c2"

=> Hbase::Table- DeviceState


此時,已經創建了一個DeviceState表,包含name(設備名稱)、state(狀態)兩個列。

查看錶信息

hbase(main):003:0> list

TABLE

DeviceState

1 row(s) in0.0090 seconds

=> ["DeviceState"]

hbase(main):003:0> describe "DeviceState"

TableDeviceStateis ENABLED

DeviceState

COLUMN FAMILIES DESCRIPTION

{NAME => 'name', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSIO

N => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}

{NAME => 'state', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSI

ON => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'}

2 row(s) in0.0870 seconds


  • 寫入數據

通過下面的命令,向DeviceState寫入兩條記錄。

由於有兩個列族,因此需要寫入四個單元格數據:

put "DeviceState", "row1", "name", "空調"

put "DeviceState", "row1", "state", "打開"

put "DeviceState", "row2", "name", "電視機"

put "DeviceState", "row2", "state", "關閉"


  • 查詢數據

查詢某行、某列

hbase(main):012:0> get"DeviceState","row1"

COLUMN CELL

name: timestamp=1562834473008, value=\\\\xE7\\\\x94\\\\xB5\\\\xE8\\\\xA7\\\\x86\\\\xE6\\\\x9C\\\\xBA

state: timestamp=1562834474630, value=\\\\xE5\\\\x85\\\\xB3\\\\xE9\\\\x97\\\\xAD

1 row(s) in0.0230 seconds

hbase(main):013:0> get"DeviceState","row1", "name"

COLUMN CELL

name: timestamp=1562834473008, value=\\\\xE7\\\\x94\\\\xB5\\\\xE8\\\\xA7\\\\x86\\\\xE6\\\\x9C\\\\xBA

1 row(s) in0.0200 seconds


掃描表

hbase(main):026:0> scan "DeviceState"

ROW COLUMN+CELL

row1 column=name:, timestamp=1562834999374, value=\\\\xE7\\\\xA9\\\\xBA\\\\xE8\\\\xB0\\\\x83

row1 column=state:, timestamp=1562834999421, value=\\\\xE6\\\\x89\\\\x93\\\\xE5\\\\xBC\\\\x80

row2 column=name:, timestamp=1562834999452, value=\\\\xE7\\\\x94\\\\xB5\\\\xE8\\\\xA7\\\\x86\\\\xE6\\\\x9C\\\\xBA

row2 column=state:, timestamp=1562835001064, value=\\\\xE5\\\\x85\\\\xB3\\\\xE9\\\\x97\\\\xAD

2 row(s) in0.0250 seconds


查詢數量

hbase(main):014:0> count "DeviceState"

2 row(s) in0.0370 seconds

=> 1


  • 清除數據

刪除某列、某行

delete"DeviceState", "row1", "name"

0 row(s) in0.0080 seconds

hbase(main):003:0> deleteall "DeviceState", "row2"

0 row(s) in0.1290 seconds


清空整個表數據

hbase(main):021:0> truncate "DeviceState"

Truncating'DeviceState' table (it may take a while):

- Disabling table...

- Truncating table...

0 row(s) in3.5060 seconds


刪除表(需要先disable)

hbase(main):006:0> disable "DeviceState"

0 row(s) in2.2690 seconds

hbase(main):007:0> drop "DeviceState"

0 row(s) in1.2880 seconds


五、FAQ

  • A. 啟動時提示 ZK 端口監聽失敗: Could not start ZK at requested port of 2181. ZK was started at port: 2182. Aborting as clients (e.g. shell) will not be able to find this ZK quorum

原因

HBase需要啟動Zookeeper,而本地的2181端口已經被啟用(可能有其他Zookeeper實例)

解決辦法

conf/hbase-site.xml中修改hbase.zookeeper.property.clientPort的值,將其修改為2182,:

<configuration>

<property>

<name>hbase.zookeeper.property.clientPort/<name>

<value>2182/<value>


  • B. 啟動HBase Shell 時提示java.lang.UnsatisfiedLinkError

原因

在執行hbase shell期間,JRuby會在“java.io.tmpdir”路徑下創建一個臨時文件,該路徑的默認值為“/tmp”。如果為“/tmp”目錄設置NOEXEC權限,然後hbase shell會啟動失敗並拋出“java.lang.UnsatisfiedLinkError”錯誤。

解決辦法

  1. 取消/tmp的noexec權限(不推薦)
  2. 設置java.io.tmpdir變量,指向可用的路徑,編輯conf/hbase-env.sh文件:

export HBASE_TMP_DIR=/opt/local/hbase/temp

export HBASE_OPTS="-XX:+UseConcMarkSweepGC -Djava.io.tmpdir=$HBASE_TMP_DIR"


分享到:


相關文章: