HBase の入門から高度な開発知識のレビューまで
HBase の概要
Hadoop の概要
Hadoop は、Apache Foundation によって開発された分散システム インフラストラクチャです。ユーザーは、分散の基礎となる詳細を理解していなくても、分散プログラムを開発できます。高速コンピューティングとストレージのためにクラスターの力を最大限に活用します。Hadoop は分散ファイル システム (Distributed File System) を実装しており、その 1 つが HDFS (Hadoop Distributed File System) です。HDFS は耐障害性が高く、低コストのハードウェアに導入できるように設計されており、アプリケーション データにアクセスするための高いスループットを提供するため、大規模なデータ セットを持つユーザーに適しています。HDFS は POSIX 要件を緩和し、ファイル システム内のデータへのストリーミング アクセスを可能にします。Hadoop フレームワークの中心となる設計は、HDFS と MapReduce です。HDFS は大規模なデータのストレージを提供し、MapReduce は大規模なデータの計算を提供します。
1970 年以来、ほとんどの企業はデータの保存と保守にリレーショナル データベースを使用してきましたが、
ビッグ データ テクノロジの出現後、大量のデータを保有する多くの企業は、大量のデータを保存するために Hadoop などの方法を選択し始めました。
ハドゥープの制限事項
- Hadoop は主にバッチ データ処理を実装し、データにシーケンシャルにアクセスします。
- データを見つけるには、データセット全体を検索する必要があり、データをランダムに読み取ると効率が悪くなります。
HBase と NoSQL
-
NoSQL は、SQL を主言語として使用しない非リレーショナル データベースを指す一般用語です。
-
HBase は、BigTable のオープン ソース Java バージョンです。HDFS 上に構築されたデータベース システムであり、高信頼性、高性能、カラム ストレージ、スケーラビリティ、NoSQL のリアルタイム読み書きを提供します。
-
HBase は主キー (行キー) と主キーの範囲を介してのみデータを取得でき、単一行トランザクションのみをサポートします。
-
主に構造化および半構造化されたルースデータを保存するために使用されます
-
HBase のデータ クエリ機能は非常に単純です。結合などの複雑な操作や複雑なトランザクション (行レベルのトランザクション) はサポートされていません。技術的に言えば、HBase には RDBMS がないため、HBase は「データベース」というよりも「データ ストレージ」に似ています。 . 型付き列、セカンダリ インデックス、高度なクエリ言語などの機能
-
Hbase でサポートされるデータ型: byte[]
-
Hadoop と同様に、Hbase の目標は主に水平方向の拡張に依存しており、安価な商用サーバーを継続的に追加してストレージと処理能力を向上させます。たとえば、クラスターを 10 ノードから 20 ノードに拡張すると、ストレージと処理能力が 2 倍になります。
-
HBase のテーブルには通常、次の特徴があります。
-
大規模: テーブルには数十億の行と数百万の列が含まれる場合があります
-
列指向: 列 (ファミリー) 指向のストレージと権限制御、列 (ファミリー) に依存しない取得
-
スパース: Null 列は記憶域を占有しないため、テーブルを非常にスパースになるように設計できます。
-
HBase アプリケーションのシナリオ
-
オブジェクトストレージ
- 多くの見出し、ニュースのようなニュース、Web ページ、写真が HBase に保存されており、一部のウイルス会社のウイルス データベースも HBase に保存されています。
-
時系列データ
- HBase の上には OpenTSDB モジュールがあり、タイミング シナリオのニーズを満たすことができます。
-
おすすめのポートレート
- ユーザー ポートレートは比較的大きな疎行列であり、Ant Financial のリスク管理は HBase 上に構築されています。
-
時空間データ
- 主に軌跡、気象グリッドなど、滴滴タクシーの軌跡データは主に HBase に保存されており、さらに、データ量が大きいすべての Internet of Vehicles 企業では、データが HBase に保存されています。
-
CubeDB OLAP
- Kylin はキューブ分析ツールです。基礎となるデータは HBase に保存されます。多くのお客様は、オンライン レポート クエリのニーズを満たすために、オフライン計算に基づいてキューブを構築し、HBase に保存しています。
-
メッセージ・ご注文
- 通信および銀行業務の分野では、多くの注文クエリが基盤となるストレージに保存され、多くの通信およびメッセージ同期アプリケーションが HBase 上に構築されています。
-
フィードストリーム
- 典型的なアプリケーションは、xx Moments に似たアプリケーションで、ユーザーはいつでも新しいコンテンツを投稿したり、コメントしたり、「いいね!」をしたりできます。
-
新しいSQL
- その上に Phoenix プラグインがあり、セカンダリ インデックスと SQL のニーズを満たすことができます。従来のデータをドッキングするには SQL の非トランザクション ニーズが必要です。
-
他の
- クローラーデータを保存する
- 大容量データのバックアップ
- 短縮URL
- …
開発パス
年 | 主要なイベント |
---|---|
2006 年 11 月 | Google が BigTable の論文をリリース。 |
2007 年 10 月 | Hadoop 0.15.0 に基づく、最初に利用可能な HBase バージョンのリリース |
2008 年 1 月 | HBaseはHadoopのサブプロジェクトと呼ばれます |
2010 年 5 月 | HBaseはApacheのトップレベルプロジェクトと呼ばれます |
HBaseの機能
- 強い整合性の読み取り/書き込み
- HBASE は「結果的に整合性のある」データ ストアではありません
- 高速カウンタ集計などのタスクに最適です。
- 自動チャンク化
- HBase テーブルはリージョンを介してクラスター上に分散され、データが増加すると、リージョンは自動的に分割され、再分散されます。
- 自動リージョンサーバーフェイルオーバー
- Hadoop/HDFSの統合
- HBase は、分散ファイル システムとしてすぐに使用できる HDFS をサポートします
- MapReduce
- HBase は、HBase をソースおよびシンクとして使用し、MapReduce による大規模並列処理をサポートします。
- JavaクライアントAPI
- HBase は、プログラムによるアクセスのための使いやすい Java API をサポートしています
- 節約/REST API
- ブロックキャッシングとブルームフィルター
- HBase は、大量のクエリを最適化するためのブロック キャッシュとブルーム フィルターをサポートしています
- 運用管理
- HBase は、ビジネスの洞察と JMX メトリクス用の組み込み Web ページを提供します。
RDBMS と HBase の比較
リレーショナルデータベース
リレーショナルデータベースの構造
- データベースはテーブルの形式で存在します
- FAT、NTFS、EXT、ファイルシステムをサポート
- 主キー (pk) を使用する
- サブデータベースとテーブルは外部ミドルウェアを通じてサポートできますが、最下層は依然としてスタンドアロン エンジンです。
- 行、列、セルを使用する
リレーショナルデータベース機能
- 上方拡張のサポート (より優れたハードウェア構成の購入)
- SQLクエリを使用する
- 行指向、つまり各行は連続した単位です
- データ量はサーバー構成によって異なります
- ACIDサポートあり
- 構造化データに適しています
- 従来のリレーショナル データベースは一般に集中型です。
- サポート事務
- サポート参加
HBase
HBase 構造
- テーブル形式で存在する
- HDFSファイルシステムをサポート
- 行キーを使用する
- 分散ストレージとコンピューティング エンジンのネイティブ サポート
- 行、列、列クラスター、セルの操作
HBase 関数
- 対外展開をサポート
- API と MapReduce、Spark、Flink を使用して HBase テーブル データにアクセスする
- 列クラスターを対象とする。つまり、各列クラスターは連続した単位である。
- データの総量は特定のマシンには依存せず、マシンの数に依存します。
- HBase不支持ACID(Atomicity、Consistency、Isolation、Durability)
- 構造化データと非構造化データに適しています
- 一般に配布されている
- l HBase はトランザクションをサポートしませんが、単一行のデータに対するトランザクション操作のみをサポートします。
- 結合はサポートされていません
HDFS と HBase
HDFS
- HDFS は、大きなファイルの保存に最適な分散ファイル システムです。
- HDFS は一般的なファイル システムではないため、ファイル内の特定のデータを迅速にクエリすることはできません。
HBase
- HBase は HDFS 上に構築されており、大規模なテーブルの高速レコード検索 (および更新) を提供します。
- HBase は、高速検索のために、内部的に大量のデータを HDFS の「StoreFiles」と呼ばれるインデックスに配置します。
- Hbase は高速クエリやその他のニーズには適していますが、大規模な OLAP アプリケーションには適していません。
Hive与HBase
Hive と Hbase は、Hadoop に基づいた 2 つの異なるテクノロジです。Hive
は SQL に似たエンジンで、MapReduce タスクを実行します。Hbase は、
Hadoop 上の NoSQL キー/値データベースです。
これら 2 つのツールは同時に使用できます。Google を使用して検索し、FaceBook を使用してソーシャル化するのと同じように、Hive は統計クエリに使用でき、HBase はリアルタイム クエリに使用できます。データを Hive から HBase に書き込むことも、HBase から Hive に書き戻すこともできます。
ハイブ
- データウェアハウス分析ツール
- Hive の本質は、実際には、Mysql の HDFS に既に保存されているファイル間に全単射関係を作成し、HQL を使用してクエリを管理しやすくすることに相当します。
- データ分析とクリーニングに使用されます
- Hive は、待ち時間が長く、オフラインのデータ分析とクリーニングに適しています。
- HDFS、MapReduce に基づく
- Hive に保存されているデータは依然として DataNode 上にあり、記述された HQL ステートメントは最終的に実行のために MapReduce コードに変換されます。
HBase
- NoSQLデータベース
- これは、列ストレージを重視した非リレーショナル データベースです。
- 構造化データと非構造化データの保存に使用されます
- これは、非リレーショナル データを 1 つのテーブルに保存するのに適していますが、関連するクエリや結合などの操作には適していません。
- HDFSに基づく
- 永続的なデータ ストレージの形式は Hfile で、DataNode に格納され、ResionServer によってリージョン形式で管理されます。
- 低遅延でオンラインビジネス用途に最適
- 大量のエンタープライズ データに直面する場合、HBase は効率的なデータ アクセス速度を提供しながら、大量のデータを 1 つのテーブルに直接保存できます。
HBase クラスターの構築とデプロイメント
【知らせ】
HBase のクラスターのインストールとデプロイについては、別の記事に書かれています: https://blog.csdn.net/wt334502157/article/details/116837187
スペースの都合上、展開の内容は展開記事に別途記載します。
HBase データモデル
構造の紹介
HBASE では、データは行と列を含むテーブルに保存されます。これはリレーショナル データベース (RDBMS) と同じように見えますが、HBASE テーブルを多次元のマップ構造と考えると理解しやすくなります。
ローキー | C1列 | C2カラム |
---|---|---|
行キー | 列 1 | 列 2 | 列 3 | 列 4 | 列 5 | 列 6 |
行キー | 0001 |
---|---|
C1(地図) | 列 1 => 値 1 列 2 => 値 2 列 3 => 値 3 |
C2(マップ) | 列 4 => 値 4 列 5 => 値 5 列 6 => 値 6 |
HBase の共通用語
テーブル
- HBase のデータはテーブル形式で編成されます
- HBase のテーブルは複数の行で構成されます
列(row)
- HBase の行は行キー (行キー) と 1 つ以上の列で構成され、列の値は行キーと列に関連付けられます。
- 行は行キーによって辞書順にソートされて保存されます
- 行キーの設計は非常に重要なので、関連する行をまとめて保存するようにしてください。
- 例: ストア Web サイトのドメイン。行キーがドメインの場合、ドメイン名を逆にして保存する必要があります (org.apache.www、org.apache.mail、org.apache.jira)。このように、すべての Apache ドメインは、サブドメインの最初の文字に基づいて展開されるのではなく、テーブルにまとめて保存されます。
列(Column)
- HBASE の列は、列ファミリーと列修飾子で構成されます
- 例: [列名:列修飾子名]。C1:USER_ID、C1:NAME
列簇(Column Family)
- 列クラスターは、パフォーマンス上の理由から一連の列とその値をまとめて編成します。
- 各列ファミリーには一連のストレージ プロパティがあります。
- コンテンツにキャッシュする必要があるか
- データの圧縮方法や行キーのエンコード方法など。
- テーブルの各行には同じ列バンドルがありますが、列バンドルには何も格納されません。
- すべての列データは一緒に保存されます (ファイル システム HDFS)
- HBase は、すべての列バンドルで同じ列を保持し、同じタイプの列を 1 つの列バンドルに入れることを公式に推奨しています。
列修飾子
- 列バンドルには、保存されたデータのインデックスを提供する個々の列修飾子が含まれています。
- 列クラスターはテーブルの作成時に固定されますが、列修飾子は制限されません。
- 異なる行には異なる列識別子が割り当てられる場合があります
細胞
- セルは行、列系列、および列修飾子の組み合わせです。
- 値とタイムスタンプ (値のバージョンを示す) が含まれます。
- セルの内容はバイナリで保存されます
行 | 列+セル |
---|---|
1250995 | 列=C1:アドレス、タイムスタンプ=1588591604729、値=\xC9\xBD\xCE\xF7\xCA |
1250995 | 列=C1:LATEST_DATE、タイムスタンプ=1588591604729、値=2019-03-28 |
1250995 | 列=C1:NAME、タイムスタンプ=1588591604729、値=\xB7\xBD\xBA\xC6\xD0\xF9 |
1250995 | 列=C1:NUM_CURRENT、タイムスタンプ=1588591604729、値=398.5 |
1250995 | 列=C1:NUM_PREVIOUS、タイムスタンプ=1588591604729、値=379.5 |
1250995 | 列=C1:NUM_USEAGE、タイムスタンプ=1588591604729、値=19 |
1250995 | 列=C1:PAY_DATE、タイムスタンプ=1588591604729、値=2019-02-26 |
1250995 | 列=C1:RECORD_DATE、タイムスタンプ=1588591604729、値=2019-02-11 |
1250995 | 列=C1:SEX、タイムスタンプ=1588591604729、値=\xC5\xAE |
1250995 | 列=C1:TOTAL_MONEY、タイムスタンプ=1588591604729、値=114 |
概念モデル
行キー | タイムスタンプ | コラムファミリーの内容 | ColumnFamilyアンカー | コラム家族の人々 |
---|---|---|---|---|
「com.cnn.www」 | t9 | アンカー:cnnsi.com = 「CNN」 | ||
「com.cnn.www」 | t8 | アンカー:my.look.ca = “CNN.com” | ||
「com.cnn.www」 | t6 | 内容:html = “…” | ||
「com.cnn.www」 | t5 | 内容:html = “…” | ||
「com.cnn.www」 | t3 | 内容:html = “…” | ||
「com.example.www」 | t5 | 内容:html = “…” | 人物:著者 = 「ジョン・ドゥ」 |
- 上の表には 2 行 3 列 (contens、ancho、people) があります。
- 行「com.cnn.www」には、2 つのアンカー列 (anchor:cssnsi.com、anchor:my.look.ca) と 1 つのコンテンツ列 (html) があります。
- 「com.cnn.www」には、HBase に 5 つのバージョンのデータがあります: t3、t5、t6、t8、および t9
- HBase では、行の列が更新されると、最新のデータが最初にランク付けされます。つまり、同じ行キーのデータは逆順にソートされます。
一般的なシェル操作
HBaseの運用プロセス
単純な需要実現から徐々にhbaseの一連の操作と実装に至る
シナリオ要件: 次の注文データがあり、このデータの一部を HBase に保存したいと考えています。
注文ID | 注文の状況 | 支払金額 | 支払い方法ID | ユーザーID | 稼働時間 | カテゴリー |
---|---|---|---|---|---|---|
001 | 既に支払いました | 200.5 | 1 | 001 | 2020-5-2 18:08:53 | 携帯電話; |
次に、HBase シェルを使用して次のことを行います。
-
テーブルの作成
-
データの追加
-
データを更新する
-
データを削除する
-
クエリデータ
テーブルの作成
HBase では、すべてのデータもテーブルに保存されます。注文データを HBase に保存するには、まずテーブルを作成する必要があります
HBaseシェルを起動する
HBase シェルを開始します: hbase シェル
wangting@ops01:/home/wangting >hbase shell
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
HBase Shell
Use "help" to get list of supported commands.
Use "exit" to quit this interactive shell.
For Reference, please visit: http://hbase.apache.org/2.0/book.html#shell
Version 2.0.5, r76458dd074df17520ad451ded198cd832138e929, Mon Mar 18 00:41:49 UTC 2019
Took 0.0043 seconds hbase(main):001:0>
テーブルの作成
構文: create 'テーブル名'、'カラム名'…
オーダー テーブルを作成します。テーブル名は ORDER_INFO です。テーブルには C1 という名前の列があります。
hbase(main):036:0> create 'ORDER_INFO','C1'
Created table ORDER_INFO
Took 0.7355 seconds
=> Hbase::Table - ORDER_INFO
hbase(main):037:0>
【知らせ】:
- create は小文字にする必要があります
- テーブルには複数の列バンドルを含めることができます
- コマンド分析: hbase が提供する Ruby スクリプトの create メソッドを呼び出し、2 つの文字列パラメータを渡します
ビューテーブル
hbase(main):045:0> list
TABLE
ORDER_INFO
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
7 row(s)
Took 0.0098 seconds
=> ["ORDER_INFO", "SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS"]
hbase(main):046:0>
テーブルの削除
テーブルを削除するには、まずテーブルを無効にする必要があります
テーブルを無効にする
構文: 「テーブル名」を無効にする
テーブルの削除
構文: 「テーブル名」を削除
ORDER_INFOテーブルの削除
hbase(main):046:0> disable "ORDER_INFO"
Took 0.4547 seconds
hbase(main):047:0> drop "ORDER_INFO"
Took 0.4385 seconds
hbase(main):048:0> list
TABLE
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
6 row(s)
Took 0.0052 seconds
=> ["SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS"]
データの追加
次に、次のデータを注文テーブルに追加する必要があります。
注文ID | 注文の状況 | 支払金額 | 支払い方法ID | ユーザーID | 稼働時間 | カテゴリー |
---|---|---|---|---|---|---|
ID | 状態 | お金を払う | ペイウェイ | ユーザーID | OPERATION_DATE | カテゴリー |
000001 | 提出された | 4070 | 1 | 4944191 | 2020-04-25 12:09:16 | 携帯電話; |
put 操作を使用する
HBase の put コマンドを使用して、データをテーブルに保存できます。ただし、put は一度に 1 つの列の値しか保存できません。put の構文構造は次のとおりです。
put 'テーブル名','ROWKEY','列名:列名','値'
hbase(main):049:0> create 'ORDER_INFO','C1'
Created table ORDER_INFO
Took 1.2423 seconds
=> Hbase::Table - ORDER_INFO
hbase(main):050:0> put 'ORDER_INFO','000001','C1:ID','000001'
Took 0.1005 seconds
hbase(main):051:0> put 'ORDER_INFO','000001','C1:STATUS','已提交'
Took 0.0044 seconds
hbase(main):052:0> put 'ORDER_INFO','000001','C1:PAY_MONEY',4070
Took 0.0045 seconds
hbase(main):053:0> put 'ORDER_INFO','000001','C1:PAYWAY',1
Took 0.0042 seconds
hbase(main):054:0> put 'ORDER_INFO','000001','C1:USER_ID',4944191
Took 0.0045 seconds
hbase(main):055:0> put 'ORDER_INFO','000001','C1:OPERATION_DATE','2020-04-25 12:09:16'
Took 0.0040 seconds
hbase(main):056:0> put 'ORDER_INFO','000001','C1:CATEGORY','手机;'
Took 0.0053 seconds
hbase(main):057:0>
追加されたデータを表示する
要求将rowkey为:000001对应的数据查询出来
使用get操作
在HBase中,可以使用get命令来获取单独的一行数据。语法:get ‘表名’,‘rowkey’
hbase(main):057:0> get 'ORDER_INFO','000001'
COLUMN CELL
C1:CATEGORY timestamp=1660558718961, value=\xE6\x89\x8B\xE6\x9C\xBA;
C1:ID timestamp=1660558716878, value=000001
C1:OPERATION_DATE timestamp=1660558716999, value=2020-04-25 12:09:16
C1:PAYWAY timestamp=1660558716958, value=1
C1:PAY_MONEY timestamp=1660558716936, value=4070
C1:STATUS timestamp=1660558716907, value=\xE5\xB7\xB2\xE6\x8F\x90\xE4\xBA\xA4
C1:USER_ID timestamp=1660558716978, value=4944191
1 row(s)
Took 0.0520 seconds
hbase(main):058:0>
查看数据value显示中文
在HBase shell中,如果在数据中出现了一些中文,默认HBase shell中显示出来的是十六进制编码。要想将这些编码显示为中文,我们需要在get命令后添加一个属性:{FORMATTER => ‘toString’}
格式为:get ‘ORDER_INFO’,‘000001’, {FORMATTER => ‘toString’}
- { key => value},这个是Ruby语法,表示定义一个HASH结构
- get是一个HBase Ruby方法,’ORDER_INFO’、’000001’、{FORMATTER => ‘toString’}是put方法的三个参数
- FORMATTER要使用大写
- 在Ruby中用{}表示一个字典,类似于hashtable,FORMATTER表示key、’toString’表示值
hbase(main):058:0> get 'ORDER_INFO','000001', {FORMATTER => 'toString'}
COLUMN CELL
C1:CATEGORY timestamp=1660558718961, value=手机;
C1:ID timestamp=1660558716878, value=000001
C1:OPERATION_DATE timestamp=1660558716999, value=2020-04-25 12:09:16
C1:PAYWAY timestamp=1660558716958, value=1
C1:PAY_MONEY timestamp=1660558716936, value=4070
C1:STATUS timestamp=1660558716907, value=已提交
C1:USER_ID timestamp=1660558716978, value=4944191
1 row(s)
Took 0.0222 seconds
hbase(main):059:0>
更新操作
将订单ID为000001的状态,更改为「已付款」
使用put操作
同样,在HBase中,也是使用put命令来进行数据的更新,语法与之前的添加数据一模一样
hbase(main):060:0> get 'ORDER_INFO','000001', {
FORMATTER => 'toString'}
COLUMN CELL
C1:CATEGORY timestamp=1660558718961, value=手机;
C1:ID timestamp=1660558716878, value=000001
C1:OPERATION_DATE timestamp=1660558716999, value=2020-04-25 12:09:16
C1:PAYWAY timestamp=1660558716958, value=1
C1:PAY_MONEY timestamp=1660558716936, value=4070
C1:STATUS timestamp=1660558716907, value=已提交
C1:USER_ID timestamp=1660558716978, value=4944191
1 row(s)
Took 0.0258 seconds
hbase(main):061:0> put 'ORDER_INFO','000001','C1:STATUS','已付款'
Took 0.0051 seconds
hbase(main):062:0> get 'ORDER_INFO','000001', {
FORMATTER => 'toString'}
COLUMN CELL
C1:CATEGORY timestamp=1660558718961, value=手机;
C1:ID timestamp=1660558716878, value=000001
C1:OPERATION_DATE timestamp=1660558716999, value=2020-04-25 12:09:16
C1:PAYWAY timestamp=1660558716958, value=1
C1:PAY_MONEY timestamp=1660558716936, value=4070
C1:STATUS timestamp=1660615875423, value=已付款
C1:USER_ID timestamp=1660558716978, value=4944191
1 row(s)
Took 0.0143 seconds
hbase(main):063:0>
- HBase中会自动维护数据的版本
- 每次put操作的执行,都会重新生成新的时间戳
删除操作
删除状态列数据
将订单ID为000001的状态STSTUS列删除
使用delete操作
在HBase中,可以使用delete命令来将一个单元格的数据删除
语法格式:delete ‘表名’, ‘rowkey’, ‘列蔟:列’
注意:此处HBase默认会保存多个时间戳的版本数据,所以这里的delete删除的是最新版本的列数据
hbase(main):068:0> delete 'ORDER_INFO', '000001', 'C1:STATUS'
Took 0.0136 seconds
删除整行数据
将订单ID为000001的信息全部删除(删除所有的列)
使用deleteall操作
deleteall命令可以将指定rowkey对应的所有列全部删除
语法格式:deleteall ‘表名’,‘rowkey’
hbase(main):070:0> deleteall 'ORDER_INFO','000001'
Took 0.0091 seconds
清空表
将ORDER_INFO的数据全部删除
使用truncate操作
truncate命令用来清空某个表中的所有数据
语法格式:truncate “表名”
hbase(main):071:0> truncate 'ORDER_INFO'
Truncating 'ORDER_INFO' table (it may take a while):
Disabling table...
Truncating table...
Took 1.5176 seconds
导入测试数据文件
需求:ORDER_INFO.txt 中,有一份这样的HBase数据集,我们需要将这些指令放到HBase中执行,将数据导入到HBase中
资料下载:wget http://osswangting.oss-cn-shanghai.aliyuncs.com/hbase/ORDER_INFO.txt
注意是linux命令行操作,并非进入到交互界面后
wangting@ops01:/home/wangting/hbase >ll
total 52
-rw-r--r-- 1 wangting wangting 49366 Aug 5 16:34 ORDER_INFO.txt
wangting@ops01:/home/wangting/hbase >
wangting@ops01:/home/wangting/hbase >pwd
/home/wangting/hbase
wangting@ops01:/home/wangting/hbase >
wangting@ops01:/home/wangting/hbase >hbase shell /home/wangting/hbase/ORDER_INFO.txt
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html multiple_bindings for an explanation.
Took 0.5581 seconds Took 0.0072 seconds Took 0.0047 seconds Took 0.0046 seconds Took 0.0064 seconds
...
...
计数操作
查看HBase中的ORDER_INFO表,一共有多少条记录
使用count操作
hbase(main):074:0> count 'ORDER_INFO'
66 row(s)
Took 0.0570 seconds => 66
hbase(main):075:0>
注意:这个操作是比较耗时的。在数据量大的这个命令可能会运行很久,本次导入数据量非常少
大量数据的计数统计方式
当HBase中数据量大时,可以使用HBase中提供的MapReduce程序来进行计数统计
语法如下:$HBASE_HOME/bin/hbase org.apache.hadoop.hbase.mapreduce.RowCounter ‘表名’
wangting@ops01:/home/wangting/hbase >$HBASE_HOME/bin/hbase org.apache.hadoop.hbase.mapreduce.RowCounter 'ORDER_INFO'
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
2022-08-16 10:51:15,013 INFO [main] client.RMProxy: Connecting to ResourceManager at ops02/11.8.36.63:8032
2022-08-16 10:51:17,747 INFO [main] zookeeper.ReadOnlyZKClient: Connect 0x0e27ba81 to
...
...
HBase Counters
BYTES_IN_REMOTE_RESULTS=5616
BYTES_IN_RESULTS=5616
MILLIS_BETWEEN_NEXTS=591
NOT_SERVING_REGION_EXCEPTION=0
NUM_SCANNER_RESTARTS=0
NUM_SCAN_RESULTS_STALE=0
REGIONS_SCANNED=1
REMOTE_RPC_CALLS=1
REMOTE_RPC_RETRIES=0
ROWS_FILTERED=0
ROWS_SCANNED=66
RPC_CALLS=1
RPC_RETRIES=0
org.apache.hadoop.hbase.mapreduce.RowCounter$RowCounterMapper$Counters
ROWS=66
File Input Format Counters
Bytes Read=0
File Output Format Counters
Bytes Written=0
wangting@ops01:/home/wangting/hbase >
最终可以看到ROWS=66的输出信息
扫描操作
查看ORDER_INFO表中所有的数据
使用scan操作
在HBase,我们可以使用scan命令来扫描HBase中的表。语法:scan ‘表名’
hbase(main):075:0> scan 'ORDER_INFO',{
FORMATTER => 'toString'}
ROW COLUMN+CELL
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:CATEGORY, timestamp=1660617813649, value=手机;
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:OPERATION_DATE, timestamp=1660617813327, value=2020-04-25 12:09:16
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:PAYWAY, timestamp=1660617812628, value=1
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:PAY_MONEY, timestamp=1660617812242, value=4070
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:STATUS, timestamp=1660617811677, value=已提交
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:USER_ID, timestamp=1660617812988, value=4944191
0968a418-f2bc-49b4-b9a9-2157cf214cfd column=C1:CATEGORY, timestamp=1660617813651, value=家用电器;电脑;
....
....
注意:尽量避免scan一张大表
查询订单数据(只显示3条)
hbase(main):076:0> scan 'ORDER_INFO', {
LIMIT => 3, FORMATTER => 'toString'}
只查询订单状态以及支付方式,并且只展示3条数据
hbase(main):077:0> scan 'ORDER_INFO', {
LIMIT => 3, COLUMNS => ['C1:STATUS', 'C1:PAYWAY'], FORMATTER => 'toString'}
ROW COLUMN+CELL
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:PAYWAY, timestamp=1660617812628, value=1
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:STATUS, timestamp=1660617811677, value=已提交
0968a418-f2bc-49b4-b9a9-2157cf214cfd column=C1:PAYWAY, timestamp=1660617812632, value=1
0968a418-f2bc-49b4-b9a9-2157cf214cfd column=C1:STATUS, timestamp=1660617811735, value=已完成
0e01edba-5e55-425e-837a-7efb91c56630 column=C1:PAYWAY, timestamp=1660617812639, value=3
0e01edba-5e55-425e-837a-7efb91c56630 column=C1:STATUS, timestamp=1660617811748, value=已付款
3 row(s)
Took 0.0127 seconds
查询指定订单ID的数据并以中文展示
根据ROWKEY来查询对应的数据,ROWKEY为02602f66-adc7-40d4-8485-76b5632b5b53,只查询订单状态、支付方式,并以中文展示
要查询指定ROWKEY的数据,需要使用ROWPREFIXFILTER
语法格式:scan ‘表名’, {ROWPREFIXFILTER => ‘rowkey’}
hbase(main):078:0> scan 'ORDER_INFO', {
ROWPREFIXFILTER => '02602f66-adc7-40d4-8485-76b5632b5b53', COLUMNS => ['C1:STATUS', 'C1:PAYWAY'], FORMATTER => 'toString'}
ROW COLUMN+CELL
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:PAYWAY, timestamp=1660617812628, value=1
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:STATUS, timestamp=1660617811677, value=已提交
1 row(s)
Took 0.0103 seconds
hbase(main):079:0>
过滤器
在HBase中,如果要对海量的数据来进行查询,此时基本的操作是比较无力的。需要借助HBase中的高级语法——Filter来进行查询。Filter可以根据列簇、列、版本等条件来对数据进行过滤查询。因为在HBase中,主键、列、版本都是有序存储的,所以借助Filter,可以高效地完成查询。当执行Filter时,HBase会将Filter分发给各个HBase服务器节点来进行查询。
HBase中的过滤器也是基于Java开发的,只不过在Shell中,我们是使用基于JRuby的语法来实现的交互式查询。
在HBase的shell中,通过show_filters指令,可以查看到HBase中内置的一些过滤器
内置过滤器
hbase(main):079:0> show_filters
DependentColumnFilter # 允许用户指定一个参考列或引用列来过滤其他列的过滤器
KeyOnlyFilter # 只对单元格的键进行过滤和显示,不显示值
ColumnCountGetFilter # 限制每个逻辑行返回键值对的个数,在get方法中使用
SingleColumnValueFilter # 在指定的列蔟和列中进行比较的值过滤器
PrefixFilter # rowkey前缀过滤器
SingleColumnValueExcludeFilter # 排除匹配成功的值
FirstKeyOnlyFilter # 只扫描显示相同键的第一个单元格,其键值对会显示出来
ColumnRangeFilter # 过滤列名称的范围
ColumnValueFilter # 过滤列值的范围
TimestampsFilter # 时间戳过滤,支持等值,可以设置多个时间戳
FamilyFilter # 列簇过滤器
QualifierFilter # 列标识过滤器,只显示对应列名的数据
ColumnPrefixFilter # 对列名称的前缀进行过滤
RowFilter # 实现行键字符串的比较和过滤
MultipleColumnPrefixFilter # 可以指定多个前缀对列名称过滤
InclusiveStopFilter # 替代 ENDROW 返回终止条件行
PageFilter # 对显示结果按行进行分页显示
ValueFilter # 值过滤器,找到符合值条件的键值对
ColumnPaginationFilter # 对一行的所有列分页,只返回 [offset,offset+limit] 范围内的列
Took 0.0075 seconds
过滤器用法
scan ‘表名’, { Filter => "过滤器(比较运算符, ‘比较器表达式’)” }
比较器
比较器 | 描述 | 表达式语言缩写 |
---|---|---|
BinaryComparator | 匹配完整字节数组 | binary:值 |
BinaryPrefixComparator | 匹配字节数组前缀 | binaryprefix:值 |
BitComparator | 匹配比特位 | bit:值 |
NullComparator | 匹配空值 | null |
RegexStringComparator | 匹配正则表达式 | regexstring:正则表达式 |
SubstringComparator | 匹配子字符串 | substring:值 |
基本语法:比较器类型:比较器的值
需求1:使用RowFilter查询指定订单ID的数据
只查询订单的ID为:02602f66-adc7-40d4-8485-76b5632b5b53、订单状态以及支付方式
分析:
- 因为要订单ID就是ORDER_INFO表的rowkey,所以,我们应该使用rowkey过滤器来过滤
- 通过HBase的JAVA API,找到RowFilter构造器
- 参数1:op——比较运算符
- 参数2:rowComparator——比较器
所以构建该Filter的时候,只需要传入两个参数即可
hbase(main):081:0> scan 'ORDER_INFO', {
FILTER => "RowFilter(=,'binary:02602f66-adc7-40d4-8485-76b5632b5b53')",FORMATTER => 'toString'}
ROW COLUMN+CELL
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:CATEGORY, timestamp=1660617813649, value=手机;
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:OPERATION_DATE, timestamp=1660617813327, value=2020-04-25 12:09:16
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:PAYWAY, timestamp=1660617812628, value=1
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:PAY_MONEY, timestamp=1660617812242, value=4070
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:STATUS, timestamp=1660617811677, value=已提交
02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:USER_ID, timestamp=1660617812988, value=4944191
1 row(s)
Took 0.0139 seconds
hbase(main):082:0>
需求2:查询状态为「已付款」的订单
分析:
- 因为此处要指定列来进行查询,所以,我们不再使用rowkey过滤器,而是要使用列过滤器
- 我们要针对指定列和指定值进行过滤,比较适合使用SingleColumnValueFilter过滤器,查看JAVA API
- 参数1:列簇
- 参数2:列标识(列名)
- 比较运算符
- 比较器
注意:
- 列名STATUS的大小写一定要对!此处使用的是大写!
- 列名写错了查不出来数据,但HBase不会报错,因为HBase是无模式的
hbase(main):082:0> scan 'ORDER_INFO', {
FILTER => "SingleColumnValueFilter('C1', 'STATUS', =, 'binary:已付款')", FORMATTER => 'toString'}
ROW COLUMN+CELL
0e01edba-5e55-425e-837a-7efb91c56630 column=C1:CATEGORY, timestamp=1660617813657, value=男装;男鞋;
0e01edba-5e55-425e-837a-7efb91c56630 column=C1:OPERATION_DATE, timestamp=1660617813337, value=2020-04-25 12:09:44
0e01edba-5e55-425e-837a-7efb91c56630 column=C1:PAYWAY, timestamp=1660617812639, value=3
0e01edba-5e55-425e-837a-7efb91c56630 column=C1:PAY_MONEY, timestamp=1660617812254, value=6370
0e01edba-5e55-425e-837a-7efb91c56630 column=C1:STATUS, timestamp=1660617811748, value=已付款
...
...
...
需求3:查询支付方式为1,且金额大于3000的订单
分析:
- 此处需要使用多个过滤器共同来实现查询,多个过滤器,可以使用AND或者OR来组合多个过滤器完成查询
- 使用SingleColumnValueFilter实现对应列的查询
查询支付方式为1:SingleColumnValueFilter(‘C1’, ‘PAYWAY’, = , ‘binary:1’)
查询金额大于3000的订单:SingleColumnValueFilter(‘C1’, ‘PAY_MONEY’, > , ‘binary:3000’)
hbase(main):083:0> scan 'ORDER_INFO', {
FILTER => "SingleColumnValueFilter('C1', 'PAYWAY', = , 'binary:1') AND SingleColumnValueFilter('C1', 'PAY_MONEY', > , 'binary:3000')", FORMATTER => 'toString'}
注意:
- HBase shell中比较默认都是字符串比较,所以如果是比较数值类型的,会出现不准确的情况
- 例如:在字符串比较中4000是比100000大的
INCR
某新闻APP应用为了统计每个新闻的每隔一段时间的访问次数,他们将这些数据保存在HBase中
该表格数据如下所示:
新闻ID | 访问次数 | 时间段 | ROWKEY |
---|---|---|---|
0000000001 | 12 | 00:00-01:00 | 0000000001_00:00-01:00 |
0000000002 | 12 | 01:00-02:00 | 0000000002_01:00-02:00 |
要求:原子性增加新闻的访问次数值
incr可以实现对某个单元格的值进行原子性计数。
语法:incr ‘表名’,‘rowkey’,‘列蔟:列名’,累加值(默认累加1)
- 如果某一列要实现计数功能,必须要使用incr来创建对应的列
- 使用put创建的列是不能实现累加的
导入测试数据
下载地址:wget http://osswangting.oss-cn-shanghai.aliyuncs.com/hbase/NEWS_VISIT_CNT.txt
该脚本文件创建了一个表,名为NEWS_VISIT_CNT,列蔟为C1。并使用incr创建了若干个计数器,每个rowkey为:新闻的编号_时间段。CNT为count的缩写,表示访问的次数
wangting@ops01:/home/wangting/hbase >pwd
/home/wangting/hbase
wangting@ops01:/home/wangting/hbase >ll
total 60
-rw-r--r-- 1 wangting wangting 6595 Aug 5 16:34 NEWS_VISIT_CNT.txt
-rw-r--r-- 1 wangting wangting 49366 Aug 5 16:34 ORDER_INFO.txt
wangting@ops01:/home/wangting/hbase >hbase shell /home/wangting/hbase/NEWS_VISIT_CNT.txt
hbase(main):084:0> scan 'NEWS_VISIT_CNT', {
LIMIT => 3, FORMATTER => 'toString'}
ROW COLUMN+CELL
0000000001_00:00-01:00 column=C1:CNT, timestamp=1660639040544, value=
0000000001_00:00-01:00 column=C1:TIME_RANGE, timestamp=1660639040815, value=00:00-01:00
0000000002_01:00-02:00 column=C1:CNT, timestamp=1660639040575, value=
0000000002_01:00-02:00 column=C1:TIME_RANGE, timestamp=1660639040820, value=01:00-02:00
0000000003_02:00-03:00 column=C1:CNT, timestamp=1660639040581, value={
0000000003_02:00-03:00 column=C1:TIME_RANGE, timestamp=1660639040825, value=02:00-03:00
3 row(s)
Took 0.0215 seconds
hbase(main):085:0>
需求1:对0000000020新闻01:00 - 02:00访问计数+1
- 获取0000000020这条新闻在01:00-02:00当前的访问次数
hbase(main):085:0> get_counter 'NEWS_VISIT_CNT','0000000020_01:00-02:00','C1:CNT'
COUNTER VALUE = 6
Took 0.0101 seconds
- 使用incr进行累加
hbase(main):087:0> incr 'NEWS_VISIT_CNT','0000000020_01:00-02:00','C1:CNT'
COUNTER VALUE = 7
Took 0.0099 seconds
hbase(main):088:0>
- 再次查案新闻当前的访问次数
hbase(main):088:0> get_counter 'NEWS_VISIT_CNT','0000000020_01:00-02:00','C1:CNT'
COUNTER VALUE = 7
Took 0.0041 seconds
hbase(main):089:0>
shell管理操作
status
显示服务器状态信息
hbase(main):090:0> status
1 active master, 0 backup masters, 3 servers, 0 dead, 13.6667 average load
Took 0.1194 seconds
whoami
显示HBase当前用户
hbase(main):091:0> whoami
wangting (auth:SIMPLE)
groups: wangting
Took 0.0093 seconds
list
显示当前所有的表
hbase(main):092:0> list
TABLE
NEWS_VISIT_CNT
ORDER_INFO
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
8 row(s)
Took 0.0065 seconds
=> ["NEWS_VISIT_CNT", "ORDER_INFO", "SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS"]
count
统计指定表的记录数
hbase(main):093:0> count 'ORDER_INFO'
66 row(s)
Took 0.0241 seconds
=> 66
describe
展示表结构信息
hbase(main):094:0> describe 'ORDER_INFO'
Table ORDER_INFO is ENABLED
ORDER_INFO
COLUMN FAMILIES DESCRIPTION
{
NAME => 'C1', 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.0260 seconds
hbase(main):095:0>
exists
检查表是否存在,适用于表量特别多的情况
hbase(main):095:0> exists 'ORDER_INFO'
Table ORDER_INFO does exist
Took 0.0084 seconds
=> true
is_endbled、is_disabled
检查表是否启用或禁用
hbase(main):096:0> is_enabled 'ORDER_INFO'
true
Took 0.0080 seconds
=> true
hbase(main):097:0> is_disabled 'ORDER_INFO'
false
Took 0.0040 seconds
=> 1
alter
该命令可以改变表和列蔟的模式
# 创建测试USER_INFO表,列簇C1,C2
hbase(main):098:0> create 'USER_INFO', 'C1', 'C2'
Created table USER_INFO
Took 0.7598 seconds
=> Hbase::Table - USER_INFO
# 新增列簇C3
hbase(main):099:0> alter 'USER_INFO', 'C3'
Updating all regions with the new schema...
1/1 regions updated.
Done.
Took 2.2088 seconds
# 删除列簇C3
hbase(main):100:0> alter 'USER_INFO', 'delete' => 'C3'
Updating all regions with the new schema...
1/1 regions updated.
Done.
Took 2.1770 seconds
disable、enable
禁用一张表/启用一张表
hbase(main):101:0> disable 'USER_INFO'
Took 0.7453 seconds
hbase(main):102:0> enable 'USER_INFO'
Took 0.7531 seconds
drop
删除一张表,记得在删除表之前必须先禁用
truncate
清空表的数据,相当于禁用表-删除表-创建表
HBase_java_api编程
需求与数据集
某某自来水公司,需要存储大量的缴费明细数据。以下截取了缴费明细的一部分内容
用户id | 姓名 | 用户地址 | 性别 | 缴费时间 | 表示数(本次) | 表示数(上次) | 用量(立方) | 合计金额 | 查表日期 | 最迟缴费日期 |
---|---|---|---|---|---|---|---|---|---|---|
4944191 | 登卫红 | 贵州省铜仁市德江县7单元267室 | 男 | 2020-05-10 | 308.1 | 283.1 | 25 | 150 | 2020-04-25 | 2020-06-09 |
因为缴费明细的数据记录非常庞大,该公司的信息部门决定使用HBase来存储这些数据。并且,他们希望能够通过Java程序来访问这些数据
实验环境准备
创建maven工程
通过idea工具创建maven项目(idea开发工具自行准备)
groupId | cn.wangt |
---|---|
artifactId | hbase_op |
导入pom依赖
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.wangt</groupId>
<artifactId>hbase_op</artifactId>
<version>1.0-SNAPSHOT</version>
<repositories><!-- 代码库 -->
<repository>
<id>aliyun</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<target>1.8</target>
<source>1.8</source>
</configuration>
</plugin>
</plugins>
</build>
</project>
右边打开maven工具栏,对项目进行test测试,会去拉取依赖包
复制HBase和Hadoop配置文件
将以下三个配置文件复制到创建的maven项目中resource目录中
- hbase-site.xml (hbase/conf)
- core-site.xml (hadoop-3.1.3/etc/hadoop)
- log4j.properties (hbase/conf)
项目创建包结构和类
-
在项目test你也可以创建cn.wangt.hbase.admin.api_test 包结构
-
创建TableAmdinTest类
创建Hbase连接以及admin管理对象
要操作Hbase也需要建立Hbase的连接。此处我们仍然使用TestNG来编写测试。使用@BeforeTest初始化HBase连接,创建admin对象、@AfterTest关闭连接
实现步骤:
- 使用HbaseConfiguration.create()创建Hbase配置
- 使用ConnectionFactory.createConnection()创建Hbase连接
- 要创建表,需要基于Hbase连接获取admin管理对象
- 使用admin.close、connection.close关闭连接
package cn.wangt.hbase.admin.api_test;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.*;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import java.io.IOException;
public class TableAmdinTest {
private Connection connection;
private Admin admin;
@BeforeTest
public void beforeTest() throws IOException {
// 1.使用HbaseConfiguration.create()创建Hbase配置
Configuration configuration = HBaseConfiguration.create();
// 2. 使用ConnectionFactory.createConnection()创建Hbase连接
connection = ConnectionFactory.createConnection(configuration);
// 3. 要创建表,需要基于Hbase连接获取admin管理对象
// 要创建表、删除表需要和HMaster连接,所以需要有一个admin对象
admin = connection.getAdmin();
}
@AfterTest
public void afterTest() throws IOException {
// 4. 使用admin.close、connection.close关闭连接
admin.close();
connection.close();
}
}
需求1:使用java代码创建表
创建一个名为WATER_BILL的表,包含一个列蔟C1
实现步骤:
- 判断表是否存在
a) 存在,则退出 - 使用TableDescriptorBuilder.newBuilder构建表描述构建器
- 使用ColumnFamilyDescriptorBuilder.newBuilder构建列蔟描述构建器
- 构建列蔟描述,构建表描述
- 创建表
当前的hbase表list信息:
hbase(main):103:0> list
TABLE
NEWS_VISIT_CNT
ORDER_INFO
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
USER_INFO
9 row(s)
Took 0.0079 seconds
=> ["NEWS_VISIT_CNT", "ORDER_INFO", "SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS", "USER_INFO"]
建表代码
package cn.wangt.hbase.admin.api_test;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import java.io.IOException;
public class TableAmdinTest {
private Connection connection;
private Admin admin;
@BeforeTest
public void beforeTest() throws IOException {
// 1.使用HbaseConfiguration.create()创建Hbase配置
Configuration configuration = HBaseConfiguration.create();
// 2. 使用ConnectionFactory.createConnection()创建Hbase连接
connection = ConnectionFactory.createConnection(configuration);
// 3. 要创建表,需要基于Hbase连接获取admin管理对象
// 要创建表、删除表需要和HMaster连接,所以需要有一个admin对象
admin = connection.getAdmin();
}
@Test
public void createTableTest() throws IOException {
// 表名
String TABLE_NAME = "WATER_BILL";
// 列簇名
String COLUMN_FAMILY = "C1";
// 1.判断表是否存在
if(admin.tableExists(TableName.valueOf(TABLE_NAME))){
return;
}
// 2.构建表描述构建器
TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(TableName.valueOf(TABLE_NAME));
// 3.构建列簇描述构建器
ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(COLUMN_FAMILY));
// 4.构建列簇描述
ColumnFamilyDescriptor columnFamilyDescriptor = columnFamilyDescriptorBuilder.build();
// 5.构建表描述 添加列簇
tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
TableDescriptor tableDescriptor = tableDescriptorBuilder.build();
// 6.创建表
admin.createTable(tableDescriptor);
}
@AfterTest
public void afterTest() throws IOException {
// 4. 使用admin.close、connection.close关闭连接
admin.close();
connection.close();
}
}
再次查看hbase表list信息:
hbase(main):105:0> list
TABLE
NEWS_VISIT_CNT
ORDER_INFO
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
USER_INFO
WATER_BILL
10 row(s)
Took 0.0038 seconds
=> ["NEWS_VISIT_CNT", "ORDER_INFO", "SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS", "USER_INFO", "WATER_BILL"]
可以看到WATER_BILL表创建成功
需求2:向表中插入数据
-
在 test 目录中创建 cn.wangt.hbase.data.api_test 包
-
创建DataOpTest类
在表中插入一个行,该行只包含一个列
ROWKEY | 姓名(列名:NAME) |
---|---|
4944191 | 登卫红 |
实现步骤:
1.使用Hbase连接获取Htable
2.构建ROWKEY、列蔟名、列名
3.构建Put对象(对应put命令)
4.添加姓名列
5.使用Htable表对象执行put操作
6.关闭Htable表对象
代码:
package cn.wangt.hbase.data.api_test;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import java.io.IOException;
public class DataOpTest {
private Configuration configuration;
private Connection connection;
@BeforeTest
public void beforeTest() throws IOException {
configuration = HBaseConfiguration.create();
connection = ConnectionFactory.createConnection(configuration);
}
@Test
public void addTest() throws IOException {
// 1.使用Hbase连接获取Htable
TableName waterBillTableName = TableName.valueOf("WATER_BILL");
Table waterBillTable = connection.getTable(waterBillTableName);
// 2.构建ROWKEY、列蔟名、列名
String rowkey = "4944191";
String cfName = "C1";
String colName = "NAME";
// 3.构建Put对象(对应put命令)
Put put = new Put(Bytes.toBytes(rowkey));
// 4.添加姓名列
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colName)
, Bytes.toBytes("登卫红"));
// 5.使用Htable表对象执行put操作
waterBillTable.put(put);
// 6. 关闭表
waterBillTable.close();
}
@AfterTest
public void afterTest() throws IOException {
connection.close();
}
}
查询验证:
hbase(main):106:0> get 'WATER_BILL','4944191',{
FORMATTER => 'toString'}
COLUMN CELL
C1:NAME timestamp=1660704584510, value=登卫红
1 row(s)
Took 0.0118 seconds
hbase(main):107:0>
继续尝试插入其他列数据
列名 | 说明 | 值 |
---|---|---|
ADDRESS | 用户地址 | 贵州省铜仁市德江县7单元267室 |
SEX | 性别 | 男 |
PAY_DATE | 缴费时间 | 2020-05-10 |
NUM_CURRENT | 表示数(本次) | 308.1 |
NUM_PREVIOUS | 表示数(上次) | 283.1 |
NUM_USAGE | 用量(立方) | 25 |
TOTAL_MONEY | 合计金额 | 150 |
RECORD_DATE | 查表日期 | 2020-04-25 |
LATEST_DATE | 最迟缴费日期 | 2020-06-09 |
代码:
package cn.wangt.hbase.data.api_test;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import java.io.IOException;
public class DataOpTest {
private Configuration configuration;
private Connection connection;
@BeforeTest
public void beforeTest() throws IOException {
configuration = HBaseConfiguration.create();
connection = ConnectionFactory.createConnection(configuration);
}
@Test
public void addTest() throws IOException {
// 1.使用Hbase连接获取Htable
TableName waterBillTableName = TableName.valueOf("WATER_BILL");
Table waterBillTable = connection.getTable(waterBillTableName);
// 2.构建ROWKEY、列蔟名、列名
String rowkey = "4944191";
String cfName = "C1";
String colName = "NAME";
String colADDRESS = "ADDRESS";
String colSEX = "SEX";
String colPAY_DATE = "PAY_DATE";
String colNUM_CURRENT = "NUM_CURRENT";
String colNUM_PREVIOUS = "NUM_PREVIOUS";
String colNUM_USAGE = "NUM_USAGE";
String colTOTAL_MONEY = "TOTAL_MONEY";
String colRECORD_DATE = "RECORD_DATE";
String colLATEST_DATE = "LATEST_DATE";
// 3.构建Put对象(对应put命令)
Put put = new Put(Bytes.toBytes(rowkey));
// 4.添加姓名列
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colName)
, Bytes.toBytes("登卫红"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colADDRESS)
, Bytes.toBytes("贵州省铜仁市德江县7单元267室"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colSEX)
, Bytes.toBytes("男"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colPAY_DATE)
, Bytes.toBytes("2020-05-10"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colNUM_CURRENT)
, Bytes.toBytes("308.1"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colNUM_PREVIOUS)
, Bytes.toBytes("283.1"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colNUM_USAGE)
, Bytes.toBytes("25"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colTOTAL_MONEY)
, Bytes.toBytes("150"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colRECORD_DATE)
, Bytes.toBytes("2020-04-25"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colLATEST_DATE)
, Bytes.toBytes("2020-06-09"));
// 5.使用Htable表对象执行put操作
waterBillTable.put(put);
// 6. 关闭表
waterBillTable.close();
}
@AfterTest
public void afterTest() throws IOException {
connection.close();
}
}
查询验证:
hbase(main):108:0> get 'WATER_BILL','4944191',{
FORMATTER => 'toString'}
COLUMN CELL
C1:ADDRESS timestamp=1660705352480, value=贵州省铜仁市德江县7单元267室
C1:LATEST_DATE timestamp=1660705352480, value=2020-06-09
C1:NAME timestamp=1660705352480, value=登卫红
C1:NUM_CURRENT timestamp=1660705352480, value=308.1
C1:NUM_PREVIOUS timestamp=1660705352480, value=283.1
C1:NUM_USAGE timestamp=1660705352480, value=25
C1:PAY_DATE timestamp=1660705352480, value=2020-05-10
C1:RECORD_DATE timestamp=1660705352480, value=2020-04-25
C1:SEX timestamp=1660705352480, value=男
C1:TOTAL_MONEY timestamp=1660705352480, value=150
1 row(s)
Took 0.0122 seconds
需求3:表中查看1条数据
查询rowkey为4944191的所有列的数据,并打印出来
实现步骤:
- 获取HTable
- 使用rowkey构建Get对象
- 执行get请求
- 获取所有单元格
- 打印rowkey
- 迭代单元格列表
- 关闭表
代码:
注意:截至目前代码均是类的完整代码,实际只是在原有代码中增加了get方法,运行时只需要单独运行get即可
后续代码至附上相关代码段
package cn.wangt.hbase.data.api_test;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import java.io.IOException;
import java.util.List;
public class DataOpTest {
private Configuration configuration;
private Connection connection;
@BeforeTest
public void beforeTest() throws IOException {
configuration = HBaseConfiguration.create();
connection = ConnectionFactory.createConnection(configuration);
}
@Test
public void addTest() throws IOException {
// 1.使用Hbase连接获取Htable
TableName waterBillTableName = TableName.valueOf("WATER_BILL");
Table waterBillTable = connection.getTable(waterBillTableName);
// 2.构建ROWKEY、列蔟名、列名
String rowkey = "4944191";
String cfName = "C1";
String colName = "NAME";
String colADDRESS = "ADDRESS";
String colSEX = "SEX";
String colPAY_DATE = "PAY_DATE";
String colNUM_CURRENT = "NUM_CURRENT";
String colNUM_PREVIOUS = "NUM_PREVIOUS";
String colNUM_USAGE = "NUM_USAGE";
String colTOTAL_MONEY = "TOTAL_MONEY";
String colRECORD_DATE = "RECORD_DATE";
String colLATEST_DATE = "LATEST_DATE";
// 3.构建Put对象(对应put命令)
Put put = new Put(Bytes.toBytes(rowkey));
// 4.添加姓名列
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colName)
, Bytes.toBytes("登卫红"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colADDRESS)
, Bytes.toBytes("贵州省铜仁市德江县7单元267室"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colSEX)
, Bytes.toBytes("男"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colPAY_DATE)
, Bytes.toBytes("2020-05-10"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colNUM_CURRENT)
, Bytes.toBytes("308.1"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colNUM_PREVIOUS)
, Bytes.toBytes("283.1"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colNUM_USAGE)
, Bytes.toBytes("25"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colTOTAL_MONEY)
, Bytes.toBytes("150"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colRECORD_DATE)
, Bytes.toBytes("2020-04-25"));
put.addColumn(Bytes.toBytes(cfName)
, Bytes.toBytes(colLATEST_DATE)
, Bytes.toBytes("2020-06-09"));
// 5.使用Htable表对象执行put操作
waterBillTable.put(put);
// 6. 关闭表
waterBillTable.close();
}
@Test
public void getOneTest() throws IOException {
// 1. 获取HTable
TableName waterBillTableName = TableName.valueOf("WATER_BILL");
Table waterBilltable = connection.getTable(waterBillTableName);
// 2. 使用rowkey构建Get对象
Get get = new Get(Bytes.toBytes("4944191"));
// 3. 执行get请求
Result result = waterBilltable.get(get);
// 4. 获取所有单元格
List<Cell> cellList = result.listCells();
// 打印rowkey
System.out.println("rowkey => " + Bytes.toString(result.getRow()));
// 5. 迭代单元格列表
for (Cell cell : cellList) {
// 打印列蔟名
System.out.print(Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()));
System.out.println(" => " + Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
}
// 6.关闭表
waterBillTable.close();
}
@AfterTest
public void afterTest() throws IOException {
connection.close();
}
}
run运行getOneTest查看 idea控制台输出:
2022-08-17 11:13:15,559 INFO [ReadOnlyZKClient-ops01:2181,ops02:2181,ops03:2181@0x15eb5ee5-SendThread(ops03:2181)] zookeeper.ClientCnxn: Socket connection established to ops03/11.8.36.76:2181, initiating session
2022-08-17 11:13:15,590 INFO [ReadOnlyZKClient-ops01:2181,ops02:2181,ops03:2181@0x15eb5ee5-SendThread(ops03:2181)] zookeeper.ClientCnxn: Session establishment complete on server ops03/11.8.36.76:2181, sessionid = 0x30a8f7122b00027, negotiated timeout = 40000
rowkey => 4944191
ADDRESS => 贵州省铜仁市德江县7单元267室
LATEST_DATE => 2020-06-09
NAME => 登卫红
NUM_CURRENT => 308.1
NUM_PREVIOUS => 283.1
NUM_USAGE => 25
PAY_DATE => 2020-05-10
RECORD_DATE => 2020-04-25
SEX => 男
TOTAL_MONEY => 150
2022-08-17 11:13:17,012 INFO [main] zookeeper.ReadOnlyZKClient: Close zookeeper connection 0x15eb5ee5 to ops01:2181,ops02:2181,ops03:2181
===============================================
Default Suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================
2022-08-17 11:13:17,028 INFO [ReadOnlyZKClient-ops01:2181,ops02:2181,ops03:2181@0x15eb5ee5] zookeeper.ZooKeeper: Session: 0x30a8f7122b00027 closed
Process finished with exit code 0
需求4:删除一条数据
删除rowkey为4944191的整条数据
实现步骤:
1.获取HTable对象
2.根据rowkey构建delete对象
3.执行delete请求
4.关闭表
相关代码块:
@Test
public void deleteOneTest() throws IOException {
// 1. 获取HTable对象
Table waterBillTable = connection.getTable(TableName.valueOf("WATER_BILL"));
// 2. 根据rowkey构建delete对象
Delete delete = new Delete(Bytes.toBytes("4944191"));
// 3. 执行delete请求
waterBillTable.delete(delete);
// 4. 关闭表
waterBillTable.close();
}
复核验证
hbase(main):110:0> get 'WATER_BILL','4944191',{
FORMATTER => 'toString'}
COLUMN CELL
0 row(s)
Took 0.0053 seconds
hbase(main):111:0>
需求5:导入数据&导出数据
数据导入
需求将一份数据文件导入HBase中
实验数据文件:http://osswangting.oss-cn-shanghai.aliyuncs.com/hbase/part-m-00000_10w
在HBase中,有一个Import的MapReduce作业,可以专门用来将数据文件导入到HBase中
使用方式:hbase org.apache.hadoop.hbase.mapreduce.Import 表名 HDFS数据文件路径
- 将资料中数据文件上传到Linux中或wget下载
- 将文件上传到hdfs中
# 下载数据文件
wangting@ops01:/home/wangting/hbase >wget http://osswangting.oss-cn-shanghai.aliyuncs.com/hbase/part-m-00000_10w
# 查看
wangting@ops01:/home/wangting/hbase >ll
total 50340
-rw-r--r-- 1 wangting wangting 6595 Aug 5 16:34 NEWS_VISIT_CNT.txt
-rw-r--r-- 1 wangting wangting 49366 Aug 5 16:34 ORDER_INFO.txt
-rw-r--r-- 1 wangting wangting 51483241 Aug 5 16:32 part-m-00000_10w
# 创建hdfs目录
wangting@ops01:/home/wangting/hbase >hadoop fs -mkdir -p /water_bill/output_ept_10W
2022-08-17 11:39:05,123 INFO [main] Configuration.deprecation (Configuration.java:logDeprecation(1395)) - No unit for dfs.client.datanode-restart.timeout(30) assuming SECONDS
# 文件上传至hdfs
wangting@ops01:/home/wangting/hbase >hadoop fs -put part-m-00000_10w /water_bill/output_ept_10W
2022-08-17 11:39:17,432 INFO [main] Configuration.deprecation (Configuration.java:logDeprecation(1395)) - No unit for dfs.client.datanode-restart.timeout(30) assuming SECONDS
2022-08-17 11:39:18,194 INFO [Thread-7] sasl.SaslDataTransferClient (SaslDataTransferClient.java:checkTrustAndSend(239)) - SASL encryption trust check: localHostTrusted = false, remoteHostTrusted = false
# 查看上传结果
wangting@ops01:/home/wangting/hbase >hadoop dfs -ls /water_bill/output_ept_10W
WARNING: Use of this script to execute dfs is deprecated.
WARNING: Attempting to execute replacement "hdfs dfs" instead.
2022-08-17 11:39:44,182 INFO [main] Configuration.deprecation (Configuration.java:logDeprecation(1395)) - No unit for dfs.client.datanode-restart.timeout(30) assuming SECONDS
Found 1 items
-rw-r--r-- 3 wangting supergroup 51483241 2022-08-17 11:39 /water_bill/output_ept_10W/part-m-00000_10w
wangting@ops01:/home/wangting/hbase >
当前相当于HBase有一张表water_bill,但暂时未和hdfs上数据关联,还需要导入hbase
wangting@ops01:/home/wangting/hbase >hbase org.apache.hadoop.hbase.mapreduce.Import WATER_BILL /water_bill/output_ept_10W
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
2022-08-17 11:44:48,894 INFO [main] mapreduce.Import: writing directly to table from Mapper.
2022-08-17 11:44:49,100 INFO [main] client.RMProxy: Connecting to ResourceManager at ops02/11.8.36.63:8032
...
...
2022-08-17 11:45:00,711 INFO [main] mapreduce.Job: map 0% reduce 0%
2022-08-17 11:45:13,801 INFO [main] mapreduce.Job: map 100% reduce 0%
2022-08-17 11:45:14,814 INFO [main] mapreduce.Job: Job job_1615531413182_9573 completed successfully
2022-08-17 11:45:14,901 WARN [main] counters.FrameworkCounterGroup: MAP_PHYSICAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:45:14,902 WARN [main] counters.FrameworkCounterGroup: MAP_VIRTUAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:45:14,911 INFO [main] mapreduce.Job: operations=0
HDFS: Number of bytes read=51483366
HDFS: Number of bytes written=0
HDFS: Number of read operations=3
HDFS: Number of large read operations=0
HDFS: Number of write operations=0
Job Counters
Launched map tasks=1
Data-local map tasks=1
Total time spent by all maps in occupied slots (ms)=11095
Total time spent by all reduces in occupied slots (ms)=0
Total time spent by all map tasks (ms)=11095
Total vcore-milliseconds taken by all map tasks=11095
Total megabyte-milliseconds taken by all map tasks=11361280
Map-Reduce Framework
Map input records=99505
Map output records=99505
Input split bytes=125
Spilled Records=0
Failed Shuffles=0
Merged Map outputs=0
GC time elapsed (ms)=172
CPU time spent (ms)=12840
Physical memory (bytes) snapshot=272273408
Virtual memory (bytes) snapshot=2012495872
Total committed heap usage (bytes)=170393600
File Input Format Counters
Bytes Read=51483241
File Output Format Counters
Bytes Written=0
2022-08-17 11:45:14,917 WARN [main] counters.FrameworkCounterGroup: MAP_PHYSICAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:45:14,917 WARN [main] counters.FrameworkCounterGroup: MAP_VIRTUAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:45:14,921 WARN [main] counters.FrameworkCounterGroup: MAP_PHYSICAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:45:14,921 WARN [main] counters.FrameworkCounterGroup: MAP_VIRTUAL_MEMORY_BYTES_MAX is not a recognized counter.
wangting@ops01:/home/wangting/hbase >
数据导出
# 查看hdfs的/water_bill/目录
wangting@ops01:/home/wangting/hbase >hadoop dfs -ls /water_bill/
WARNING: Use of this script to execute dfs is deprecated.
WARNING: Attempting to execute replacement "hdfs dfs" instead.
2022-08-17 11:54:49,211 INFO [main] Configuration.deprecation (Configuration.java:logDeprecation(1395)) - No unit for dfs.client.datanode-restart.timeout(30) assuming SECONDS
Found 1 items
drwxr-xr-x - wangting supergroup 0 2022-08-17 11:39 /water_bill/output_ept_10W
wangting@ops01:/home/wangting/hbase >
# 数据导出
wangting@ops01:/home/wangting/hbase >hbase org.apache.hadoop.hbase.mapreduce.Export WATER_BILL /water_bill/output_ept_10W_export_0817
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.htmlmultiple_bindings for an explanation.
...
...
2022-08-17 11:55:09,033 INFO [main] mapreduce.Job: map 0% reduce 0%
2022-08-17 11:55:19,115 INFO [main] mapreduce.Job: map 100% reduce 0%
2022-08-17 11:55:20,128 INFO [main] mapreduce.Job: Job job_1615531413182_9574 completed successfully
2022-08-17 11:55:20,214 WARN [main] counters.FrameworkCounterGroup: MAP_PHYSICAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:55:20,214 WARN [main] counters.FrameworkCounterGroup: MAP_VIRTUAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:55:20,224 INFO [main] mapreduce.Job: Total time spent by all reduces in occupied slots (ms)=0
Total time spent by all map tasks (ms)=8586
Total vcore-milliseconds taken by all map tasks=8586
Total megabyte-milliseconds taken by all map tasks=8792064
Map-Reduce Framework
Map input records=99505
Map output records=99505
Input split bytes=136
Spilled Records=0
Failed Shuffles=0
Merged Map outputs=0
GC time elapsed (ms)=149
CPU time spent (ms)=10410
Physical memory (bytes) snapshot=287027200
Virtual memory (bytes) snapshot=2046070784
Total committed heap usage (bytes)=186122240
HBase Counters
BYTES_IN_REMOTE_RESULTS=53960732
BYTES_IN_RESULTS=53960732
MILLIS_BETWEEN_NEXTS=3697
NOT_SERVING_REGION_EXCEPTION=0
NUM_SCANNER_RESTARTS=0
NUM_SCAN_RESULTS_STALE=0
REGIONS_SCANNED=1
REMOTE_RPC_CALLS=996
REMOTE_RPC_RETRIES=0
ROWS_FILTERED=1
ROWS_SCANNED=99506
RPC_CALLS=996
RPC_RETRIES=0
File Input Format Counters
Bytes Read=0
File Output Format Counters
Bytes Written=51483241
# 查看验证hdfs已经成功导出一份output_ept_10W_export_0817数据
wangting@ops01:/home/wangting/hbase >hadoop dfs -ls /water_bill/
WARNING: Use of this script to execute dfs is deprecated.
WARNING: Attempting to execute replacement "hdfs dfs" instead.
2022-08-17 11:56:30,957 INFO [main] Configuration.deprecation (Configuration.java:logDeprecation(1395)) - No unit for dfs.client.datanode-restart.timeout(30) assuming SECONDS
Found 2 items
drwxr-xr-x - wangting supergroup 0 2022-08-17 11:39 /water_bill/output_ept_10W
drwxr-xr-x - wangting supergroup 0 2022-08-17 11:55 /water_bill/output_ept_10W_export_0817
wangting@ops01:/home/wangting/hbase >
需求6:代码实现条件查询
查询2020年6月份所有用户的用水量
在Java API中,我们也是使用scan + filter来实现过滤查询。2020年6月份其实就是从2020年6月1日到2020年6月30日的所有抄表数据
-
在cn.wangt.hbase.data.api_test包下创建ScanFilterTest类
-
使用@BeforeTest、@AfterTest构建HBase连接、以及关闭HBase连接
实现步骤:
- 获取表
- 构建scan请求对象
- 构建两个过滤器
a) 构建两个日期范围过滤器(注意此处请使用RECORD_DATE——抄表日期比较
b) 构建过滤器列表 - 执行scan扫描请求
- 迭代打印result
- 迭代单元格列表
- 关闭ResultScanner(这玩意把转换成一个个的类似get的操作,注意要关闭释放资源)
- 关闭表
代码实现
package cn.wangt.hbase.data.api_test;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.BinaryComparator;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
public class ScanFilterTest {
private Connection connection;
private TableName TABLE_NAME = TableName.valueOf("WATER_BILL");
@BeforeTest
public void beforeTest() throws IOException {
Configuration configuration = HBaseConfiguration.create();
connection = ConnectionFactory.createConnection(configuration);
}
// 查询2020年6月份所有用户的用水量数据
@Test
public void scanFilterTest() throws IOException {
// 1. 获取表
Table table = connection.getTable(TABLE_NAME);
// 2. 构建scan请求对象
Scan scan = new Scan();
// 3. 构建两个过滤器
// a) 构建两个日期范围过滤器(注意此处请使用RECORD_DATE——抄表日期比较
SingleColumnValueFilter startFilter = new SingleColumnValueFilter(Bytes.toBytes("C1")
, Bytes.toBytes("RECORD_DATE")
, CompareOperator.GREATER_OR_EQUAL
, new BinaryComparator(Bytes.toBytes("2020-06-01")));
SingleColumnValueFilter endFilter = new SingleColumnValueFilter(Bytes.toBytes("C1")
, Bytes.toBytes("RECORD_DATE")
, CompareOperator.LESS_OR_EQUAL
, new BinaryComparator(Bytes.toBytes("2020-06-30")));
// b) 构建过滤器列表
FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL, startFilter, endFilter);
// 4. 执行scan扫描请求
scan.setFilter(filterList);
ResultScanner resultScanner = table.getScanner(scan);
Iterator<Result> iterator = resultScanner.iterator();
// 5. 迭代打印result
while (iterator.hasNext()) {
Result result = iterator.next();
// 列出所有的单元格
List<Cell> cellList = result.listCells();
// 5. 打印rowkey
byte[] rowkey = result.getRow();
System.out.println(Bytes.toString(rowkey));
// 6. 迭代单元格列表
for (Cell cell : cellList) {
// 将字节数组转换为字符串
// 获取列蔟的名称
String cf = Bytes.toString(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());
// 获取列的名称
String columnName = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());
String value = "";
// 解决乱码问题:
// 思路:
// 如果某个列是以下列中的其中一个,调用toDouble将它认为是一个数值来转换
//1. NUM_CURRENT
//2. NUM_PREVIOUS
//3. NUM_USAGE
//4. TOTAL_MONEY
if (columnName.equals("NUM_CURRENT")
|| columnName.equals("NUM_PREVIOUS")
|| columnName.equals("NUM_USAGE")
|| columnName.equals("TOTAL_MONEY")) {
value = Bytes.toDouble(cell.getValueArray()) + "";
} else {
// 获取值
value = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
}
System.out.println(cf + ":" + columnName + " -> " + value);
}
}
// 7. 关闭ResultScanner(这玩意把转换成一个个的类似get的操作,注意要关闭释放资源)
resultScanner.close();
// 8. 关闭表
table.close();
}
@AfterTest
public void afterTest() throws IOException {
connection.close();
}
}
需求7:使用Java代码删除表
创建一个测试表
hbase(main):114:0> create 'TEST_FOR_DROP','C1'
Created table TEST_FOR_DROP
Took 0.7583 seconds
=> Hbase::Table - TEST_FOR_DROP
hbase(main):115:0> list
TABLE
NEWS_VISIT_CNT
ORDER_INFO
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
TEST_FOR_DROP
USER_INFO
WATER_BILL
11 row(s)
Took 0.0078 seconds
=> ["NEWS_VISIT_CNT", "ORDER_INFO", "SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS", "TEST_FOR_DROP", "USER_INFO", "WATER_BILL"]
hbase(main):116:0>
实现步骤:
- 判断表是否存在
- 如果存在,则禁用表
- 再删除表
在admin.api_test包下的,TableAmdinTest类中增加一个dropTable方法
相关代码块
@Test
public void dropTable() throws IOException {
// 表名
TableName tableName = TableName.valueOf("TEST_FOR_DROP");
// 1. 判断表是否存在
if(admin.tableExists(tableName)) {
// 2. 禁用表
admin.disableTable(tableName);
// 3. 删除表
admin.deleteTable(tableName);
}
}
命令行查看验证发现TEST_FOR_DROP表已经不存在
hbase(main):117:0> list
TABLE
NEWS_VISIT_CNT
ORDER_INFO
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
USER_INFO
WATER_BILL
10 row(s)
Took 0.0070 seconds
=> ["NEWS_VISIT_CNT", "ORDER_INFO", "SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS", "USER_INFO", "WATER_BILL"]
hbase(main):118:0>
HBase高可用
考虑关于HBase集群的一个问题,在当前的HBase集群中,只有一个Master,一旦Master出现故障,将会导致HBase不再可用。所以,在实际的生产环境中,是非常有必要搭建一个高可用的HBase集群的
HBase高可用介绍
HBase的高可用配置其实就是HMaster的高可用。要搭建HBase的高可用,只需要再选择一个节点作为HMaster,在HBase的conf目录下创建文件backup-masters,然后再backup-masters添加备份Master的记录。一条记录代表一个backup master,可以在文件配置多个记录
当前环境查看
wangting@ops01:/home/wangting >for i in ops01 ops02 ops03;do echo "=== $i ===" && ssh $i jps -l | grep hbase;done
=== ops01 ===
16029 org.apache.hadoop.hbase.regionserver.HRegionServer
15806 org.apache.hadoop.hbase.master.HMaster
=== ops02 ===
60896 org.apache.hadoop.hbase.regionserver.HRegionServer
=== ops03 ===
80496 org.apache.hadoop.hbase.regionserver.HRegionServer
wangting@ops01:/home/wangting >
11.8.37.50 ops01 # HMaster
11.8.36.63 ops02
11.8.36.76 ops03
hbase(main):118:0> status
1 active master, 0 backup masters, 3 servers, 0 dead, 14.3333 average load
Took 0.0117 seconds
hbase(main):119:0>
搭建部署HBase高可用落地
# 在hbase的conf文件夹中创建 backup-masters 文件
wangting@ops01:/home/wangting >cd /opt/module/hbase/conf/
# 将另2台服务器hosts添加至文件
wangting@ops01:/opt/module/hbase/conf >vim backup-masters
ops02
ops03
#分发同步文件
wangting@ops01:/opt/module/hbase/conf >scp backup-masters ops02:$PWD
wangting@ops01:/opt/module/hbase/conf >scp backup-masters ops03:$PWD
# 重启服务
wangting@ops01:/opt/module/hbase/bin >stop-hbase.sh
wangting@ops01:/opt/module/hbase/bin >start-hbase.sh
# 再次查看应用相关信息
wangting@ops01:/opt/module/hbase/bin >for i in ops01 ops02 ops03;do echo "=== $i ===" && ssh $i jps -l | grep hbase;done
=== ops01 ===
112651 org.apache.hadoop.hbase.master.HMaster
112874 org.apache.hadoop.hbase.regionserver.HRegionServer
=== ops02 ===
117251 org.apache.hadoop.hbase.regionserver.HRegionServer
117339 org.apache.hadoop.hbase.master.HMaster
=== ops03 ===
64828 org.apache.hadoop.hbase.master.HMaster
64733 org.apache.hadoop.hbase.regionserver.HRegionServer
状态查看
hbase(main):119:0> status
1 active master, 2 backup masters, 3 servers, 0 dead, 14.3333 average load
Took 0.0641 seconds
hbase(main):120:0>
通过zk去查看hbase集群信息
wangting@ops01:/home/wangting >zkCli.sh
Connecting to localhost:2181
2022-08-17 16:44:34,063 [myid:] - INFO [main:Environment@109] - Client environment:zookeeper.version=3.5.7-f0fdd52973d373ffd9c86b81d99842dc2c7f660e, built on 02/10/2020 11:30 GMT
2022-08-17 16:44:34,067 [myid:] - INFO [main:Environment@109] - Client environment:host.name=ops01
[zk: localhost:2181(CONNECTED) 0]
[zk: localhost:2181(CONNECTED) 1]
# 通过get /hbase/master看到hbase的master是ops01
[zk: localhost:2181(CONNECTED) 2] get /hbase/master
�master:16000D��PBUF
ops01�}���ת0�}
# 通过ls /hbase/backup-masters看到backup-masters为ops02和ops03
[zk: localhost:2181(CONNECTED) 3] ls /hbase/backup-masters
[ops02,16000,1660725472902, ops03,16000,1660725472775]
[zk: localhost:2181(CONNECTED) 4]
验证HBase高可用
# 查看当前ops01上hbase进程
wangting@ops01:/home/wangting >jps -l | grep hbase
112651 org.apache.hadoop.hbase.master.HMaster
112874 org.apache.hadoop.hbase.regionserver.HRegionServer
# 杀掉master的进程
wangting@ops01:/home/wangting >kill -9 112651
wangting@ops01:/home/wangting >
# 查看hbase集群服务信息
wangting@ops01:/home/wangting >for i in ops01 ops02 ops03;do echo "=== $i ===" && ssh $i jps -l | grep hbase;done
=== ops01 ===
112874 org.apache.hadoop.hbase.regionserver.HRegionServer
=== ops02 ===
117251 org.apache.hadoop.hbase.regionserver.HRegionServer
117339 org.apache.hadoop.hbase.master.HMaster
=== ops03 ===
64828 org.apache.hadoop.hbase.master.HMaster
64733 org.apache.hadoop.hbase.regionserver.HRegionServer
wangting@ops01:/home/wangting >
# 通过zookeeper查看集群角色信息
wangting@ops01:/home/wangting >zkCli.sh
Connecting to localhost:2181
2022-08-17 16:58:25,940 [myid:] - INFO [main:Environment@109] - Client environment:zookeeper.version=3.5.7-f0fdd52973d373ffd9c86b81d99842dc2c7f660e, built on 02/10/2020 11:30 GMT
2022-08-17 16:58:25,944 [myid:] - INFO [main:Environment@109] - Client environment:host.name=ops01
WatchedEvent state:SyncConnected type:None path:null
# 可以看到杀掉ops01的master进程后,ops03成为master
[zk: localhost:2181(CONNECTED) 0] get /hbase/master
�master:16000��� L\�BPBUF
ops03�}���ת0�}
# backup-masters为ops02
[zk: localhost:2181(CONNECTED) 1] ls /hbase/backup-masters
[ops02,16000,1660725472902]
[zk: localhost:2181(CONNECTED) 2]
至此,hbase自动实现了master的角色切换
HBase架构
系统架构图
角色-Client
客户端,指发出HBase操作的请求方。例如:之前编写的Java API代码发起求情、以及HBase shell交互命令行,都是CLient
角色-Master Server
- 监控RegionServer
- 处理RegionServer故障转移
- 处理元数据的变更
- 处理region的分配或移除
- 在空闲时间进行数据的负载均衡
- 通过Zookeeper发布自己的位置给客户端
角色-Region Server
- 处理分配给它的Region
- 负责存储HBase的实际数据
- 刷新缓存到HDFS
- 维护HLog
- 执行压缩
- 负责处理Region分片
其中RegionServer中包含了大量丰富的组件
Write-Ahead logs
HFile(StoreFile)
Store
MemStore
Region
逻辑结构模型
Region
- 在HBASE中,表被划分为很多「Region」,并由Region Server提供服务
Store
- Region按列蔟垂直划分为「Store」,存储在HDFS在文件中
MemStore
- MemStore与缓存内存类似
- 当往HBase中写入数据时,首先是写入到MemStore
- 每个列蔟将有一个MemStore
- 当MemStore存储快满的时候,整个数据将写入到HDFS中的HFile中
StoreFile
- 每当任何数据被写入HBASE时,首先要写入MemStore
- 当MemStore快满时,整个排序的key-value数据将被写入HDFS中的一个新的HFile中
- 写入HFile的操作是连续的,速度非常快
- 物理上存储的是HFile
WAL
- WAL全称为Write Ahead Log,它最大的作用就是故障恢复
- WAL是HBase中提供的一种高并发、持久化的日志保存与回放机制
- 每个业务数据的写入操作(PUT/DELETE/INCR),都会保存在WAL中
- 一旦服务器崩溃,通过回放WAL,就可以实现恢复崩溃之前的数据
- 物理上存储是Hadoop的Sequence File
Apache Phoenix
phoenix简介
Apache Phoenix让Hadoop中支持低延迟OLTP和业务操作分析。
-
提供标准的SQL以及完备的ACID事务支持
-
通过利用HBase作为存储,让NoSQL数据库具备通过有模式的方式读取数据,我们可以使用SQL语句来操作HBase,例如:创建表、以及插入数据、修改数据、删除数据等。
-
Phoenix通过协处理器在服务器端执行操作,最小化客户机/服务器数据传输
Apache Phoenix可以很好地与其他的Hadoop组件整合在一起,例如:Spark、Hive、Flume以及MapReduce。
hoenix特点
- Phoenix不会影响HBase性能,反而会提升HBase性能
- Phoenix将SQL查询编译为本机HBase扫描
- 确定scan的key的最佳startKey和endKey
- 编排scan的并行执行
- 将WHERE子句中的谓词推送到服务器端
- 通过协处理器执行聚合查询
- 用于提高非行键列查询性能的二级索引
- 统计数据收集,以改进并行化,并指导优化之间的选择
- 跳过扫描筛选器以优化IN、LIKE和OR查询
- 行键加盐保证分配均匀,负载均衡
Phoenix只是在HBase之上构建了SQL查询引擎,并不是像MapReduce、Spark这种大规模数据计算引擎。HBase的定位是在高性能随机读写,Phoenix可以使用SQL快查询HBase中的数据,但数据操作底层是必须符合HBase的存储结构,例如:必须要有ROWKEY、必须要有列蔟。因为有这样的一些限制,绝大多数公司不会选择HBase+Phoenix来作为数据仓库的开发。而是用来快速进行海量数据的随机读写。这方面HBase +Phoenix有很大的优势。
安装部署phoenix
官网下载地址:http://archive.apache.org/dist/phoenix/
# 安装包
wangting@ops01:/home/wangting >cd /opt/software/
wangting@ops01:/opt/software >ll | grep phoenix
-rw-r--r-- 1 wangting wangting 436868323 May 9 17:11 apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz
# 解压
wangting@ops01:/opt/software >tar -zxvf apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz -C /opt/module/
# 改名
wangting@ops01:/opt/module >mv apache-phoenix-5.0.0-HBase-2.0-bin phoenix
wangting@ops01:/opt/module >cd phoenix/
wangting@ops01:/opt/module/phoenix >
# 复制server包和client包到hbase集群各节点的lib目录下
wangting@ops01:/opt/module/phoenix >cp phoenix-*.jar /opt/module/hbase/lib/
wangting@ops01:/opt/module/phoenix >scp phoenix-*.jar ops02:/opt/module/hbase/lib/
wangting@ops01:/opt/module/phoenix >scp phoenix-*.jar ops03:/opt/module/hbase/lib/
# 添加环境变量
wangting@ops01:/opt/module/phoenix >sudo vim /etc/profile
#phoenix
export PHOENIX_HOME=/opt/module/phoenix
export PATH=$PATH:$PHOENIX_HOME/bin
wangting@ops01:/opt/module/phoenix >source /etc/profile
wangting@ops01:/opt/module/phoenix >for i in ops01 ops02 ops03 ;do ssh $i sudo netstat -tnlpu|grep 2181;done
tcp6 0 0 :::2181 :::* LISTEN 56644/java
tcp6 0 0 :::2181 :::* LISTEN 105183/java
tcp6 0 0 :::2181 :::* LISTEN 53772/java
wangting@ops01:/opt/module/phoenix >cd /opt/module/hbase/conf/
# hbase配置文件增加命名空间映射和索引相关配置
wangting@ops01:/opt/module/hbase/conf >vim hbase-site.xml
<!-- 支持HBase命名空间映射 -->
<property>
<name>phoenix.schema.isNamespaceMappingEnabled</name>
<value>true</value>
</property>
<!-- 支持索引预写日志编码 -->
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
wangting@ops01:/opt/module/hbase/conf >scp hbase-site.xml ops02:$PWD
wangting@ops01:/opt/module/hbase/conf >scp hbase-site.xml ops03:$PWD
# 4. 将配置后的hbase-site.xml拷贝到phoenix的bin目录
wangting@ops01:/opt/module/hbase/conf >cp hbase-site.xml /opt/module/phoenix/bin/
# 重启hbase集群
wangting@ops01:/opt/module/phoenix >stop-hbase.sh
wangting@ops01:/opt/module/phoenix >start-hbase.sh
# 启动Phoenix 指定zookeeper集群
wangting@ops01:/home/wangting >sqlline.py ops01,ops02,ops03:2181
Setting property: [incremental, false]
Setting property: [isolation, TRANSACTION_READ_COMMITTED]
issuing: !connect jdbc:phoenix:ops01,ops02,ops03:2181 none none org.apache.phoenix.jdbc.PhoenixDriver
Connecting to jdbc:phoenix:ops01,ops02,ops03:2181
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/phoenix/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
22/08/17 18:01:30 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
22/08/17 18:01:34 WARN query.ConnectionQueryServicesImpl: Expected 5 system tables but found 6:[SYSTEM.CATALOG, SYSTEM.FUNCTION, SYSTEM.LOG, SYSTEM.MUTEX, SYSTEM.SEQUENCE, SYSTEM.STATS]
Connected to: Phoenix (version 5.0)
Driver: PhoenixEmbeddedDriver (version 5.0)
Autocommit status: true
Transaction isolation: TRANSACTION_READ_COMMITTED
Building list of tables and columns for tab-completion (set fastconnect to true to skip)...
133/133 (100%) Done
Done
sqlline version 1.2.0
0: jdbc:phoenix:ops01,ops02,ops03:2181>
0: jdbc:phoenix:ops01,ops02,ops03:2181> !tables
+------------+--------------+-------------+---------------+----------+------------+----------------------------+-----------------+--------------+-----------------+---------------+---------------+-----------------+------------+-------------+----------------+----------+
| TABLE_CAT | TABLE_SCHEM | TABLE_NAME | TABLE_TYPE | REMARKS | TYPE_NAME | SELF_REFERENCING_COL_NAME | REF_GENERATION | INDEX_STATE | IMMUTABLE_ROWS | SALT_BUCKETS | MULTI_TENANT | VIEW_STATEMENT | VIEW_TYPE | INDEX_TYPE | TRANSACTIONAL | IS_NAMES |
+------------+--------------+-------------+---------------+----------+------------+----------------------------+-----------------+--------------+-----------------+---------------+---------------+-----------------+------------+-------------+----------------+----------+
| | SYSTEM | CATALOG | SYSTEM TABLE | | | | | | false | null | false | | | | false | true |
| | SYSTEM | FUNCTION | SYSTEM TABLE | | | | | | false | null | false | | | | false | true |
| | SYSTEM | LOG | SYSTEM TABLE | | | | | | true | 32 | false | | | | false | true |
| | SYSTEM | SEQUENCE | SYSTEM TABLE | | | | | | false | null | false | | | | false | true |
| | SYSTEM | STATS | SYSTEM TABLE | | | | | | false | null | false | | | | false | true |
+------------+--------------+-------------+---------------+----------+------------+----------------------------+-----------------+--------------+-----------------+---------------+---------------+-----------------+------------+-------------+----------------+----------+
0: jdbc:phoenix:ops01,ops02,ops03:2181>
phoenix使用
phoenix创建表
在Phoenix中,我们可以使用类似于MySQL DDL的方式快速创建表
建表语法:
CREATE TABLE IF NOT EXISTS 表名 (
ROWKEY名称 数据类型 PRIMARY KEY
列蔟名.列名1 数据类型 NOT NULL,
列蔟名.列名2 数据类型 NOT NULL,
列蔟名.列名3 数据类型);
create table if not exists ORDER_DTL(
ID varchar primary key,
C1.STATUS varchar,
C1.MONEY float,
C1.PAY_WAY integer,
C1.USER_ID varchar,
C1.OPERATION_TIME varchar,
C1.CATEGORY varchar
);
查看表结构信息
0: jdbc:phoenix:ops01,ops02,ops03:2181> !desc ORDER_DTL
+------------+--------------+-------------+-----------------+------------+------------+--------------+----------------+-----------------+-----------------+-----------+----------+-------------+----------------+-------------------+--------------------+-----------------+
| TABLE_CAT | TABLE_SCHEM | TABLE_NAME | COLUMN_NAME | DATA_TYPE | TYPE_NAME | COLUMN_SIZE | BUFFER_LENGTH | DECIMAL_DIGITS | NUM_PREC_RADIX | NULLABLE | REMARKS | COLUMN_DEF | SQL_DATA_TYPE | SQL_DATETIME_SUB | CHAR_OCTET_LENGTH | ORDINAL_POSITIO |
+------------+--------------+-------------+-----------------+------------+------------+--------------+----------------+-----------------+-----------------+-----------+----------+-------------+----------------+-------------------+--------------------+-----------------+
| | | ORDER_DTL | ID | 12 | VARCHAR | null | null | null | null | 0 | | | null | null | null | 1 |
| | | ORDER_DTL | STATUS | 12 | VARCHAR | null | null | null | null | 1 | | | null | null | null | 2 |
| | | ORDER_DTL | MONEY | 6 | FLOAT | null | null | null | null | 1 | | | null | null | null | 3 |
| | | ORDER_DTL | PAY_WAY | 4 | INTEGER | null | null | null | null | 1 | | | null | null | null | 4 |
| | | ORDER_DTL | USER_ID | 12 | VARCHAR | null | null | null | null | 1 | | | null | null | null | 5 |
| | | ORDER_DTL | OPERATION_TIME | 12 | VARCHAR | null | null | null | null | 1 | | | null | null | null | 6 |
| | | ORDER_DTL | CATEGORY | 12 | VARCHAR | null | null | null | null | 1 | | | null | null | null | 7 |
+------------+--------------+-------------+-----------------+------------+------------+--------------+----------------+-----------------+-----------------+-----------+----------+-------------+----------------+-------------------+--------------------+-----------------+
0: jdbc:phoenix:ops01,ops02,ops03:2181>
删除表
0: jdbc:phoenix:ops01,ops02,ops03:2181> drop table if exists ORDER_DTL;
No rows affected (1.601 seconds)
phoenix大小写
在HBase中,如果在列蔟、列名没有添加双引号。Phoenix会自动转换为大写
create table if not exists ORDER_DTL(
id varchar primary key,
C1.status varchar,
C1.money double,
C1.pay_way integer,
C1.user_id varchar,
C1.operation_time varchar,
C1.category varchar
);
0: jdbc:phoenix:ops01,ops02,ops03:2181> !desc ORDER_DTL
|COLUMN_NAME
|ID
|STATUS
|MONEY
|PAY_WAY
|USER_ID
|OPERATION_TIME
|CATEGORY
插入数据
在Phoenix中,插入并不是使用insert来实现的。而是 「upsert 」命令。它的功能为insert + update,与HBase中的put相对应。如果不存在则插入,否则更新。列表是可选的,如果不存在,值将按模式中声明的顺序映射到列。这些值必须计算为常量
语法:upsert into 表名(列蔟列名, xxxx, ) VALUES(XXX, XXX, XXX)
0: jdbc:phoenix:ops01,ops02,ops03:2181> UPSERT INTO ORDER_DTL VALUES('000001', '已提交', 4070, 1, '4944191', '2020-04-25 12:09:16', '手机');
1 row affected (0.249 seconds)
数据查询
与标准SQL一样,Phoenix也是使用select语句来实现数据的查询
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL;
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000001 | 已提交 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机 |
+---------+---------+---------+----------+----------+----------------------+-----------+
1 row selected (0.162 seconds)
数据更新
在Phoenix中,更新数据也是使用UPSERT。语法格式如下:
UPSERT INTO 表名(列名, …) VALUES(对应的值, …);
将ORDER_DTL表ID为’000001’的订单状态修改为已付款
0: jdbc:phoenix:ops01,ops02,ops03:2181> UPSERT INTO ORDER_DTL(ID, STATUS) VALUES ('000001', '已付款');
1 row affected (0.014 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL;
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000001 | 已付款 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机 |
+---------+---------+---------+----------+----------+----------------------+-----------+
1 row selected (0.083 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181>
条件查询
0: jdbc:phoenix:ops01,ops02,ops03:2181> UPSERT INTO ORDER_DTL VALUES('000002', '已提交', 8888, 1, '9988776', '2222-02-22 12:09:16', '激光枪');
1 row affected (0.015 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL;
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000001 | 已付款 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机 |
| 000002 | 已提交 | 8888.0 | 1 | 9988776 | 2222-02-22 12:09:16 | 激光枪 |
+---------+---------+---------+----------+----------+----------------------+-----------+
2 rows selected (0.08 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> SELECT * FROM ORDER_DTL WHERE "ID" = '000002';
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000002 | 已提交 | 8888.0 | 1 | 9988776 | 2222-02-22 12:09:16 | 激光枪 |
+---------+---------+---------+----------+----------+----------------------+-----------+
1 row selected (0.054 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> SELECT * FROM ORDER_DTL WHERE "OPERATION_TIME" > '2022-08-18 00:00:00';
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000002 | 已提交 | 8888.0 | 1 | 9988776 | 2222-02-22 12:09:16 | 激光枪 |
+---------+---------+---------+----------+----------+----------------------+-----------+
1 row selected (0.069 seconds)
条件删除
0: jdbc:phoenix:ops01,ops02,ops03:2181> DELETE FROM ORDER_DTL WHERE "ID" = '000002';
1 row affected (0.01 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL;
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000001 | 已付款 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机 |
+---------+---------+---------+----------+----------+----------------------+-----------+
1 row selected (0.057 seconds)
导入测试数据(执行sql脚本)
使用phoenix自带的psql.py工具
# 编写sql测试数据
wangting@ops01:/home/wangting >cd /home/wangting/hbase/
wangting@ops01:/home/wangting/hbase >vim load_phoenix.sql
UPSERT INTO "ORDER_DTL" VALUES('000002','已提交',4070,1,'4944191','2020-04-25 12:09:16','手机;');
UPSERT INTO "ORDER_DTL" VALUES('000003','已完成',4350,1,'1625615','2020-04-25 12:09:37','家用电器;;电脑;');
UPSERT INTO "ORDER_DTL" VALUES('000004','已提交',6370,3,'3919700','2020-04-25 12:09:39','男装;男鞋;');
UPSERT INTO "ORDER_DTL" VALUES('000005','已付款',6370,3,'3919700','2020-04-25 12:09:44','男装;男鞋;');
UPSERT INTO "ORDER_DTL" VALUES('000006','已提交',9380,1,'2993700','2020-04-25 12:09:41','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000007','已付款',9380,1,'2993700','2020-04-25 12:09:46','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000008','已完成',6400,2,'5037058','2020-04-25 12:10:13','数码;女装;');
UPSERT INTO "ORDER_DTL" VALUES('000009','已付款',280,1,'3018827','2020-04-25 12:09:53','男鞋;汽车;');
UPSERT INTO "ORDER_DTL" VALUES('000010','已完成',5600,1,'6489579','2020-04-25 12:08:55','食品;家用电器;');
UPSERT INTO "ORDER_DTL" VALUES('000011','已付款',5600,1,'6489579','2020-04-25 12:09:00','食品;家用电器;');
UPSERT INTO "ORDER_DTL" VALUES('000012','已提交',8340,2,'2948003','2020-04-25 12:09:26','男装;男鞋;');
UPSERT INTO "ORDER_DTL" VALUES('000013','已付款',8340,2,'2948003','2020-04-25 12:09:30','男装;男鞋;');
UPSERT INTO "ORDER_DTL" VALUES('000014','已提交',7060,2,'2092774','2020-04-25 12:09:38','酒店;旅游;');
UPSERT INTO "ORDER_DTL" VALUES('000015','已提交',640,3,'7152356','2020-04-25 12:09:49','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000016','已付款',9410,3,'7152356','2020-04-25 12:10:01','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000017','已提交',9390,3,'8237476','2020-04-25 12:10:08','男鞋;汽车;');
UPSERT INTO "ORDER_DTL" VALUES('000018','已提交',7490,2,'7813118','2020-04-25 12:09:05','机票;文娱;');
UPSERT INTO "ORDER_DTL" VALUES('000019','已付款',7490,2,'7813118','2020-04-25 12:09:06','机票;文娱;');
UPSERT INTO "ORDER_DTL" VALUES('000020','已付款',5360,2,'5301038','2020-04-25 12:08:50','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000021','已提交',5360,2,'5301038','2020-04-25 12:08:53','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000022','已取消',5360,2,'5301038','2020-04-25 12:08:58','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000023','已付款',6490,0,'3141181','2020-04-25 12:09:22','食品;家用电器;');
UPSERT INTO "ORDER_DTL" VALUES('000024','已付款',3820,1,'9054826','2020-04-25 12:10:04','家用电器;;电脑;');
UPSERT INTO "ORDER_DTL" VALUES('000025','已提交',4650,2,'5837271','2020-04-25 12:08:52','机票;文娱;');
UPSERT INTO "ORDER_DTL" VALUES('000026','已付款',4650,2,'5837271','2020-04-25 12:08:57','机票;文娱;');
wangting@ops01:/home/wangting/hbase >pwd
/home/wangting/hbase
wangting@ops01:/home/wangting/hbase >ls -l /home/wangting/hbase/load_phoenix.sql
-rw-rw-r-- 1 wangting wangting 2773 Aug 18 09:19 /home/wangting/hbase/load_phoenix.sql
# 如果配置了环境变量,执行时也可以不带全路径
wangting@ops01:/home/wangting >ls -l /opt/module/phoenix/bin/psql.py
-rwxr-xr-x 1 wangting wangting 2739 Jun 27 2018 /opt/module/phoenix/bin/psql.py
wangting@ops01:/home/wangting >/opt/module/phoenix/bin/psql.py /home/wangting/hbase/load_phoenix.sql
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/phoenix/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
22/08/18 09:24:01 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
1 row upserted
Time: 0.061 sec(s)
1 row upserted
Time: 0.009 sec(s)
...
...
...
1 row upserted
Time: 0.005 sec(s)
# 进入phoenix命令行验证
wangting@ops01:/home/wangting >
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL;
+---------+---------+---------+----------+----------+----------------------+------------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+------------+
| 000001 | 已付款 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机 |
| 000002 | 已提交 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机; |
| 000003 | 已完成 | 4350.0 | 1 | 1625615 | 2020-04-25 12:09:37 | 家用电器;;电脑; |
| 000004 | 已提交 | 6370.0 | 3 | 3919700 | 2020-04-25 12:09:39 | 男装;男鞋; |
| 000005 | 已付款 | 6370.0 | 3 | 3919700 | 2020-04-25 12:09:44 | 男装;男鞋; |
| 000006 | 已提交 | 9380.0 | 1 | 2993700 | 2020-04-25 12:09:41 | 维修;手机; |
| 000007 | 已付款 | 9380.0 | 1 | 2993700 | 2020-04-25 12:09:46 | 维修;手机; |
| 000008 | 已完成 | 6400.0 | 2 | 5037058 | 2020-04-25 12:10:13 | 数码;女装; |
| 000009 | 已付款 | 280.0 | 1 | 3018827 | 2020-04-25 12:09:53 | 男鞋;汽车; |
| 000010 | 已完成 | 5600.0 | 1 | 6489579 | 2020-04-25 12:08:55 | 食品;家用电器; |
| 000011 | 已付款 | 5600.0 | 1 | 6489579 | 2020-04-25 12:09:00 | 食品;家用电器; |
| 000012 | 已提交 | 8340.0 | 2 | 2948003 | 2020-04-25 12:09:26 | 男装;男鞋; |
| 000013 | 已付款 | 8340.0 | 2 | 2948003 | 2020-04-25 12:09:30 | 男装;男鞋; |
| 000014 | 已提交 | 7060.0 | 2 | 2092774 | 2020-04-25 12:09:38 | 酒店;旅游; |
| 000015 | 已提交 | 640.0 | 3 | 7152356 | 2020-04-25 12:09:49 | 维修;手机; |
| 000016 | 已付款 | 9410.0 | 3 | 7152356 | 2020-04-25 12:10:01 | 维修;手机; |
| 000017 | 已提交 | 9390.0 | 3 | 8237476 | 2020-04-25 12:10:08 | 男鞋;汽车; |
| 000018 | 已提交 | 7490.0 | 2 | 7813118 | 2020-04-25 12:09:05 | 机票;文娱; |
| 000019 | 已付款 | 7490.0 | 2 | 7813118 | 2020-04-25 12:09:06 | 机票;文娱; |
| 000020 | 已付款 | 5360.0 | 2 | 5301038 | 2020-04-25 12:08:50 | 维修;手机; |
| 000021 | 已提交 | 5360.0 | 2 | 5301038 | 2020-04-25 12:08:53 | 维修;手机; |
| 000022 | 已取消 | 5360.0 | 2 | 5301038 | 2020-04-25 12:08:58 | 维修;手机; |
| 000023 | 已付款 | 6490.0 | 0 | 3141181 | 2020-04-25 12:09:22 | 食品;家用电器; |
| 000024 | 已付款 | 3820.0 | 1 | 9054826 | 2020-04-25 12:10:04 | 家用电器;;电脑; |
| 000025 | 已提交 | 4650.0 | 2 | 5837271 | 2020-04-25 12:08:52 | 机票;文娱; |
| 000026 | 已付款 | 4650.0 | 2 | 5837271 | 2020-04-25 12:08:57 | 机票;文娱; |
+---------+---------+---------+----------+----------+----------------------+------------+
26 rows selected (0.17 seconds)
分页数据查询
使用limit和offset可以快速进行分页
limit表示每页多少条记录,offset表示从第几条记录开始查起
-- 第一页
select * from ORDER_DTL limit 10 offset 0;
-- 第二页
-- offset从10开始
select * from ORDER_DTL limit 10 offset 10;
-- 第三页
select * from ORDER_DTL limit 10 offset 20;
# select * from ORDER_DTL limit 10 offset 0;当offset为不定义,默认为0开始
# 第一页数据
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL limit 10 offset 0;
+---------+---------+---------+----------+----------+----------------------+------------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+------------+
| 000001 | 已付款 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机 |
| 000002 | 已提交 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机; |
| 000003 | 已完成 | 4350.0 | 1 | 1625615 | 2020-04-25 12:09:37 | 家用电器;;电脑; |
| 000004 | 已提交 | 6370.0 | 3 | 3919700 | 2020-04-25 12:09:39 | 男装;男鞋; |
| 000005 | 已付款 | 6370.0 | 3 | 3919700 | 2020-04-25 12:09:44 | 男装;男鞋; |
| 000006 | 已提交 | 9380.0 | 1 | 2993700 | 2020-04-25 12:09:41 | 维修;手机; |
| 000007 | 已付款 | 9380.0 | 1 | 2993700 | 2020-04-25 12:09:46 | 维修;手机; |
| 000008 | 已完成 | 6400.0 | 2 | 5037058 | 2020-04-25 12:10:13 | 数码;女装; |
| 000009 | 已付款 | 280.0 | 1 | 3018827 | 2020-04-25 12:09:53 | 男鞋;汽车; |
| 000010 | 已完成 | 5600.0 | 1 | 6489579 | 2020-04-25 12:08:55 | 食品;家用电器; |
+---------+---------+---------+----------+----------+----------------------+------------+
10 rows selected (0.079 seconds)
# 第二页数据
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL limit 10 offset 10;
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000011 | 已付款 | 5600.0 | 1 | 6489579 | 2020-04-25 12:09:00 | 食品;家用电器; |
| 000012 | 已提交 | 8340.0 | 2 | 2948003 | 2020-04-25 12:09:26 | 男装;男鞋; |
| 000013 | 已付款 | 8340.0 | 2 | 2948003 | 2020-04-25 12:09:30 | 男装;男鞋; |
| 000014 | 已提交 | 7060.0 | 2 | 2092774 | 2020-04-25 12:09:38 | 酒店;旅游; |
| 000015 | 已提交 | 640.0 | 3 | 7152356 | 2020-04-25 12:09:49 | 维修;手机; |
| 000016 | 已付款 | 9410.0 | 3 | 7152356 | 2020-04-25 12:10:01 | 维修;手机; |
| 000017 | 已提交 | 9390.0 | 3 | 8237476 | 2020-04-25 12:10:08 | 男鞋;汽车; |
| 000018 | 已提交 | 7490.0 | 2 | 7813118 | 2020-04-25 12:09:05 | 机票;文娱; |
| 000019 | 已付款 | 7490.0 | 2 | 7813118 | 2020-04-25 12:09:06 | 机票;文娱; |
| 000020 | 已付款 | 5360.0 | 2 | 5301038 | 2020-04-25 12:08:50 | 维修;手机; |
+---------+---------+---------+----------+----------+----------------------+-----------+
10 rows selected (0.063 seconds)
# 第三页数据
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL limit 10 offset 20;
+---------+---------+---------+----------+----------+----------------------+------------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+------------+
| 000021 | 已提交 | 5360.0 | 2 | 5301038 | 2020-04-25 12:08:53 | 维修;手机; |
| 000022 | 已取消 | 5360.0 | 2 | 5301038 | 2020-04-25 12:08:58 | 维修;手机; |
| 000023 | 已付款 | 6490.0 | 0 | 3141181 | 2020-04-25 12:09:22 | 食品;家用电器; |
| 000024 | 已付款 | 3820.0 | 1 | 9054826 | 2020-04-25 12:10:04 | 家用电器;;电脑; |
| 000025 | 已提交 | 4650.0 | 2 | 5837271 | 2020-04-25 12:08:52 | 机票;文娱; |
| 000026 | 已付款 | 4650.0 | 2 | 5837271 | 2020-04-25 12:08:57 | 机票;文娱; |
+---------+---------+---------+----------+----------+----------------------+------------+
6 rows selected (0.061 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181>
创建视图
0: jdbc:phoenix:ops01,ops02,ops03:2181> CREATE VIEW VIEW_ORDER_DTL AS SELECT * FROM ORDER_DTL;
No rows affected (0.063 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> select count(*) from VIEW_ORDER_DTL;
+-----------+
| COUNT(1) |
+-----------+
| 26 |
+-----------+
1 row selected (0.047 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> select count(*) from ORDER_DTL;
+-----------+
| COUNT(1) |
+-----------+
| 26 |
+-----------+
1 row selected (0.015 seconds)
指定分区压缩格式建表
# ===按条件建表===
# 按照用户ID来分区,一共4个分区。并指定数据的压缩格式为GZ
create table if not exists ORDER_DTL_NEW(
"id" varchar primary key,
C1."status" varchar,
C1."money" float,
C1."pay_way" integer,
C1."user_id" varchar,
C1."operation_time" varchar,
C1."category" varchar
)
CONPRESSION='GZ'
SPLIT ON ('3','5','7');
# 加盐指定数量分区并指定数据的压缩格式
create table if not exists ORDER_DTL_NEWWW(
"id" varchar primary key,
C1."status" varchar,
C1."money" float,
C1."pay_way" integer,
C1."user_id" varchar,
C1."operation_time" varchar,
C1."category" varchar
)
CONPRESSION='GZ', SALT_BUCKETS=10;
phoenix索引介绍
索引-全局索引
- 全局索引适用于读多写少业务
- 全局索引绝大多数负载都发生在写入时,当构建了全局索引时,Phoenix会拦截写入(DELETE、UPSERT值和UPSERT SELECT)上的数据表更新,构建索引更新,同时更新所有相关的索引表,开销较大
- 读取时,Phoenix将选择最快能够查询出数据的索引表。默认情况下,除非使用Hint,如果SELECT查询中引用了其他非索引列,该索引是不会生效的
- 全局索引一般和覆盖索引搭配使用,读的效率很高,但写入效率会受影响
创建语法:
CREATE INDEX 索引名称 ON 表名 (列名1, 列名2, 列名3...)
索引-本地索引
- 本地索引适合写操作频繁,读相对少的业务
- 当使用SQL查询数据时,Phoenix会自动选择是否使用本地索引查询数据
- 在本地索引中,索引数据和业务表数据存储在同一个服务器上,避免写入期间的其他网络开销
- 在Phoenix 4.8.0之前,本地索引保存在一个单独的表中,在Phoenix 4.8.1中,本地索引的数据是保存在一个影子列蔟中
- 本地索引查询即使SELECT引用了非索引中的字段,也会自动应用索引的
- [注意]-创建表的时候指定了SALT_BUCKETS,是不支持本地索引的
创建语法:
CREATE local INDEX 索引名称 ON 表名 (列名1, 列名2, 列名3...)
索引-覆盖索引
Phoenix提供了覆盖的索引,可以不需要在找到索引条目后返回到主表。Phoenix可以将关心的数据捆绑在索引行中,从而节省了读取时间的开销
例如,以下语法将在v1和v2列上创建索引,并在索引中包括v3列,也就是通过v1、v2就可以直接把数据查询出来。
创建语法:
CREATE INDEX my_index ON my_table (v1,v2) INCLUDE(v3)
索引-函数索引
函数索引(4.3和更高版本)可以支持在列上创建索引,还可以基于任意表达式上创建索引。然后,当查询使用该表达式时,可以使用索引来检索结果,而不是数据表。例如,可以在UPPER(FIRST_NAME||‘ ’||LAST_NAME)上创建一个索引,这样将来搜索两个名字拼接在一起时,索引依然可以生效
创建语法:
-- 创建索引
CREATE INDEX UPPER_NAME_IDX ON EMP (UPPER(FIRST_NAME||' '||LAST_NAME))
-- 以下查询会走索引
SELECT EMP_ID FROM EMP WHERE UPPER(FIRST_NAME||' '||LAST_NAME)='JOHN DOE'
HBase工作机制
hbase读取数据流程
- 客户端拿到一个rowkey(首先得要知道这个rowkey存在哪个region中)
- 根据zk获取hbase:meta表,这个表中存放了region的信息,根据namespace、表名,就可以根据rowkey查看是否匹配某个region的startkey、endkey,返回region的信息
- 还需要查询region是在哪个HRegionServer(因为我们是不知道region会在存在什么地方的)
- 读取Store
- 优先读取写缓存(MemStore)
- 读取BlockCache(LRUBlockCache、BucketBlockCache)
- 再读取HFile
hbase数据存储流程
HBase的数据存储过程是分为几个阶段的。写入的过程与HBase的LSM结构对应。
-
为了提高HBase的写入速度,数据都是先写入到MemStore(内存)结构中,V2.0 MemStore也会进行Compaction
-
MemStore写到一定程度(默认128M),由后台程序将MemStore的内容flush刷写到HDFS中的StoreFile
-
数据量较大时,会产生很多的StoreFile。这样对高效读取不利,HBase会将这些小的StoreFile合并,一般3-10个文件合并成一个更大的StoreFile
写入MemStore
- Client访问zookeeper,从ZK中找到meta表的region位置
- 读取meta表中的数据,根据namespace、表名、rowkey获取对应的Region信息
- 通过刚刚获取的地址访问对应的RegionServer,拿到对应的表存储的RegionServer
- 去表所在的RegionServer进行数据的添加
- 查找对应的region,在region中寻找列族,先向MemStore中写入数据
MemStore溢写合并
- 当MemStore写入的值变多,触发溢写操作(flush),进行文件的溢写,成为一个StoreFile
- 当溢写的文件过多时,会触发文件的合并(Compact)操作,合并有两种方式(major,minor)
- 一旦MemStore达到128M时,则触发Flush溢出(Region级别)
- MemStore的存活时间超过1小时(默认),触发Flush溢写(RegionServer级别)
In-memory合并
In-memory合并是HBase 2.0之后添加的。它与默认的MemStore的区别:实现了在内存中进行compaction(合并)。
在CompactingMemStore中,数据是以段(Segment)为单位存储数据的。MemStore包含了多个segment
- 当数据写入时,首先写入到的是Active segment中(也就是当前可以写入的segment段)
- 在2.0之前,如果MemStore中的数据量达到指定的阈值时,就会将数据flush到磁盘中的一个StoreFile
- 2.0的In-memory compaction,active segment满了后,将数据移动到pipeline中。这个过程跟以前不一样,以前是flush到磁盘,而这次是将Active segment的数据,移到称为pipeline的内存当中。一个pipeline中可以有多个segment。而In-memory compaction会将pipeline的多个segment合并为更大的、更紧凑的segment,这就是compaction
- HBase会尽量延长CompactingMemStore的生命周期,以达到减少总的IO开销。当需要把CompactingMemStore flush到磁盘时,pipeline中所有的segment会被移动到一个snapshot中,然后进行合并后写入到HFile
StoreFile合并
- 当MemStore超过阀值的时候,就要flush到HDFS上生成一个StoreFile。因此随着不断写入,HFile的数量将会越来越多,根据前面所述,StoreFile数量过多会降低读性能
- 为了避免对读性能的影响,需要对这些StoreFile进行compact操作,把多个HFile合并成一个HFile
- compact操作需要对HBase的数据进行多次的重新读写,因此这个过程会产生大量的IO。可以看到compact操作的本质就是以IO操作换取后续的读性能的提高
Region管理
region分配
- 任何时刻,一个region只能分配给一个region server
- Master记录了当前有哪些可用的region server,以及当前哪些region分配给了哪些region server,哪些region还没有分配。当需要分配的新的region,并且有一个region server上有可用空间时,master就给这个region server发送一个装载请求,把region分配给这个region server。region server得到请求后,就开始对此region提供服务。
region server上线
- Master使用ZooKeeper来跟踪region server状态
- 当某个region server启动时
- 首先在zookeeper上的server目录下建立代表自己的znode
- 由于Master订阅了server目录上的变更消息,当server目录下的文件出现新增或删除操作时,master可以得到来自zookeeper的实时通知
- 一旦region server上线,master能马上得到消息
region server下线
- 当region server下线时,它和zookeeper的会话断开,ZooKeeper而自动释放代表这台server的文件上的独占锁
- Master即可以确定
- region server和zookeeper之间的网络断开
- region server挂掉
- 无论哪种情况,region server都无法继续为它的region提供服务了,此时master会删除server目录下代表这台region server的znode数据,并将这台region server的region分配给其它还活着的节点
region分裂
- 当region中的数据逐渐变大之后,达到某一个阈值,会进行裂变
- 一个region等分为两个region,并分配到不同的RegionServer
- 原本的Region会下线,新Split出来的两个Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上
- HBase只是增加数据,所有的更新和删除操作,都是在Compact阶段做的
- 用户写操作只需要进入到内存即可立即返回,从而保证I/O高性能读写
自动分区
- 如果初始时R=1,那么Min(128MB,10GB)=128MB,也就是说在第一个flush的时候就会触发分裂操作
- 当R=2的时候Min(22128MB,10GB)=512MB ,当某个store file大小达到512MB的时候,就会触发分裂
- 如此类推,当R=9的时候,store file 达到10GB的时候就会分裂,也就是说当R>=9的时候,store file 达到10GB的时候就会分裂
- split 点都位于region中row key的中间点
手动分区
在创建表的时候,就可以指定表分为多少个Region。默认一开始的时候系统会只向一个RegionServer写数据,系统不指定startRow和endRow,可以在运行的时候提前Split,提高并发写入
Master工作机器
master上线
Master启动进行以下步骤:
- 从zookeeper上获取唯一一个代表active master的锁,用来阻止其它master成为master
- 一般hbase集群中总是有一个master在提供服务,还有一个以上的‘master’在等待时机抢占它的位置。
- 扫描zookeeper上的server父节点,获得当前可用的region server列表
- 和每个region server通信,获得当前已分配的region和region server的对应关系
- 扫描.META.region的集合,计算得到当前还未分配的region,将他们放入待分配region列表
master下线
- 由于master只维护表和region的元数据,而不参与表数据IO的过程,master下线仅导致所有元数据的修改被冻结
- 无法创建删除表
- 无法修改表的schema
- 无法进行region的负载均衡
- 无法处理region 上下线
- 无法进行region的合并
- 唯一例外的是region的split可以正常进行,因为只有region server参与
- 表的数据读写还可以正常进行
- 因此master下线短时间内对整个hbase集群没有影响
- 从上线过程可以看到,master保存的信息全是可以冗余信息(都可以从系统其它地方收集到或者计算出来)
HBase常规优化
每个集群会有系统配置,社区一定会把一些通用的、适应性强的作为默认配置,有很多都是折中的配置。很多时候,出现问题的时候,我们要考虑优化。
- 通用优化
- 跟硬件有一定的关系,SSD、RAID(给NameNode使用RAID1架构,可以有一定容错能力)
- 操作系统优化
- 最大的开启文件数量(集群规模大之后,写入的速度很快,经常要Flush,会在操作系统上同时打开很多的文件读取)
- 最大允许开启的进程
- HDFS优化
- 副本数
- RPC的最大数量
- 开启的线程数
- HBase优化
- 配置StoreFile大小
- 预分区
- 数据压缩
- 设计ROWKEY
- 开启BLOOMFILER
- 2.X开启In-memory Compaction
- …
- JVM
- 调整堆内存大小
- 调整GC,并行GC,缩短GC的时间