一、關鍵字
分佈式存儲系統、開源、基於列模式、適合非結構化
二、特性
海量存儲,數據可達PB級別
列式存儲,一種NoSql數據庫
極易擴展,分佈式數據庫
高併發,多個PC同時處理
稀疏,指HBase列的靈活性,可以指定任意多的列
三、什麼適合用HBase?
HBase不適合解決所有的問題:
數據庫量要足夠多, 如果有十億及百億行數據, 那麼HBase是一個很好的選項, 如果只有幾百萬行甚至不到的數據量,RDBMS是一個很好的選擇, 因爲數據量小的話, 真正能工作的機器少, 剩餘的機器處於空閒的狀態
不需要輔助索引,靜態類型的列,事務等特性
保證硬件資源足夠,集羣中節點數少於5個,都不能表現的很好
四、應用場景
-
存儲業務數據:不同小區的人口信息、個人的定位信息等
-
存儲日誌數據:登錄日誌、中間件訪問日誌、推送日誌、業務操作日誌等信息
-
存儲業務附件:圖像、視頻、文檔等附件信息
五、HBase架構體系
-
HMaster: 集羣中的主服務器, 負責監控集羣中所有的RegionServer,並且管理着所有的元數據
-
HRegionServer: 管理Regions,每個RegionServer有多個Region, 運行在DataNode上
-
Store: Region有多個Store,每個Store對應表中一個列族
-
Memstore: 一個Store有一個Memstore和多個StoreFile(HFile)
-
StoreFile: 底層是Block
-
HFile: 真實的數據存儲文件
-
HLog: 預寫日誌文件,也叫WAL
六、應用開發
-
批量創建表
在創建表時只要指定表名、列簇就可以創建了, 當然在實際生產環境中通常會根據業務場景指定好表的預分區,這樣可以防止數據存入到一個Region上,造成熱點問題。
/* 判斷表是否存在 */
def isExists(tableName: String): Boolean = {
var result = false
val tName = TableName.valueOf(tableName)
if (admin.tableExists(tName)) {
result = true
}
result
}
def createTable(tableName: String, columnFamilys: Array[String]) = {
//操作的表名
val tName = TableName.valueOf(tableName)
//當表不存在的時候創建Hbase表
if (!admin.tableExists(tName)) {
//創建Hbase表模式
val descriptor = new HTableDescriptor(tName)
//創建列簇i
for (columnFamily <- columnFamilys) {
descriptor.addFamily(new HColumnDescriptor(columnFamily))
}
//創建表
admin.createTable(descriptor)
//建立分區表,使數據均勻分佈到各個服務器上
val splitKesy = Array(2, 4, 6, 8).map(Bytes.toBytes(_))
admin.createTable(descriptor, splitKesy)
println("create successful!!")
}
}
2. 數據寫入
數據寫入可以單條寫入也可以批量寫入, 實際環境中通常是批量寫入,這樣會提高寫入效率
//向hbase表中插入數據, 更新操作一樣
def insertTable(tableName: String, rowkey: String, columnFamily: String, column: String, value: String) = {
val table = connection.getTable(TableName.valueOf(tableName))
//準備key 的數據
val puts = new Put(rowkey.getBytes())
//添加列簇名,字段名,字段值value
puts.addColumn(columnFamily.getBytes(), column.getBytes(), value.getBytes())
//把數據插入到tbale中
table.put(puts)
table.close()
println("insert successful!!")
}
3. 數據更新
數據的更新和數據插入代碼是一樣的, 邏輯是不存在的列會創建插入, 存在則會覆蓋掉原有的數據。
//向hbase表中插入數據, 更新操作一樣
def insertTable(tableName: String, rowkey: String, columnFamily: String, column: String, value: String) = {
val table = connection.getTable(TableName.valueOf(tableName))
//準備key 的數據
val puts = new Put(rowkey.getBytes())
//添加列簇名,字段名,字段值value
puts.addColumn(columnFamily.getBytes(), column.getBytes(), value.getBytes())
//把數據插入到tbale中
table.put(puts)
table.close()
println("insert successful!!")
}
4. 數據刪除
數據刪除就是根據表名、rowKey、還有對應的列簇和列刪除即可。
//刪除某條記錄
def deleteRecord(tableName: String, rowkey: String, columnFamily: String, column: String) = {
val table = connection.getTable(TableName.valueOf(tableName))
val info = new Delete(Bytes.toBytes(rowkey))
info.addColumn(columnFamily.getBytes(), column.getBytes())
table.delete(info)
table.close()
println("delete successful!!")
}
5. 數據查詢
數據查詢通常根據rowKey範圍進行掃描查詢,這樣效率最高
//獲取hbase表中的數據
def scanDataFromHTable(tableName: String, columnFamily: String, column: String) = {
val table = connection.getTable(TableName.valueOf(tableName))
//定義scan對象
val scan = new Scan()
//添加列簇名稱
scan.addFamily(columnFamily.getBytes())
//從table中抓取數據來scan
val scanner = table.getScanner(scan)
var result = scanner.next()
//數據不爲空時輸出數據
while (result != null) {
println(s"rowkey:${Bytes.toString(result.getRow)},列簇:${columnFamily}:${column},value:${Bytes.toString(result.getValue(Bytes.toBytes(columnFamily), Bytes.toBytes(column)))}")
result = scanner.next()
}
//通過scan取完數據後,記得要關閉ResultScanner,否則RegionServer可能會出現問題(對應的Server資源無法釋放)
scanner.close()
table.close()
}
查詢優化的點有
-
設置Scan緩存, 這樣能有效提升掃描查詢性能
-
指定要查詢的列, 這樣很大程度減少
以上就是本文的全部內容。