2012年9月28日 星期五

自問自答之為什麼有了HIVE還要HBASE?

純紀錄而已不爽看不要看


問:


Hive可以直接用於Hadoop,為什麼還要在Hadoop和Hive之間加上Hbase呢?是因為有啥好處?


答:


1.HIVE可以"直接"讀取HDFS數據資料,但是HDFS"不支援"隨機讀取,但HBASE支援
2.HIVE只是一個簡化MR撰寫程式的一個工具, 只是資料倉儲中的一個DB
3.HBASE是NOSQL資料庫, 可以處理"事務"
4.HBASE提供高性能的key/value查詢

似乎可以處理"事務"比較屌的樣子, 還有很多答案吧, 以後看到再繼續補上去

2012年9月14日 星期五

Hbase連結Eclipse

前言
  再說一萬次我也不閒煩, 如果我身份證的父親欄是寫"鍋台名", 我還會晚上不睡覺在研究Hbase怎麼跟Eclipse連接起來嗎?
  寫這篇網誌跟上一篇中間過程已經先作掉"動物園管理員"了,這有空再補上去吧。我為啥要補這篇先呢?因為Google了整晚,把手邊可以參考的兩本書翻爛了,就是搞不定"KeeperErrorCode = ConnectionLoss for /hbase/master"這一個Exception。當看到console不再跑紅字並且連通的瞬間我眼淚都差點要噴出來了,所以再爆肝我都要先把熱騰騰的記憶寫上來,雖然這篇篇幅不長, 還是讓我們繼續看下去吧

環境
上一篇一樣

實作
  Client端要與Hbase叢集互動,有幾種方式可以選擇:Java, Arvo, REST, Thrift。是的,在下只會Java。Hbase的資料庫是建立在HDFS上的,還記得hbase-site.xml有個屬性的value是hdfs://UD1204:9000/hbase嗎?待會可以從程式執行成功後可以從Eclipse裡或是terminal中看到
  待會要實作的就類似JDBC, 透過JAVA CODE存取資料庫, Hbase也有提供這樣的API,不過詳述底層API不是本篇的主旨,而是要利用Eclipse跟Hbase作連接,使JAVA CODE可以直接存取在HDFS中的Hbase資料庫
  首先要把Hbase相關的lib都add到project裡面,見下圖
Project按右鍵-->Build Path-->Configuration Build Path..-->Libraries-->Add External JARs.. 路徑選到${HBASE_HOME}/lib,把全部的jar檔都加進去, 再回到${HBASE_HOME}下還有兩個jar檔別忘記了



接下來的重頭戲就是要把Hbase的設定檔也加入Eclipse,不然執行程式時會連接不到你的master。步驟就是Project按右鍵-->Build Path-->Configuration Build Path..-->Libraries-->Add Variable..(跟上面加入jar的步驟一樣)
到了下面這畫面選擇Configure Variables..再選擇NEW, 會跳出一個對話框, 這就是要讓你加入Hbase設定檔的路徑,請見下圖


這對話框的Name隨便你取, Path指定到${HBASE_HOME}/bin就好了
按下OKOKOKOKOKOKOK......以上設定就結束了,下面用一個Create Table的程式馬來測試一下。至於API就不詳述如何使用, 改天(我搞清楚的那一天XD)再另外開一篇blog說明吧

以下這段code貼到eclipse中,記得hadoop跟hbase都要啟動, 再run as hadoop
console中會出現Table: ex1TableExisted. 1. create table :ex1Table這段字就代表新增成功。至於上面有些紅色的錯誤訊息,我還在研究那是啥原因跑出來的



import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.util.GenericOptionsParser;

public class CreateTable {
public static void createHBaseTable(String tablename, String family)
      throws IOException {
    // HTableDescriptor 用來描述table的屬性
    HTableDescriptor htd = new HTableDescriptor(tablename);

    // HTableDescriptor 透過 add() 方法來加入Column family
    htd.addFamily(new HColumnDescriptor(family));

    // HBaseConfiguration 能接收 hbase-site.xml 的設定值
    Configuration conf = HBaseConfiguration.create();

    // 檔案的操作則使用 HBaseAdmin
    HBaseAdmin admin = new HBaseAdmin(conf);

    // 檢查
    if (admin.tableExists(tablename)) {
      System.out.println("Table: " + tablename + "Existed.");
    } else {

      // 建立
      admin.createTable(htd);

      // 
      System.out.println( tablename + " created.");
    }
  }

  static public void main(String argv[]) throws IOException {
    // eclipse only
    String[] args = {"ex1Table","Detail"};
    argv = args;

    String[] otherArgs = new GenericOptionsParser(new Configuration(), argv)
        .getRemainingArgs();
    if (otherArgs.length < 2) {
      System.out.println("CreateTable <newTableName> <Family>");
      return;
    }
    
    String tablename = otherArgs[0];
    String family = otherArgs[1];

    createHBaseTable(tablename, family);
    
    System.out.println("1. create table :" + tablename);
  }
}

執行完後你可以再Eclipse左邊的視景窗看到這一個table已經產生在hdfs中了。


再來也可以從terminal進到hbase去看table存不存在, 用hbase shell進入再下list指令你可以看到這個table的名字'ex1Table',見下圖

以上就是Hbase連接Eclipse的方法,我只有做新增Table,也可以利用其他api對DB作存取,改天有空再寫詳細一點, Over

2012年9月11日 星期二

哪天忘記怎麼裝Hbase時可以來這邊找

前言
如果我身份證的父親欄是寫"鍋台名", 我還會晚上不睡覺在研究Hbase怎麼裝嗎?
Hbase是一個"欄導向"(column-orinted)的分散式資料庫, 建構於HDFS之上當你要即時讀寫或隨機存取big data時, 就適合使用Hbase這個Hadoop應用。剩下的廢話就去看Hadoop技術手冊第二版 Chapter13。或是好兄弟Google會告訴你

看到好文補充一下:
HBase的伺服器體系架構是遵從簡單的主從伺服器架構, 他由HRegion Server群以及HBase Master伺服器構成HBase Master伺服器負責管理所有的HReion Server,而HBase中所有的伺服器都是透過ZooKeeper來進行協調的,並處理HBase伺服器在執行期間可能遇到的錯誤
HBase Master Server本身並不儲存HBase中的任何資料,Hbase邏輯上的表可能會被劃分成多個HRegion,然後儲存到HRegion Server群中HBase Master Server本身儲存的是資料到HRegion Server群的映射路徑


環境
這篇一樣
特別註明:HBase的版本跟Hadoop的版本會妖精打架, 我目前使用的Hadoop-1.0.3跟Hbase-0.94.1還算處得來, 或許玩的不夠深入還沒被蟲咬到

實作
服用本篇前一定要確認Hadoop環境是可以正常啟動的
本人大膽的假設各位看官們都已經啟動了HDP141, HDP142, HDP143這三台電腦。(還沒啟動Hadoop)
話說阿~~安裝Hbase也分成單機, 偽分散, 完全分散。看過我之前網誌的人就知道我一定是採用哪一種了,不合口味的就先說聲抱歉, 不送了, 請慢走

在這個當下相信各位已經啟動了VM, 待會要接著啟動Hadoop跟Hbase, 對於營養不良的VM們這又是一場搶奪資源大作戰阿! 讓我不勝噓唏給每一台VM的記憶體只有1G, 卻要起那麼多java process我只能說很多時候系統管理的問題牽涉到資源配置, 大家都在搶資源時, 有些process就會無聲無息的被linux kernel幹掉了。所以進行到這邊的大大們可以的話請先調高你們每一台VM的記憶體吧, 不然等一下最後啟動Hbase時很有可能會看不到HMaster, HRegionServer, HQuorumPeer。~~這是我第一次啟動Hbase時一直遇到的怪問題, 不知我這樣的解讀是否正確, 希望有好心的大大看到有感而發留個言給我, 謝謝囉XD

步驟一
實作以下動作前我們要先在NameNode跟DataNode的${HADOOP_HOME}/conf/hdfs-site.xml加入兩個屬性

 <property>
        <name>dfs.datanode.max.xcievers</name>
        <value>4096</value>
 </property>

沒加的話運行過程會丟出 missing blocks的錯誤

另外有個很玄的東西, Hadoop的文件寫說這是有bug的, 可是Hbase的文件又寫避免資料遺失最好加上這屬性。我也不是很懂,改天研究懂了再來詳細筆記一番(預設是false)

<property>
     <name>dfs.support.append</name>
     <value>true</value>
</property>



步驟二
步驟一都在主戰機實作(Host OS)
先寫個小script(HbaseRsync.sh), 等一下可以一次把安裝檔以及設定檔用scp丟過去, 要一個一個下指令也是可以啦, 因為我懶所以我用script。內容如下:


#!/bin/bash
scp -r hbase-0.94.1 HDP141:
scp -r hbase-0.94.1 HDP142:
scp -r hbase-0.94.1 HDP143:

很明顯的我把script放在家目錄,hbase-0.94.1這資料夾我也是放在家目錄。

接下來hbase-0.94.1/conf裡面有三個要改的, hbase-env.sh, hbase-site.xml, regionservers

1.hbase-env.sh,這檔案主要是配置系統的設定以及環境變數,會被start-hbase.sh啟動
在最下面加入以下幾行字。實際路徑要按照每台機器的來改



PS:一個分散式運行的HBase依賴一個zookeeper集群。所有的節點和客户端都必須能夠訪問zookeeper。默認的情况下HBase會管理一個zookeep集群。這個集群會随著HBase的啟動而啟動。想當然而,你也可以自己管理一個zookeeper集群,但需要配置HBase。你需要修改conf/hbase-env.sh 裡面的HBASE_MANAGES_ZK來切換。這個值默認是true的,作用是讓HBase啟動的時候同時也啟動內建自帶的zookeeper
PS:我自己使用起來自帶的zookeeper不是很穩,目前我是用獨立的zookeeper。
HBASE_MANAGES_ZK=false這樣子就不會使用hbase內建的zookeeper。


2.hbase-site.xml。


hbase.rootdir:要跟${HADOOP_HOME}/conf/core-site.xml的fs.default.name名字一樣,後面再加上/hbase
hbase.zookeeper.qourum這屬性就是列出zookeepr的ensemble servers要運行完全分布式模式, hbase.cluster.distributed這屬性要設為true

3.regionservers(RegionServer的節點)




步驟三
以上比Hadoop配置還要簡單吧,接下來利用HbaseRsync.sh把主戰機的hbase-0.94.1資料夾一次copy到三台datanode裡面去。



按下enter就會看到一直在copy的字跑阿跑的。執行完之後到datanode去看看是不是有這一個資料夾了


步驟四
在每一台機器的.bashrc加入HBASE_HOME, PATH也要記得加上${HBASE_HOME}/bin


以上三步驟都做完後本人習慣是全部重新開機, 然後去補充尼古丁順道思考一下有沒有落掉的東西XD

步驟四

這步驟稍微說明一下啟動Hbase以及新增table,寫入資料,刪除table
重新都開機完後, start-all.sh跟start-hbase.sh(停止是stop-hbase.sh)給他執行下去, Hadoop跟Hbase都啟動完後用jps看看,像下圖一樣namenode有出現HMaster, datanode有出現HRegionServer跟HQuorumPeer就是成功了

溫馨提醒:預設NameNode會停30秒在Safe Mode太急著start-hbase.sh也許會造成啟動失敗呦!




再用shell連進你的HBase並且隨便create一個table看看

create 'ooxx','ggyy'這段代表建立一個名為ooxx的table, 其中有一個column family名為'ggyy'見下圖

再執行list指令, 會列出Hbase所有用戶空間(user space)的資料表, 會看到有一個ooxx

接著新增(put)三個列和欄進入ooxx這一個column family中, 再列出(scan)資料表內容, 指令見下圖

移除資料表前要先停用(disable)才可以刪除(drop)


呼~~以上,OVER
這篇只是很基本很初階的安裝而已,細節的操作以及運用還要繼續啃書本以及Google, 哪天心血來潮時會陸陸續續再補上

老話一句,我真他媽的想要當富二代,不然哪有勁來研究這些玩意阿!!有錢的男人最帥,不能當富二代就想辦法變成富一代吧XD~~跟大家共勉之

2012年9月10日 星期一

Eclipse開發MapReduce程式(2)

前言
無止境的一直學習,深恐年紀大了,記憶力不好,趕快寫上來。

基於前一篇Eclipse開發MapReduce程式(1)中,有一段我寫到

"請記得把輸入輸出的路徑改回args[0]以及args[1]再把/hduser/cite/output這資料夾先刪除,不然會出錯。"
想了又想好像不應該是這樣的,於是把MyJob改寫了一段,可以避免輸出資料夾(/hduser/cite/output)已經存在了而發生的Exception。順道附上兩段利用HDFS API來實現hadoop dfs -put跟hadoop dfs -cat這兩個指令。
這都是基本的API操作,還有delete,merge...等等的,好兄弟google真的有一堆,嚇死你。

ps:output資料夾已存在就不能執行程式這是hadoop的安全機制, 以防意外蓋掉之前產生所產生的檔案

環境

Eclipse開發MapReduce程式(1)一樣。

實作

第一步, 在Eclipse中new一個PutHdfs.java
這段code就是"hadoop dfs -put <local input file> <hdfs input dir>" 的意思


import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;


public class PutHdfs {


public static void main(String[] args) throws IOException {

String local_path = args[0];
String hdfs_path = args[1];

InputStream in = new BufferedInputStream(new FileInputStream(local_path));
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(conf);
OutputStream out = fs.create(new Path(hdfs_path), new Progressable() {

@Override

public void progress() {
System.out.print("=");
}
});
IOUtils.copyBytes(in, out, 4096, true);
}

}


一樣在terminal中編譯成.class再包裝成.jar,利用hadoop jar執行。

這次示範把cite75_99.txt利用java code上傳到/hduser/cite/input中,並改名成cite.txt。以下是指令:
(我把PutHdfs.java放在/home/hadoopbooks/HDFS/put底下,指令請依照自己存放的路徑 )
javac -classpath hadoop-1.0.3/hadoop-core-1.0.3.jar -d hadoopbooks/HDFS/put/ hadoopbooks/HDFS/put/PutHdfs.java
jar -cvf hadoopbooks/HDFS/put/PutHdfs.jar -C hadoopbooks/HDFS/put/ .
hadoop jar hadoopbooks/HDFS/put/PutHdfs.jar PutHdfs hadoopbooks/cite/data/cite75_99.txt /hduser/cite/input/cite.txt


第二步就是本篇主要要講的,改寫MyJob.java,避免發生資料夾已存在的錯誤發生。
在之前的MyJob.java中加入一段CheckAndDelete的class。並在run()中加入CheckAndDelete.checkAndDelete(args[1], conf)呼叫static class來做判斷。見以下的code。


import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

public class MyJob extends Configured implements Tool{

public static class MapClass extends Mapper<LongWritable, Text, Text, Text> {
public void map (LongWritable key, Text value, Context context) throws IOException , InterruptedException {
String[] citation = value.toString().split(",");
context.write(new Text(citation[0]), new Text(citation[1]));
}
}

public static class ReduceClass extends Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
String csv = "";
for (Text val : values) {
if (csv.length() > 0) {
csv += ",";
}
csv += val.toString();
}
context.write(key, new Text(csv));
}
}

public static class CheckAndDelete {
static boolean checkAndDelete(final String path , Configuration conf) {
Path dst_path = new Path(path);
try {
FileSystem hdfs = dst_path.getFileSystem(conf);
if (hdfs.exists(dst_path)) {
hdfs.delete(dst_path, true);
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
}

public int run(String [] args) throws IOException , InterruptedException, ClassNotFoundException {
Configuration conf = getConf();
Job job = new Job(conf , "MyJob");
job.setJarByClass(MyJob.class);

FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));

CheckAndDelete.checkAndDelete(args[1], conf);

job.setMapperClass(MapClass.class);
job.setReducerClass(ReduceClass.class);

job.setInputFormatClass(TextInputFormat.class);
job.setOutputFormatClass(TextOutputFormat.class);

job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);

System.exit(job.waitForCompletion(true) ? 0 : 1);

return 0;
}

public static void main(String[] args) throws Exception {
int res = ToolRunner.run(new Configuration(), new MyJob(), args);
System.exit(res);
}

}

一樣在terminal中編譯成.class再包裝成.jar,利用hadoop jar執行。
javac -classpath hadoop-1.0.3/hadoop-core-1.0.3.jar -d hadoopbooks/cite/class/MyJob/ hadoopbooks/cite/java/MyJob/MyJob.java
jar -cvf hadoopJar/MyJob.jar -C hadoopbooks/cite/class/MyJob/ .
hadoop jar hadoopJar/MyJob.jar MyJob /hduser/cite/input /hduser/cite/output

可以重複試試看,就算/hduser/cite/output已經存在了,再執行第二次也步會發生錯誤,避免要刻意去刪掉存放產出檔案的資料夾。


第三步利用CatHdfs.java來觀看執行MyJob.jar後的產出結果,以下是java code


import org.apache.hadoop.conf.Configuration;

import org.apache.hadoop.fs.FSDataInputStream;

import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;


public class CatHdfs {

public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(conf);
FSDataInputStream in = null;
try {
in = fs.open(new Path(args[0]));
IOUtils.copyBytes(in, System.out, 4096, false);
in.seek(0);
IOUtils.copyBytes(in, System.out, 4096, false);
} finally {
IOUtils.closeStream(in);
}
}

}

一樣在terminal中編譯成.class再包裝成.jar,利用hadoop jar執行。
javac -classpath hadoop-1.0.3/hadoop-core-1.0.3.jar -d hadoopbooks/HDFS/cat/ hadoopbooks/HDFS/cat/CatHdfs.java
jar -cvf hadoopbooks/HDFS/cat/CatHdfs.jar -C hadoopbooks/HDFS/cat/ .
hadoop jar hadoopbooks/HDFS/catCatHdfs.jar CatHdfs /hduser/cite/output/part-r-00000


按下enter(對岸的阿六兄弟似乎說"回車"),就會看到一堆字在跑,沒錯你沒眼花。
想停止就crtl+c。

以上,over。