/opt/hbase/conf 中不能启动hbase_Hbase从入门到入坑
一 什么是HBASE
HBASE是一個高可靠性、高性能、面向列、可伸縮的分布式存儲系統,利用HBASE技術可在廉價PC Server上搭建起大規模結構化存儲集群。
HBASE的目標是存儲并處理大型的數據,更具體來說是僅需使用普通的硬件配置,就能夠處理由成千上萬的行和列所組成的大型數據。
HBASE是Google Bigtable的開源實現,但是也有很多不同之處。比如:Google Bigtable利用GFS作為其文件存儲系統,HBASE利用Hadoop HDFS作為其文件存儲系統;Google運行MAPREDUCE來處理Bigtable中的海量數據,HBASE同樣利用Hadoop MapReduce來處理HBASE中的海量數據;Google Bigtable利用Chubby作為協同服務,HBASE利用Zookeeper作為對應。
HBASE與mysql、oralce、db2、sqlserver等關系型數據庫不同,它是一個NoSQL數據庫(非關系型數據庫)
HBASE相比于其他nosql數據庫(mongodb、redis、cassendra、hazelcast)的特點:
Hbase的表數據存儲在HDFS文件系統中
從而,hbase具備如下特性:
- 存儲容量可以線性擴展;
- 數據存儲的安全性可靠性極高!
二 安裝HBASE
HBASE是一個分布式系統
其中有一個管理角色:HMaster(一般2臺,一臺active,一臺backup)
其他的數據節點角色:HRegionServer(很多臺,看數據容量)
2.1 安裝準備
需要先有一個java環境
首先,要有一個HDFS集群,并正常運行;regionserver應該跟hdfs中的datanode在一起
其次,還需要一個zookeeper集群,并正常運行
然后,安裝HBASE
角色分配如下:
Hdp01: namenode datanode regionserver hmaster zookeeper
Hdp02: datanode regionserver zookeeper
Hdp03: datanode regionserver zookeeper
2.2 安裝步驟
解壓hbase安裝包
修改hbase-env.sh
export JAVA_HOME=/root/apps/jdk1.7.0_67
export HBASE_MANAGES_ZK=false
修改hbase-site.xml
hbase.rootdir
hdfs://hdp01:9000/hbase
hbase.cluster.distributed
true
hbase.zookeeper.quorum
hdp01:2181,hdp02:2181,hdp03:2181
修改 regionservers
hdp01
hdp02
hdp03
2.3 啟動hbase集群
bin/start-hbase.sh啟動完后,還可以在集群中找任意一臺機器啟動一個備用的master
bin/hbase-daemon.sh start master新啟的這個master會處于backup狀態
三 hbase初體驗
3.1 啟動hbase命令行客戶端
bin/hbase?shellHbase>?list?????//?查看表Hbase>?status???//?查看集群狀態Hbase> version // 查看集群版本3.2 hbase表模型的特點
要點一:首先會按行鍵排序
要點二:同一行里面的kv會按列族排序,再按k排序
3.3 hbase的表中能存儲什么數據類型
hbase中只支持byte[]
此處的byte[] 包括了:rowkey,key,value,列族名,表名
3.4 hbase命令行客戶端操作
名稱
命令表達式
創建表
create '表名', '列族名1','列族名2','列族名N'
查看所有表
list
描述表
describe ‘表名’
判斷表存在
exists '表名'
判斷是否禁用啟用表
is_enabled '表名'
is_disabled ‘表名’
添加記錄
put ‘表名’, ‘rowKey’, ‘列族 : 列‘ , '值'
查看記錄rowkey下的所有數據
get '表名' , 'rowKey'
查看表中的記錄總數
count '表名'
獲取某個列族
get '表名','rowkey','列族'
獲取某個列族的某個列
get '表名','rowkey','列族:列’
刪除記錄
delete ‘表名’ ,‘行名’ , ‘列族:列'
刪除整行
deleteall '表名','rowkey'
刪除一張表
先要屏蔽該表,才能對該表進行刪除
第一步 disable ‘表名’ ,第二步 drop '表名'
清空表
truncate '表名'
查看所有記錄
scan "表名"
查看某個表某個列中所有數據
scan "表名" , {COLUMNS=>'列族名:列名'}
更新記錄
就是重寫一遍,進行覆蓋,hbase沒有修改,都是追加
3.4.1 建表
create?'t_user_info','base_info','extra_info' 表名 列族名 列族名3.4.2 插入數據
hbase(main):011:0> put 't_user_info','001','base_info:username','zhangsan'0 row(s) in 0.2420 seconds?hbase(main):012:0> put 't_user_info','001','base_info:age','18'0 row(s) in 0.0140 seconds?hbase(main):013:0> put 't_user_info','001','base_info:sex','female'0?row(s)?in?0.0070?seconds?hbase(main):014:0> put 't_user_info','001','extra_info:career','it'0 row(s) in 0.0090 seconds?hbase(main):015:0> put 't_user_info','002','extra_info:career','actoress'0?row(s)?in?0.0090?seconds?hbase(main):016:0> put 't_user_info','002','base_info:username','liuyifei'0 row(s) in 0.0060 seconds3.4.3 查詢方式一 scan掃描
hbase(main):017:0> scan 't_user_info'ROW COLUMN+CELL 001 column=base_info:age, timestamp=1496567924507, value=18 001 column=base_info:sex, timestamp=1496567934669, value=female 001 column=base_info:username, timestamp=1496567889554, value=zhangsan 001 column=extra_info:career, timestamp=1496567963992, value=it 002 column=base_info:username, timestamp=1496568034187, value=liuyifei 002 column=extra_info:career, timestamp=1496568008631, value=actoress?3.4.4 查詢方式二 get單行數據
hbase(main):020:0> get 't_user_info','001'COLUMN CELL base_info:age timestamp=1496568160192, value=19 base_info:sex timestamp=1496567934669, value=female base_info:username timestamp=1496567889554, value=zhangsan extra_info:career timestamp=1496567963992, value=it 4 row(s) in 0.0770 seconds3.4.5 刪除一個kv數據
hbase(main):021:0> delete 't_user_info','001','base_info:sex'0 row(s) in 0.0390 seconds刪除整行數據
hbase(main):024:0> deleteall 't_user_info','001'0 row(s) in 0.0090 secondshbase(main):025:0> get 't_user_info','001'COLUMN CELL 0 row(s) in 0.0110 seconds3.4.6 刪除整個表
hbase(main):028:0> disable 't_user_info'0 row(s) in 2.3640 secondshbase(main):029:0> drop 't_user_info'0 row(s) in 1.2950 secondshbase(main):030:0> listTABLE 0 row(s) in 0.0130 seconds=> []3.5 Hbase重要特性-排序特性(行鍵)
與nosql數據庫們一樣,row key是用來檢索記錄的主鍵。訪問HBASE table中的行,只有三種方式:
1.通過單個row key訪問
2.通過row key的range(正則)
3.全表掃描
Row key行鍵 (Row key)可以是任意字符串(最大長度 是 64KB,實際應用中長度一般為 10-100bytes),在HBASE內部,row key保存為字節數組。存儲時,數據按照Row key的字典序(byte order)排序存儲。設計key時,要充分排序存儲這個特性,將經常一起讀取的行存儲放到一起。(位置相關性)
插入到hbase中去的數據,hbase會自動排序存儲:
排序規則: 首先看行鍵,然后看列族名,然后看列(key)名;按字典順序
Hbase的這個特性跟查詢效率有極大的關系
比如:一張用來存儲用戶信息的表,有名字,戶籍,年齡,職業....等信息
然后,在業務系統中經常需要:
查詢某個省的所有用戶
經常需要查詢某個省的指定姓的所有用戶
思路:如果能將相同省的用戶在hbase的存儲文件中連續存儲,并且能將相同省中相同姓的用戶連續存儲,那么,上述兩個查詢需求的效率就會提高!!!
做法:將查詢條件拼到rowkey內
四 HBASE客戶端API操作
package?com.wedoctor.hbase;?import java.util.ArrayList;?import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.hbase.Cell;import org.apache.hadoop.hbase.CellUtil;import org.apache.hadoop.hbase.HBaseConfiguration;import org.apache.hadoop.hbase.HColumnDescriptor;import org.apache.hadoop.hbase.HTableDescriptor;import org.apache.hadoop.hbase.MasterNotRunningException;import org.apache.hadoop.hbase.TableName;import org.apache.hadoop.hbase.ZooKeeperConnectionException;import org.apache.hadoop.hbase.client.Connection;import org.apache.hadoop.hbase.client.ConnectionFactory;import org.apache.hadoop.hbase.client.Delete;import org.apache.hadoop.hbase.client.Get;import org.apache.hadoop.hbase.client.HBaseAdmin;import org.apache.hadoop.hbase.client.HConnection;import org.apache.hadoop.hbase.client.HConnectionManager;import org.apache.hadoop.hbase.client.Put;import org.apache.hadoop.hbase.client.Result;import org.apache.hadoop.hbase.client.ResultScanner;import org.apache.hadoop.hbase.client.Scan;import org.apache.hadoop.hbase.client.Table;import org.apache.hadoop.hbase.filter.ColumnPrefixFilter;import org.apache.hadoop.hbase.filter.CompareFilter;import org.apache.hadoop.hbase.filter.FilterList;import org.apache.hadoop.hbase.filter.FilterList.Operator;import org.apache.hadoop.hbase.filter.RegexStringComparator;import org.apache.hadoop.hbase.filter.RowFilter;import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;import org.apache.hadoop.hbase.util.Bytes;import org.junit.After;import org.junit.Before;import org.junit.Test;?public class HbaseTest {? /** * 配置ss */ static Configuration config = null; private Connection connection = null; private Table table = null;? @Before public void init() throws Exception { config = HBaseConfiguration.create();// 配置 config.set("hbase.zookeeper.quorum", "master,work1,work2");// zookeeper地址 config.set("hbase.zookeeper.property.clientPort", "2181");// zookeeper端口 connection = ConnectionFactory.createConnection(config); table = connection.getTable(TableName.valueOf("user")); }? /** * 創建一個表 * * @throws Exception */ @Test public void createTable() throws Exception { // 創建表管理類 HBaseAdmin admin = new HBaseAdmin(config); // hbase表管理 // 創建表描述類 TableName tableName = TableName.valueOf("test3"); // 表名稱 HTableDescriptor desc = new HTableDescriptor(tableName); // 創建列族的描述類 HColumnDescriptor family = new HColumnDescriptor("info"); // 列族 // 將列族添加到表中 desc.addFamily(family); HColumnDescriptor family2 = new HColumnDescriptor("info2"); // 列族 // 將列族添加到表中 desc.addFamily(family2); // 創建表 admin.createTable(desc); // 創建表 }? @Test @SuppressWarnings("deprecation") public void deleteTable() throws MasterNotRunningException, ZooKeeperConnectionException, Exception { HBaseAdmin admin = new HBaseAdmin(config); admin.disableTable("test3"); admin.deleteTable("test3"); admin.close(); }? /** * 向hbase中增加數據 * * @throws Exception */ @SuppressWarnings({ "deprecation", "resource" }) @Test public void insertData() throws Exception { table.setAutoFlushTo(false); table.setWriteBufferSize(534534534); ArrayList arrayList = new ArrayList(); for (int i = 21; i < 50; i++) { Put put = new Put(Bytes.toBytes("1234"+i)); put.add(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("wangwu"+i)); put.add(Bytes.toBytes("info"), Bytes.toBytes("password"), Bytes.toBytes(1234+i)); arrayList.add(put); } //插入數據 table.put(arrayList); //提交 table.flushCommits(); }? /** * 修改數據 * * @throws Exception */ @Test public void uodateData() throws Exception { Put put = new Put(Bytes.toBytes("1234")); put.add(Bytes.toBytes("info"), Bytes.toBytes("namessss"), Bytes.toBytes("lisi1234")); put.add(Bytes.toBytes("info"), Bytes.toBytes("password"), Bytes.toBytes(1234)); //插入數據 table.put(put); //提交 table.flushCommits(); }? /** * 刪除數據 * * @throws Exception */ @Test public void deleteDate() throws Exception { Delete delete = new Delete(Bytes.toBytes("1234")); table.delete(delete); table.flushCommits(); }? /** * 單條查詢 * * @throws Exception */ @Test public void queryData() throws Exception { Get get = new Get(Bytes.toBytes("1234")); Result result = table.get(get); System.out.println(Bytes.toInt(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("password")))); System.out.println(Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("namessss")))); System.out.println(Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("sex")))); }? /** * 全表掃描 * * @throws Exception */ @Test public void scanData() throws Exception { Scan scan = new Scan(); //scan.addFamily(Bytes.toBytes("info")); //scan.addColumn(Bytes.toBytes("info"), Bytes.toBytes("password")); scan.setStartRow(Bytes.toBytes("wangsf_0")); scan.setStopRow(Bytes.toBytes("wangwu")); ResultScanner scanner = table.getScanner(scan); for (Result result : scanner) { System.out.println(Bytes.toInt(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("password")))); System.out.println(Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")))); //System.out.println(Bytes.toInt(result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("password")))); //System.out.println(Bytes.toString(result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("name")))); } }? /** * 全表掃描的過濾器 * 列值過濾器 * * @throws Exception */ @Test public void scanDataByFilter1() throws Exception {? // 創建全表掃描的scan Scan scan = new Scan(); //過濾器:列值過濾器 SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes.toBytes("info"), Bytes.toBytes("name"), CompareFilter.CompareOp.EQUAL, Bytes.toBytes("zhangsan2")); // 設置過濾器 scan.setFilter(filter);? // 打印結果集 ResultScanner scanner = table.getScanner(scan); for (Result result : scanner) { System.out.println(Bytes.toInt(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("password"))));??????System.out.println(Bytes.toString(result.getValue(Bytes.toBytes("info"),?Bytes.toBytes("name")))); }? } /** * rowkey過濾器 * @throws Exception */ @Test public void scanDataByFilter2() throws Exception { // 創建全表掃描的scan Scan scan = new Scan(); //匹配rowkey以wangsenfeng開頭的 RowFilter filter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^12341")); // 設置過濾器 scan.setFilter(filter); // 打印結果集 ResultScanner scanner = table.getScanner(scan); for (Result result : scanner) { System.out.println(Bytes.toInt(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("password")))); System.out.println(Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")))); //System.out.println(Bytes.toInt(result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("password")))); //System.out.println(Bytes.toString(result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("name")))); }? } /** * 匹配列名前綴 * @throws Exception */ @Test public void scanDataByFilter3() throws Exception { // 創建全表掃描的scan Scan scan = new Scan(); //匹配rowkey以wangsenfeng開頭的 ColumnPrefixFilter filter = new ColumnPrefixFilter(Bytes.toBytes("na")); // 設置過濾器 scan.setFilter(filter); // 打印結果集 ResultScanner scanner = table.getScanner(scan); for (Result result : scanner) { System.out.println("rowkey:" + Bytes.toString(result.getRow())); System.out.println("info:name:" + Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")))); // 判斷取出來的值是否為空 if (result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")) != null) { System.out.println("info:age:" + Bytes.toInt(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")))); } // 判斷取出來的值是否為空 if (result.getValue(Bytes.toBytes("info"), Bytes.toBytes("sex")) != null) { System.out.println("infi:sex:" + Bytes.toInt(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("sex")))); } // 判斷取出來的值是否為空 if (result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("name")) != null) { System.out .println("info2:name:" + Bytes.toString(result.getValue( Bytes.toBytes("info2"), Bytes.toBytes("name")))); } // 判斷取出來的值是否為空 if (result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("age")) != null) { System.out.println("info2:age:" + Bytes.toInt(result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("age")))); } // 判斷取出來的值是否為空 if (result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("sex")) != null) { System.out.println("info2:sex:" + Bytes.toInt(result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("sex")))); } } } /** * 過濾器集合 * @throws Exception */ @Test public void scanDataByFilter4() throws Exception { // 創建全表掃描的scan Scan scan = new Scan(); //過濾器集合:MUST_PASS_ALL(and),MUST_PASS_ONE(or) FilterList filterList = new FilterList(Operator.MUST_PASS_ONE); //匹配rowkey以wangsenfeng開頭的 RowFilter filter = new RowFilter(CompareFilter.CompareOp.EQUAL, new RegexStringComparator("^wangsenfeng")); //匹配name的值等于wangsenfeng SingleColumnValueFilter filter2 = new SingleColumnValueFilter(Bytes.toBytes("info"), Bytes.toBytes("name"), CompareFilter.CompareOp.EQUAL, Bytes.toBytes("zhangsan")); filterList.addFilter(filter); filterList.addFilter(filter2); // 設置過濾器 scan.setFilter(filterList); // 打印結果集 ResultScanner scanner = table.getScanner(scan); for (Result result : scanner) { System.out.println("rowkey:" + Bytes.toString(result.getRow())); System.out.println("info:name:" + Bytes.toString(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name")))); // 判斷取出來的值是否為空 if (result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")) != null) { System.out.println("info:age:" + Bytes.toInt(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age")))); } // 判斷取出來的值是否為空 if (result.getValue(Bytes.toBytes("info"), Bytes.toBytes("sex")) != null) { System.out.println("infi:sex:" + Bytes.toInt(result.getValue(Bytes.toBytes("info"), Bytes.toBytes("sex")))); } // 判斷取出來的值是否為空 if (result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("name")) != null) { System.out .println("info2:name:" + Bytes.toString(result.getValue( Bytes.toBytes("info2"), Bytes.toBytes("name")))); } // 判斷取出來的值是否為空 if (result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("age")) != null) { System.out.println("info2:age:" + Bytes.toInt(result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("age")))); } // 判斷取出來的值是否為空 if (result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("sex")) != null) { System.out.println("info2:sex:" + Bytes.toInt(result.getValue(Bytes.toBytes("info2"), Bytes.toBytes("sex")))); } } }? @After public void close() throws Exception { table.close(); connection.close(); }?}五 HBASE運行原理
5.1 master職責
1.管理監控HRegionServer,實現其負載均衡。
2.處理region的分配或轉移,比如在HRegion split時分配新的HRegion;在HRegionServer退出時遷移其負責的HRegion到其他HRegionServer上。
3.處理元數據的變更
4.管理namespace和table的元數據(實際存儲在HDFS上)。
5.權限控制(ACL)。
6.監控集群中所有HRegionServer的狀態(通過Heartbeat和監聽ZooKeeper中的狀態)。
5.2 Region Server 職責
5.3 zookeeper集群所起作用
注:HMaster通過監聽ZooKeeper中的Ephemeral節點(默認:/hbase/rs/*)來監控HRegionServer的加入和宕機。
在第一個HMaster連接到ZooKeeper時會創建Ephemeral節點(默認:/hbasae/master)來表示Active的HMaster,其后加進來的HMaster則監聽該Ephemeral節點
如果當前Active的HMaster宕機,則該節點消失,因而其他HMaster得到通知,而將自身轉換成Active的HMaster,在變為Active的HMaster之前,它會在/hbase/masters/下創建自己的Ephemeral節點。
5.4 HBASE讀寫數據流程
5.4.1 寫數據流程
客戶端現在要插入一條數據,rowkey=r000001, 這條數據應該寫入到table表中的那個region中呢?
1/ 客戶端要連接zookeeper, 從zk的/hbase節點找到hbase:meta表所在的regionserver(host:port);
2/ regionserver掃描hbase:meta中的每個region的起始行健,對比r000001這條數據在那個region的范圍內;
3/ 從對應的 info:server key中存儲了region是有哪個regionserver(host:port)在負責的;
4/ 客戶端直接請求對應的regionserver;
5/ regionserver接收到客戶端發來的請求之后,就會將數據寫入到region中
5.4.2 讀數據流程
客戶端現在要查詢rowkey=r000001這條數據,那么這個流程是什么樣子的呢?
1/ 首先Client連接zookeeper, 找到hbase:meta表所在的regionserver;
2/ 請求對應的regionserver,掃描hbase:meta表,根據namespace、表名和rowkey在meta表中找到r00001所在的region是由那個regionserver負責的;
3/找到這個region對應的regionserver
4/ regionserver收到了請求之后,掃描對應的region返回數據到Client
(先從MemStore找數據,如果沒有,再到BlockCache里面讀;BlockCache還沒有,再到StoreFile上讀(為了讀取的效率);
如果是從StoreFile里面讀取的數據,不是直接返回給客戶端,而是先寫入BlockCache,再返回給客戶端。)
注:客戶會緩存這些位置信息,然而第二步它只是緩存當前RowKey對應的HRegion的位置,因而如果下一個要查的RowKey不在同一個HRegion中,則需要繼續查詢hbase:meta所在的HRegion,然而隨著時間的推移,客戶端緩存的位置信息越來越多,以至于不需要再次查找hbase:meta Table的信息,除非某個HRegion因為宕機或Split被移動,此時需要重新查詢并且更新緩存。
5.4.3 數據flush過程
1)當MemStore數據達到閾值(默認是128M,老版本是64M),將數據刷到硬盤,將內存中的數據刪除,同時刪除HLog中的歷史數據;
2)并將數據存儲到HDFS中;
3)在HLog中做標記點。
5.4.4 數據合并過程
1)當數據塊達到3塊,Hmaster觸發合并操作,Region將數據塊加載到本地,進行合并;
2)當合并的數據超過256M,進行拆分,將拆分后的Region分配給不同的HregionServer管理;
3)當HregionServer宕機后,將HregionServer上的hlog拆分,然后分配給不同的HregionServer加載,修改.META.;
4)注意:HLog會同步到HDFS。
5.5 hbase:meta表
hbase:meta表存儲了所有用戶HRegion的位置信息:
Rowkey:tableName,regionStartKey,regionId,replicaId等;
info列族:這個列族包含三個列,他們分別是:
info:regioninfo列:
regionId,tableName,startKey,endKey,offline,split,replicaId;
info:server列:HRegionServer對應的server:port;
info:serverstartcode列:HRegionServer的啟動時間戳。
5.6 Region Server內部機制
- WAL即Write Ahead Log,在早期版本中稱為HLog,它是HDFS上的一個文件,如其名字所表示的,所有寫操作都會先保證將數據寫入這個Log文件后,才會真正更新MemStore,最后寫入HFile中。WAL文件存儲在/hbase/WALs/${HRegionServer_Name}的目錄中
- BlockCache是一個讀緩存,即“引用局部性”原理(也應用于CPU,分空間局部性和時間局部性,空間局部性是指CPU在某一時刻需要某個數據,那么有很大的概率在一下時刻它需要的數據在其附近;時間局部性是指某個數據在被訪問過一次后,它有很大的概率在不久的將來會被再次的訪問),將數據預讀取到內存中,以提升讀的性能。
- HRegion是一個Table中的一個Region在一個HRegionServer中的表達。一個Table可以有一個或多個Region,他們可以在一個相同的HRegionServer上,也可以分布在不同的HRegionServer上,一個HRegionServer可以有多個HRegion,他們分別屬于不同的Table。HRegion由多個Store(HStore)構成,每個HStore對應了一個Table在這個HRegion中的一個Column Family,即每個Column Family就是一個集中的存儲單元,因而最好將具有相近IO特性的Column存儲在一個Column Family,以實現高效讀取(數據局部性原理,可以提高緩存的命中率)。HStore是HBase中存儲的核心,它實現了讀寫HDFS功能,一個HStore由一個MemStore 和0個或多個StoreFile組成。
- MemStore是一個寫緩存(In Memory Sorted Buffer),所有數據的寫在完成WAL日志寫后,會 寫入MemStore中,由MemStore根據一定的算法將數據Flush到地層HDFS文件中(HFile),通常每個HRegion中的每個 Column Family有一個自己的MemStore。
- HFile(StoreFile) 用于存儲HBase的數據(Cell/KeyValue)。在HFile中的數據是按RowKey、Column Family、Column排序,對相同的Cell(即這三個值都一樣),則按timestamp倒序排列。
- FLUSH詳述
六.HBASE優化
6.1 高可用
在HBase中Hmaster負責監控RegionServer的生命周期,均衡RegionServer的負載,如果Hmaster掛掉了,那么整個HBase集群將陷入不健康的狀態,并且此時的工作狀態并不會維持太久。所以HBase支持對Hmaster的高可用配置。
1.關閉HBase集群(如果沒有開啟則跳過此步)
bin/stop-hbase.sh2.在conf目錄下創建backup-masters文件
touch conf/backup-masters3.在backup-masters文件中配置高可用HMaster節點
echo?hadoop110?>?conf/backup-masters4.將整個conf目錄scp到其他節點
scp?-r?conf/?hadoop110:/opt/module/hbase/6.2 預分區
每一個region維護著startRow與endRowKey,如果加入的數據符合某個region維護的rowKey范圍,則該數據交給這個region維護。那么依照這個原則,我們可以將數據所要投放的分區提前大致的規劃好,以提高HBase性能。
1.手動設定預分區
hbase> create 'staff1','info','partition1',SPLITS => ['1000','2000','3000','4000']2.生成16進制序列預分區
create 'staff2','info','partition2',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}3.按照文件中設置的規則預分區
創建splits.txt文件內容如下:
aaaa
bbbb
cccc
dddd
然后執行:
create 'staff3','partition3',SPLITS_FILE => 'splits.txt'6.3 RowKey設計
一條數據的唯一標識就是rowkey,那么這條數據存儲于哪個分區,取決于rowkey處于哪個一個預分區的區間內,設計rowkey的主要目的 ,就是讓數據均勻的分布于所有的region中,在一定程度上防止數據傾斜。接下來我們就談一談rowkey常用的設計方案。
1.生成隨機數、hash、散列值
比如:
原本rowKey為1001的,SHA1后變成:dd01903921ea24941c26a48f2cec24e0bb0e8cc7
原本rowKey為3001的,SHA1后變成:49042c54de64a1e9bf0b33e00245660ef92dc7bd
原本rowKey為5001的,SHA1后變成:7b61dec07e02c188790670af43e717f0f46e8913
在做此操作之前,一般我們會選擇從數據集中抽取樣本,來決定什么樣的rowKey來Hash后作為每個分區的臨界值。
2.字符串反轉
20170524000001轉成10000042507102
20170524000002轉成20000042507102
3.字符串拼接
20170524000001_a12e
20170524000001_93i7
6.4 內存優化
HBase操作過程中需要大量的內存開銷,畢竟Table是可以緩存在內存中的,一般會分配整個可用內存的70%給HBase的Java堆。但是不建議分配非常大的堆內存,因為GC過程持續太久會導致RegionServer處于長期不可用狀態,一般16~48G內存就可以了,如果因為框架占用內存過高導致系統內存不足,框架一樣會被系統服務拖死。
6.5 基礎優化
1.允許在HDFS的文件中追加內容
hdfs-site.xml、hbase-site.xml
屬性:dfs.support.append
解釋:開啟HDFS追加同步,可以優秀的配合HBase的數據同步和持久化。默認值為true。
2.優化DataNode允許的最大文件打開數
hdfs-site.xml
屬性:dfs.datanode.max.transfer.threads
解釋:HBase一般都會同一時間操作大量的文件,根據集群的數量和規模以及數據動作,設置為4096或者更高。默認值:4096
3.優化延遲高的數據操作的等待時間
hdfs-site.xml
屬性:dfs.image.transfer.timeout
解釋:如果對于某一次數據操作來講,延遲非常高,socket需要等待更長的時間,建議把該值設置為更大的值(默認60000毫秒),以確保socket不會被timeout掉。
4.優化數據的寫入效率
mapred-site.xml
屬性:
mapreduce.map.output.compress
mapreduce.map.output.compress.codec
解釋:開啟這兩個數據可以大大提高文件的寫入效率,減少寫入時間。第一個屬性值修改為true,第二個屬性值修改為:org.apache.hadoop.io.compress.GzipCodec或者其他壓縮方式。
5.設置RPC監聽數量
hbase-site.xml
屬性:hbase.regionserver.handler.count
解釋:默認值為30,用于指定RPC監聽的數量,可以根據客戶端的請求數進行調整,讀寫請求較多時,增加此值。
6.優化HStore文件大小
hbase-site.xml
屬性:hbase.hregion.max.filesize
解釋:默認值10737418240(10GB),如果需要運行HBase的MR任務,可以減小此值,因為一個region對應一個map任務,如果單個region過大,會導致map任務執行時間過長。該值的意思就是,如果HFile的大小達到這個數值,則這個region會被切分為兩個Hfile。
7.優化hbase客戶端緩存
hbase-site.xml
屬性:hbase.client.write.buffer
解釋:用于指定HBase客戶端緩存,增大該值可以減少RPC調用次數,但是會消耗更多內存,反之則反之。一般我們需要設定一定的緩存大小,以達到減少RPC次數的目的。
8.指定scan.next掃描HBase所獲取的行數
hbase-site.xml
屬性:hbase.client.scanner.caching
解釋:用于指定scan.next方法獲取的默認行數,值越大,消耗內存越大。
9.flush、compact、split機制
當MemStore達到閾值,將Memstore中的數據Flush進Storefile;compact機制則是把flush出來的小文件合并成大的Storefile文件。split則是當Region達到閾值,會把過大的Region一分為二。
涉及屬性:
即:128M就是Memstore的默認閾值
hbase.hregion.memstore.flush.size:134217728hbase.regionserver.global.memstore.upperLimit:0.4hbase.regionserver.global.memstore.lowerLimit:0.38即:
當MemStore使用內存總量達到hbase.regionserver.global.memstore.upperLimit指定值時,將會有多個MemStores flush到文件中,MemStore flush 順序是按照大小降序執行的,直到刷新到MemStore使用內存略小于lowerLimit即:這個參數的作用是當單個HRegion內所有的Memstore大小總和超過指定值時,flush該HRegion的所有memstore。RegionServer的flush是通過將請求添加一個隊列,模擬生產消費模型來異步處理的。那這里就有一個問題,當隊列來不及消費,產生大量積壓請求時,可能會導致內存陡增,最壞的情況是觸發OOM。
總結
以上是生活随笔為你收集整理的/opt/hbase/conf 中不能启动hbase_Hbase从入门到入坑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网易邮箱大师怎么添加邮箱(网易游戏官网)
- 下一篇: idea mybatis插件_Intel