承接上一篇HDFS實驗,下一篇,NoSQL實驗,學習入門課后的實驗真的是以簡單為主,后續估計得每個組件一一擊破。
Hbase介紹
Hbase是一個分布式的、面向列的開源數據庫,源於Google的一篇論文《BigTable:一個結構化的數據的分布式存儲系統》。HBase中確定一個元素,需要提供表名,行,列族名,列。因為是以列為單位,所以動態增刪數據的性能特別好。
安裝配置Hbase
基礎環境
hadoop version:2.7.7
java version:1.8
Hbase選擇
Hbase的版本需要與hadoop版本對應,我使用的是Hbase-1.2.1,基本上沒出差錯。版本對應鏈接
Hbase的下載可以去官網直接進行下載。
安裝
-
解壓安裝包
sudo tar -zxf /mnt/hgfs/windowShare/hbase-1.2.1-bin.tar.gz -C /usr/local sudo mv /usr/local/hbase-1.2.1 /usr/local/hbase # 改名方便使用
-
配置環境變量
vim ~/.bashrc # 針對當前用戶修改 export Path=$PATh:/usr/local/hbase/bin # 在文件中添加環境變量的最后一行進行修改,保存退出 source ~/.bashrc # 立即生效
-
配置Hbase權限
sudo chown -R hadoop /usr/local/hbase # 將hbase下所有文件的所有者改為hadoop
-
查看版本,確定Hbase安裝成功
hbase version
偽分布式配置
-
/usr/local/hbase/conf/hbase-env.sh
vim /usr/local/hbase/conf/hbase-env.sh
export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_251/bin # Jkd的安裝環境 export HBASE_CLASSPATH=/usr/local/hadoop/conf # hadoop的本地安裝conf目錄 export HBASE_MANAGES_ZK=true
-
/usr/local/hbase/conf/hbase-site.xml
gedit /usr/local/hbase/conf/hbase-site.xml
<configuration> <property> <name>hbase.rootdir</name> <value>hdfs://localhost:9000/hbase</value> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> </configuration>
hbase.rootdir:Hbase數據在HDFS上的存儲路徑
hbase.cluster.distributed:true,分布式配置
-
測試hbase
首先啟動hdfs,然后再啟動hbase
start-dfs.sh # 啟動hdfs start-hbase.sh # 啟動hbase jps # 查看是否成功啟動 hbase shell # 啟動hbase shell
最重要的步驟
在虛擬機記得快照,快照,快照。
雙系統的記得備份,備份,備份。
HBase實驗
1. 編程實現以下指定功能,並用Hadoop提供的HBase Shell命令完成相同任務:
總體代碼,每一小題只需要編寫相應的函數。
import java.io.IOException;
import java.util.Scanner;
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.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
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.HBaseAdmin;
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.util.Bytes;
public class Prac1 {
private static Configuration conf;
private static Connection conn;
private static Admin admin;
public static void main(String[] args) throws IOException {
init();
int cmd=5;//對應的題目
switch(cmd) {
case 1:list();break;
case 2:showTable();break;
case 3:choose();break;
case 4:deleteAll();break;
case 5:countRows();break;
}
close();
}
public static void init()
{
conf = HBaseConfiguration.create();
conf.set("hbase.rootdir", "hdfs://localhost:9000/hbase");
try {
conn = ConnectionFactory.createConnection(conf);
admin = conn.getAdmin();
}catch(IOException e) {
e.printStackTrace();
}
}
public static void close()
{
try {
if(admin!=null)
admin.close();
if(conn!=null)
conn.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
(1) 列出HBase所有的表的相關信息,例如表名;
shell:
list # 打印表的信息
java:
public static void list() throws IOException
{
HTableDescriptor htds[] = admin.listTables();
for(HTableDescriptor htd:htds)
System.out.println(htd.getNameAsString());
System.out.println("輸出表信息完成");
}
(2) 在終端打印出指定的表的所有記錄數據;
shell:
scan 'student'
java:
public static void showTable() throws IOException
{
System.out.print("請輸入表名: ");
Scanner input = new Scanner(System.in);
String tableName = input.next();
Table table = conn.getTable(TableName.valueOf(tableName));
if(table==null) {
System.out.println("不存在該表");
return ;
}
Scan scan = new Scan();
ResultScanner rsr = table.getScanner(scan); //遍歷列
for(Result res:rsr)
showCell(res);
table.close();
}
public static void showCell(Result res)
{
//一行的內容也要用for來輸出,因為是以列為單位的
Cell[] cells = res.rawCells();
for(Cell cell:cells) {
System.out.println("RowName: "+new String(CellUtil.cloneRow(cell))+"\t"
+"TimeStamp: "+cell.getTimestamp()+"\t"
+"Column Family: "+new String(CellUtil.cloneFamily(cell))+"\t"
+"Row Name: "+new String(CellUtil.cloneQualifier(cell))+"\t"
+"Value: "+new String(CellUtil.cloneValue(cell)));
}
}
(3) 向已經創建好的表添加和刪除指定的列族或列;
shell:
# 添加列和列族,但是列是確定的,列族不是
put 'student','95002','Sname','Mari'
put 'student','95002','course:Chinese','150'
#刪除列和列族
delete 'student','95002','Sname'
delete 'student','95002','course:Chinese'
java:
public static void choose() throws IOException
{
System.out.print("請輸入操作(insert or delete): ");
Scanner input = new Scanner(System.in);
String cmd = input.next();
if(cmd.equals("insert")||cmd.equals("delete"))
option(cmd);
else
System.out.println("錯誤命令");
}
public static void option(String opt) throws IOException
{
System.out.print("請輸入表、行、列族、列、值(space): ");
Scanner input = new Scanner(System.in);
String tableName = input.next().replaceFirst("esc","");
String rowKey = input.next().replaceFirst("esc","");
String colFamily = input.next().replaceFirst("esc","");
String col = input.next().replaceFirst("esc","");
Table table = conn.getTable(TableName.valueOf(tableName));
if(table==null){
System.out.println("該表不存在");
return;
}
if(opt.equals("insert"))
{
String val = input.next().replaceFirst("esc", "");
Put put = new Put(rowKey.getBytes());
put.addColumn(colFamily.getBytes(), col.getBytes(), val.getBytes());
table.put(put);
}else
{
Delete delete = new Delete(rowKey.getBytes());
if(!colFamily.equals("")&&col.equals(""))
delete.addColumn(colFamily.getBytes(),null);
else if(!colFamily.equals("")&&!col.equals(""))
delete.addColumn(colFamily.getBytes(),col.getBytes());
else if(colFamily.equals("")&&!col.equals("")) {
System.out.println("不存在無列族只有列的情況");
table.close();
return;
}
table.delete(delete);
}
System.out.println("操作完成");
table.close();
}
(4) 清空指定的表的所有記錄數據;
shell:
truncate 'student'
java:
public static void deleteAll() throws IOException
{
System.out.print("請輸入刪除內容的表名: ");
Scanner input = new Scanner(System.in);
String tableName = input.next();
HBaseAdmin tempAdmin = new HBaseAdmin(conf);
HTableDescriptor htd = tempAdmin.getTableDescriptor(Bytes.toBytes(tableName));
//獲取這個表的信息
if(htd==null) {
System.out.println("不存在該表");
return ;
}
TableName table_name = TableName.valueOf(tableName);
admin.disableTable(table_name);
admin.deleteTable(table_name);
admin.createTable(htd);
System.out.println("成功刪除內容");
tempAdmin.close();
}
(5) 統計表的行數。
shell:
count 'student'
java:
public static void countRows() throws IOException
{
System.out.print("請輸入查詢的表名: ");
Scanner input = new Scanner(System.in);
String tableName = input.next();
Table table = conn.getTable(TableName.valueOf(tableName));
if(table==null) {
System.out.println("不存在該表");
return ;
}
Scan scan = new Scan();
ResultScanner rsr = table.getScanner(scan);
int count=0;
for(Result res:rsr)
count++;
System.out.println("共有"+count+"行");
}
2. 現有以下關系型數據庫中的表和數據,要求將其轉換為適合於HBase存儲的表並插入數據:
學生表(Student)
學號(S_No) | 姓名(S_Name) | 性別(S_Sex) | 年齡(S_Age) |
---|---|---|---|
2015001 | Zhangsan | male | 23 |
2015002 | Mary | female | 22 |
2015003 | Lisi | male | 24 |
課程表(Course)
課程號(C_No) | 課程名(C_Name) | 學分(C_Credit) |
---|---|---|
123001 | Math | 2.0 |
123002 | Computer Science | 5.0 |
123003 | English | 3.0 |
選課表(SC)
學號(SC_Sno) | 課程號(SC_Cno) | 成績(SC_Score) |
---|---|---|
2015001 | 123001 | 86 |
2015001 | 123003 | 69 |
2015002 | 123002 | 77 |
2015002 | 123003 | 99 |
2015003 | 123001 | 98 |
2015003 | 123002 | 95 |
同時,請編程完成以下指定功能:
(1)createTable(String tableName, String[] fields)
創建表,參數tableName為表的名稱,字符串數組fields為存儲記錄各個域名稱的數組。要求當HBase已經存在名為tableName的表的時候,先刪除原有的表,然后再創建新的表。
(2)addRecord(String tableName, String row, String[] fields, String[] values)
向表tableName、行row(用S_Name表示)和字符串數組files指定的單元格中添加對應的數據values。其中fields中每個元素如果對應的列族下還有相應的列限定符的話,用“columnFamily:column”表示。例如,同時向“Math”、“Computer Science”、“English”三列添加成績時,字符串數組fields為{“Score:Math”,”Score;Computer Science”,”Score:English”},數組values存儲這三門課的成績。
(3)scanColumn(String tableName, String column)
瀏覽表tableName某一列的數據,如果某一行記錄中該列數據不存在,則返回null。要求當參數column為某一列族名稱時,如果底下有若干個列限定符,則要列出每個列限定符代表的列的數據;當參數column為某一列具體名稱(例如“Score:Math”)時,只需要列出該列的數據。
(4)modifyData(String tableName, String row, String column)
修改表tableName,行row(可以用學生姓名S_Name表示),列column指定的單元格的數據。
(5)deleteRow(String tableName, String row)
刪除表tableName中row指定的行的記錄。
在導入數據的時候,我是利用程序導入的,改編了一下第一題的所有程序,如下:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Scanner;
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.TableName;
import org.apache.hadoop.hbase.client.Admin;
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.HBaseAdmin;
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.util.Bytes;
public class Prac2 {
private static Configuration conf;
private static Connection conn;
private static Admin admin;
public static void main(String[] args) throws IOException {
init();
String filenamePrefix="/home/hadoop/Desktop/HPractice/HbasePractice/";
String[] t1 = {"S_No","S_Name","S_Sex","S_Age"};
String t1Name = "Student";
createTable(t1Name, t1);
lead(filenamePrefix+t1Name.toLowerCase()+".txt",t1Name,t1,false);
String[] t2 = {"C_No","C_Name","C_Credit"};
String t2Name = "Course";
createTable(t2Name, t2);
lead(filenamePrefix+t2Name.toLowerCase()+".txt",t2Name,t2,false);
String[] t3 = {"SC_Sno","SC_Cno","SC_score"};
String t3Name = "SC";
createTable(t3Name, t3);
lead(filenamePrefix+t3Name.toLowerCase()+".txt",t3Name,t3,true);
close();
}
public static void lead(String filename,String tableName,String[] colFamilys,boolean dupl) throws IOException
{
Table table = conn.getTable(TableName.valueOf(tableName));
if(table==null){
System.out.println("該表不存在");
return;
}
FileInputStream is = new FileInputStream(filename);
BufferedReader bfr = new BufferedReader(new InputStreamReader(is));
String line;
String rowKey,colFamily,col,val;
int j;
int count=1;
while((line = bfr.readLine())!=null)
{
String[] values = line.split(" ");
if(values.length!=colFamilys.length)
{
System.out.println(count+"行讀取失敗");
count++;
continue;
}
if(!dupl)
{
rowKey = values[0];
j=1;
}else
{
rowKey = String.valueOf(count);
j=0;
}
for(int i=j;i<values.length;i++)
{
if(colFamilys[i].contains(":"))
{
colFamily = colFamilys[i].split(":")[0];
col = colFamilys[i].split(":")[1];
}else {
colFamily = colFamilys[i];
col = "";
}
option(table,"insert",rowKey,colFamily,col,values[i]);
}
count++;
}
table.close();
System.out.println("導入"+tableName+"成功");
}
public static void init()
{
conf = HBaseConfiguration.create();
conf.set("hbase.rootdir", "hdfs://localhost:9000/hbase");
try {
conn = ConnectionFactory.createConnection(conf);
admin = conn.getAdmin();
}catch(IOException e) {
e.printStackTrace();
}
}
public static void close()
{
try {
if(admin!=null)
admin.close();
if(conn!=null)
conn.close();
}catch(IOException e) {
e.printStackTrace();
}
}
public static void createTable(String tableName,String[] colFamily) throws IOException
{
HTableDescriptor htd = null;
TableName table = TableName.valueOf(tableName);
if(admin.tableExists(table))
{
System.out.println("表已經存在");
return ;
}else
{
htd = new HTableDescriptor(table);
}
String realInfo;
for(String info:colFamily)
{
if(info.contains(":"))
realInfo = info.split(":")[0];
else
realInfo = info;
htd.addFamily(new HColumnDescriptor(new String(realInfo)));
}
admin.createTable(htd);
System.out.println("成功創建");
}
public static void list() throws IOException
{
HTableDescriptor htds[] = admin.listTables();
for(HTableDescriptor htd:htds)
System.out.println(htd.getNameAsString());
System.out.println("輸出表信息完成");
}
public static void showTable(String tableName) throws IOException
{
Table table = conn.getTable(TableName.valueOf(tableName));
if(table==null) {
System.out.println("不存在該表");
return ;
}
Scan scan = new Scan();
ResultScanner rsr = table.getScanner(scan); //遍歷列
for(Result res:rsr)
showCell(res);
table.close();
}
public static void showCell(Result res)
{
//一行的內容也要用for來輸出,因為是以列為單位的
Cell[] cells = res.rawCells();
for(Cell cell:cells) {
System.out.println("RowName: "+new String(CellUtil.cloneRow(cell))+"\t"
+"TimeStamp: "+cell.getTimestamp()+"\t"
+"Column Family: "+new String(CellUtil.cloneFamily(cell))+"\t"
+"Row Name: "+new String(CellUtil.cloneQualifier(cell))+"\t"
+"Value: "+new String(CellUtil.cloneValue(cell)));
}
}
public static void option(Table table,String opt,String rowKey,String colFamily,String col,String val) throws IOException
{
if(opt.equals("insert"))
{
Put put = new Put(rowKey.getBytes());
put.addColumn(colFamily.getBytes(), col.getBytes(), val.getBytes());
table.put(put);
}else
{
Delete delete = new Delete(rowKey.getBytes());
if(!colFamily.equals("")&&col.equals(""))
delete.addColumn(colFamily.getBytes(),null);
else if(!colFamily.equals("")&&!col.equals(""))
delete.addColumn(colFamily.getBytes(),col.getBytes());
else if(colFamily.equals("")&&!col.equals("")) {
System.out.println("不存在無列族只有列的情況");
table.close();
return;
}
table.delete(delete);
}
System.out.println("操作完成");
}
public static void deleteAll(String tableName) throws IOException
{
HBaseAdmin tempAdmin = new HBaseAdmin(conf);
HTableDescriptor htd = tempAdmin.getTableDescriptor(Bytes.toBytes(tableName));
//獲取這個表的信息
if(htd==null) {
System.out.println("不存在該表");
return ;
}
TableName table_name = TableName.valueOf(tableName);
admin.disableTable(table_name);
admin.deleteTable(table_name);
admin.createTable(htd);
System.out.println("成功刪除內容");
tempAdmin.close();
}
public static void countRows(String tableName) throws IOException
{
Table table = conn.getTable(TableName.valueOf(tableName));
if(table==null) {
System.out.println("不存在該表");
return ;
}
Scan scan = new Scan();
ResultScanner rsr = table.getScanner(scan);
int count=0;
for(Result res:rsr)
count++;
System.out.println("共有"+count+"行");
}
}
結果如下:
沒有想到的是基本上是完成接下來的編程任務,只需要改一改形參和微調下代碼就好了,比如說deleteRow。刪除一行的記錄,可以采用下面的方式:
String rowKey = "???"//你想刪除的那一行行鍵
tablename = TableName.valueOf(tableName);
if(!admin.tableExists(tablename))
{
System.out.println("不存在該表");
exit(1);
}
Table table = conn.getTable(tablename);
option(table,"delete",rowKey,"","","");
table.close();
同時,我也編寫了一些完成以上任務的函數,有scanColumn和modifyData:
public static void scanColumn(String tableName,String columns) throws IOException
{
TableName tablename = TableName.valueOf(tableName);
if(!admin.tableExists(tablename)) {
System.out.println("不存在該表");
return;
}
Table table = conn.getTable(tablename);
Scan scan = new Scan();
//scan 對列的要求,ResultScanner是對行的遍歷,如果scan為空的話,showCell中的cells就是所有的列族和里面的列,反之cells則是確定的列族或者列
String[] info = columns.split(":");
if(info.length==1)
scan.addFamily(columns.getBytes());
else
scan.addColumn(info[0].getBytes(), info[1].getBytes());
ResultScanner rsr = table.getScanner(scan);
for(Result res = rsr.next();res!=null;res = rsr.next())
showCell(res);
}
public static void modifyData(String tableName,String row,String column) throws IOException
{
TableName tablename = TableName.valueOf(tableName);
if(!admin.tableExists(tablename))
{
System.out.println("該表不存在");
return ;
}
System.out.print("輸入要修改成的值: ");
Scanner input = new Scanner(System.in);
String val = input.next();
Table table = conn.getTable(tablename);
Put put = new Put(row.getBytes());
String[] info = column.split(":");
if(info.length==1)
put.addColumn(info[0].getBytes(),"".getBytes(), val.getBytes());
else
put.addColumn(info[0].getBytes(),info[1].getBytes(), val.getBytes());
table.put(put);
System.out.println("修改成功");
table.close();
}
這些都是我經過驗證了的,如果有錯誤,期待大佬指出。
3. 利用HBase和MapReduce完成如下任務:
假設HBase有2張表,表的邏輯視圖及部分數據如下所示:
表 邏輯視圖及部分數據
書名(bookName) | 價格(price) |
---|---|
Database System Concept | 30$ |
Thinking in Java | 60$ |
Data Mining | 25$ |
要求:從HBase讀出上述兩張表的數據,對“price”的排序,並將結果存儲到HBase中。
create 'book','price','bookname'
put 'book','30$','bookname','Datavase System Concept'
put 'book','60$','bookname','Thinking in Java'
put 'book','25$','bookname','Data Mining'
# scan 一個表的時候就會自動對行鍵進行排序
scan 'book'
總結
總結一下學到的東西。
- HTableDescriptor是Hbase表的描述子,獲取這個信息后就可以創建相同的表。
- scan是選擇列的可以不加上rowKey,而get必須加上rowKey。
- ResultScanner是對Result,也就是對行的遍歷,如果ResultScanner中的scan沒加任何條件那么每個Result中每個單位,也就是列,列族都會被選出來,反之加了條件,每個Result就會選出條件的列或者返回null。
如果還有繼續更新