HBase實驗


承接上一篇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。
  • 如果還有繼續更新

人生此處,絕對樂觀


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM