hadoop1.0.3学习笔记

回 到 目 录

最近要从网上抓取数据下来,然后hadoop来做存储和分析.

 

呆毛王赛高

 

月子酱赛高

 

小唯酱赛高

 

 

 

 

 

 

目录

  安装hadoop1.0.3

  HDFS

  wordcount

  mapreduce去重

  mapreduce算平均分

  mapreduce排序

  hbase的配置

  使用java操作hbase

  hbase存储数据的方式

  HFile的存储格式

  设计hbase表结构

  hive

 

 

安装hadoop1.0.3

 

  1 ubuntu中安装hadoop 1.0.3
  2 ------------伪分布式安装-------------
  3 1.安装ssh
  4     sudo apt-get install openssh-server
  5     如果出现E:Could not open lock file /var/lib/dpkg/lock
  6     可能是前面没加sudo,如果加了还没用,就得配置一下dpkg:
  7       sudo rm -rf /var/lib/dpkg/lock
  8           sudo rm -rf /var/cache/apt/archives/lock
  9           sudo apt-get update
 10           sudo dpkg --configure -a
 11 2. 安装rsync和vim(可以不做)
 12     sudo apt-get install rsync
 13     sudo apt-get install vim
 14     
 15     Rsync(remote synchronize)是一个远程数据同步工具,
 16     可通过LAN/WAN快速同步多台主机间的文件。
 17     Rsync使用所谓的“Rsync算法”来使本地和远程两个主机之间的文件达到同步,
 18     这个算法只传送两个文件的不同部分,而不是每次都整份传送,因此速度相当快。
 19     
 20 3. 配置ssh面密码登录
 21     ssh-keygen -t rsa 然后一直回车
 22     cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
 23     
 24     修改文件权限
 25     chmod 700 ~/.ssh
 26     chmod 600 ~/.ssh/authorized_keys
 27     
 28     验证是否成功
 29     ssh localhost
 30     假如出现Agent admitted failure to sign using the key
 31     说明ssh的密匙没加进来,输入命令:ssh-add ~/.ssh/id_rsa
 32 
 33 4. 配置JDK环境和下载hadoop 1.0.3
 34     1.修改JDK环境变量:sudo vim /etc/profile
 35         在末尾加上export JAVA_HOME=/home/xxx/jdk1.7.0_51  
 36                             export PATH=$JAVA_HOME/bin:$PATH 
 37                             export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
 38     2.修改hadoop环境变量:
 39         同样修改profile文件
 40         在末尾加上export HADOOP_INSTALL=/home/xxx/hadoop-1.0.3
 41                             export PATH=$PATH:$HADOOP_INSTALL/bin
 42     3.让profile文件的修改立即生效:
 43         source /etc/profile
 44     4.检验JDK是否正确配置:
 45         输入javac命令是否有提示
 46         或输入java -version是否有jdk版本信息
 47 
 48 5. 修改hadoop配置文件,指定JDK安装目录
 49     vi conf/hadoop-env.sh
 50     export JAVA_HOME=/home/xxx/jdk1.7.0_51
 51     
 52 6. 修改Hadoop核心配置文件core-site.xml,配置HDFS的地址和端口号
 53     vi conf/core-site.xml
 54     
 55     <configuration>
 56         <property>
 57             <name>hadoop.tmp.dir</name>
 58             <value>/hadoop</value>
 59         </property>
 60         <property>
 61             <name>fs.default.name</name>
 62             <value>hdfs://localhost:9000</value>
 63         </property>
 64         <property>
 65             <name>dfs.name.dir</name>
 66             <value>/hadoop/name</value>
 67         </property>
 68     </configuration>
 69     
 70     
 71 7. 修改Hadoop中HDFS的配置,修改replication
 72     vi conf/hdfs-site.xml
 73     
 74     <configuration>
 75         <property>
 76             <name>dfs.data.dir</name>
 77             <value>/hadoop/data</value>
 78         </property>
 79         <property>
 80             <name>dfs.replication</name>
 81             <value>1</value>
 82         </property>
 83     </configuration>
 84     
 85 8. 修改Hadoop中MapReduce的配置文件,配置的是JobTracker的地址和端口
 86     vi conf/mapred-site.xml
 87     
 88     <configuration>
 89         <property>
 90             <name>mapred.job.tracker</name>
 91             <value>localhost:9001</value>
 92         </property>
 93     </configuration>
 94     
 95 9. 创建/hadoop目录并修改权限为777
 96     sudo mkdir /hadoop
 97     sudo chmod 777 /hadoop
 98     
 99 10. 格式化Hadoop的文件系统HDFS
100     bin/hadoop namenode -format
101 
102 11. 启动hadoop
103     bin/start-all.sh
104     
105 最后验证Hadoop是否安装成功.打开浏览器,分别输入一下网址
106 方法一:
107     http://localhost:50030     (MapReduce的Web页面)
108     http://localhost:50070    (HDFS的Web页面)
109     如果都能查看,说明安装成功。
110 方法二:
111     输入jps命令
112     全都出现
113     18186 NameNode
114     18949 TaskTracker
115     18718 JobTracker
116     18643 SecondaryNameNode
117     18414 DataNode
118     19126 Jps
119     说明成功
120 
121 
122 
123 
124 -----------安装集群------------
125 
126 1. 准备2个服务器,分别为
127     机器名                IP地址                    作用
128     hadoop.main        192.168.1.100        NameNode,JobTracker, DataNode, TaskTracker
129     hadoop.slave    192.168.1.101        DataNode, TaskTracker
130     
131     两台机器的用户名必须相同
132 
133 2. 分别在这两个主机上,按照单机版的安装方法,安装hadoop
134         第9、10、11步不需要操作
135 3. 在/etc/hostanem 中修改主机名
136     在/etc/hosts中配置主机名和IP地址对应关系
137     用source /etc/hostname让配置立即生效
138     
139 4. 将hadoop.main节点中的~/.ssh/id_rsa.pub文件拷贝到hadoop.slave节点的~/.ssh目录下,
140     然后在hadoop.slave的~/.ssh目录下运行
141     cat ./id_rsa.pub >> authorized_keys
142     
143 5. 分别修改2台主机中的hadoop配置文件masters和slaves    
View Code

 

HDFS

概念太多了,真的讲不完。写过的类也挺多,每天加一点吧

使用下面的程序前先把hadoop1.0.3下的hadoop-core-1.0.3.jar、lib下的所有jar给加到类加载路径。并运行bin/start-all.sh

 1 <?xml version="1.0"?>
 2 <?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
 3 
 4 <!-- Put site-specific property overrides in this file. -->
 5 
 6 <configuration>
 7     <property>
 8         <name>hadoop.tmp.dir</name>
 9         <value>/hadoop</value>
10     </property>
11     <property>
12         <name>fs.default.name</name>
13         <value>hdfs://Ubuntu1:9000</value>
14     </property>
15     <property>
16         <name>dfs.name.dir</name>
17         <value>/hadoop/name</value>
18     </property>
19 </configuration>
1core-site.xml

文件上传:

import java.io.BufferedInputStream;
import java.io.FileInputStream;
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 FileCopyWithProgress {

    public static void main(String[] args) throws Exception {
        String localSrc = args[0];
        String dst = args[1];
        InputStream in = new BufferedInputStream(new FileInputStream(localSrc));
        Configuration conf = new Configuration();
        conf.addResource("1core-site.xml");
        FileSystem fs = FileSystem.get(conf);
        //hadoop 输入流fs.open()
        //hadoop 输出流fs.create()
        OutputStream out = fs.create(new Path(dst), new Progressable() {
            public void progress() {
                System.out.println(".");
            }
        });
        IOUtils.copyBytes(in, out, 4096, false);
        IOUtils.closeStream(out);
        IOUtils.closeStream(in);
        System.out.println("over");
    }

}
FileCopyWithProgress.java

上面两个文件放在同一个目录就能运行了。这个程序完成了从linux上传到hdfs的操作。

Configuration类是用来读core-site.xml配置的,xml默认在conf文件夹中,FileSystem是hdfs类。先不用管匿名内部类Progressable。

IOUtils工具类用来复制文件流,因为上传到hdfs,所以要用hadoop的输出流;因为是从普通文件系统上传的,所以用java的输入流就行。

Path是hadoop实现的类似于URI的类,常用来表示hdfs中的文件(的路径)

 

其他的慢慢加...

 

 wordcount

使用下面的程序前先把hadoop1.0.3下的hadoop-core-1.0.3.jar、lib下的所有jar给加到类加载路径。并运行bin/start-all.sh

 1 package wordcount;
 2 
 3 import org.apache.hadoop.fs.Path;
 4 import org.apache.hadoop.io.IntWritable;
 5 import org.apache.hadoop.io.Text;
 6 import org.apache.hadoop.mapreduce.Job;
 7 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
 8 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
 9 
10 
11 public class WordCount {
12     public static void main(String[] args) throws Exception {
13         Job job = new Job();
14         job.setJarByClass(WordCount.class);
15         job.setJobName("word count demo");
16 
17         FileInputFormat.addInputPath(job, new Path(args[0]));
18         FileOutputFormat.setOutputPath(job, new Path(args[1]));
19 
20         job.setMapperClass(WordMapper.class);
21         job.setReducerClass(WordReducer.class);
22         //下面一句是优化,把每个节点的map的数据用reduce合并起来,最后把所有节点的reduce输出作为最后一次合并的输入
23         job.setCombinerClass(WordReducer.class);
24 
25         job.setOutputKeyClass(Text.class);
26         job.setOutputValueClass(IntWritable.class);
27 
28         System.exit(job.waitForCompletion(true) ? 0 : 1);
29     }
30 }
wordcount.WordCount.java
 1 package wordcount;
 2 
 3 import java.io.IOException;
 4 import java.util.StringTokenizer;
 5 
 6 import org.apache.hadoop.io.IntWritable;
 7 import org.apache.hadoop.io.LongWritable;
 8 import org.apache.hadoop.io.Text;
 9 import org.apache.hadoop.mapreduce.Mapper;
10 
11 public class WordMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
12     public static IntWritable one = new IntWritable(1);
13     private Text word = new Text();
14     /**
15      * LongWritable: 输入,分块的第几行
16      * Text: 输入,该行的内容
17      * 
18      * Text: 输出内容
19      * IntWritable:输出内容关联的数字/值
20      * 
21      * **Context**:Text和Intritable的结合
22      */
23     public void map(LongWritable key, Text value, Context ctx) throws InterruptedException, IOException {
24         String line = value.toString();
25         StringTokenizer st = new StringTokenizer(line);
26         
27         while (st.hasMoreElements()) {
28             word.set(st.nextToken());
29             ctx.write(word, one);
30         }
31     }
32 }
wordcount.WordMapper.java
 1 package wordcount;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.hadoop.io.IntWritable;
 6 import org.apache.hadoop.io.Text;
 7 import org.apache.hadoop.mapreduce.Reducer;
 8 
 9 public class WordReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
10     public void reduce(Text keyin, Iterable<IntWritable> valuein, Context ctx) throws InterruptedException, IOException  {
11         int i = 0;
12         for (IntWritable iw : valuein) {
13             i++;
14         }
15         ctx.write(keyin, new IntWritable(i));
16     }
17 }
wordcount.WordReducer.java

 

wordcount是最简单的mapreduce程序,需要一个客户端类,一个Mapper,一个Reducer。

客户端用来发布job、设置Mapper、Reducer以及Mapper的输入,Reducer的输出,最后输出的类型等

你写的mapper需要继承 Mapper<LongWritable, Text, Text, IntWritable>。注意:泛型中的四个参数是hadoop的序列化类,顾名思义。

你可以改变顺序,参数就是固定的4个。其他类型有DoubleWritable等等,基本类型的序列化类的名称是 基本类型名 + Writable,字符串的序列化类是Text。

就上面的mapper来说,输入key是long,输入value是String;输出key是String,输出value是int

 

你写的reducer要继承Reducer<Text, IntWritable, Text, IntWritable>。要注意的是Mapper中的输出要对应前两个类型。

 

再解释上面代码的意思:WordCount设置mapper和reducer等东西,然后调用waitForCompletion(),终于执行mapper了

重写的map方法是最要的方法:参数一一对应,Context 相当等于一个key加上一个value。
每次调用map,hadoop都会把文件的某一行当成value传进来了,key是行号。

StringTokenizer是java.util包中的,在这里用来提取下一个字符串(空格隔开)

每读一个字符串,都会输出:“字符串 1”,由于在WordCount中设置了CombinerClass,

因此每个结点会先合并一下数据(在reducer中做加法)。

 

然后再把结果再进行总的reduce,

reducer的核心方法reduce,第二个参数一定是泛型第二参数的遍历类型:

如上面程序中Reducer<Text, IntWritable, Text, IntWritable>第二个参数类型是IntWritable

那么reduce方法第二个参数的类型肯定是肯定是Iterable<IntWritable>

hadoop在mapper传到reducer的过程中会把键名相同的键值对合并成它的遍历类型Iterable<?>

最后把每个单词的数量加起来

看到这里后,在返回到看上一段落的最后两句话以及Wordcount类,

可以知道CombinerClass其实就是一个Reducer,

只不过是在传给总的reducer之前对各个结点先进行合并一次。

 

 

以下三个mapreduce的例子不再解释,个人比较懒,没改类名

mapreduce去重

 1 package wordcount;
 2 
 3 import org.apache.hadoop.fs.Path;
 4 import org.apache.hadoop.io.IntWritable;
 5 import org.apache.hadoop.io.Text;
 6 import org.apache.hadoop.mapreduce.Job;
 7 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
 8 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
 9 
10 
11 public class WordCount {
12     public static void main(String[] args) throws Exception {
13         Job job = new Job();
14         job.setJarByClass(WordCount.class);
15         job.setJobName("word count demo");
16         
17         FileInputFormat.addInputPath(job, new Path(args[0]));
18         FileOutputFormat.setOutputPath(job, new Path(args[1]));
19         
20         job.setMapperClass(WordMapper.class);
21         job.setReducerClass(WordReducer.class);
22         //下面一句是优化,把每个节点的map的数据用reduce合并起来,最后把所有节点的reduce输出作为最后一次合并的输入
23         job.setCombinerClass(WordReducer.class);
24         
25         job.setOutputKeyClass(Text.class);
26         job.setOutputValueClass(Text.class);
27         
28         System.exit(job.waitForCompletion(true) ? 0 : 1);
29     }
30 }
wordcount.WordCount.java
 1 package wordcount;
 2 
 3 import java.io.IOException;
 4 import java.util.StringTokenizer;
 5 
 6 import org.apache.hadoop.io.IntWritable;
 7 import org.apache.hadoop.io.LongWritable;
 8 import org.apache.hadoop.io.Text;
 9 import org.apache.hadoop.mapreduce.Mapper;
10 
11 public class WordMapper extends Mapper<LongWritable, Text, Text, Text> {
12     public void map(LongWritable key, Text value, Context ctx) throws InterruptedException, IOException {
13         ctx.write(value, new Text(""));
14     }
15 }
wordcount.WordMapper.java
 1 package wordcount;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.hadoop.io.IntWritable;
 6 import org.apache.hadoop.io.Text;
 7 import org.apache.hadoop.mapreduce.Reducer;
 8 
 9 public class WordReducer extends Reducer<Text, Text, Text, Text> {
10     public void reduce(Text keyin, Iterable<Text> valuein, Context ctx) throws InterruptedException, IOException  {
11         ctx.write(keyin, new Text(""));
12     }
13 }
wordcount.WordReducer.java

 

mapreduce算平均分

 1 package wordcount;
 2 
 3 import org.apache.hadoop.fs.Path;
 4 import org.apache.hadoop.io.IntWritable;
 5 import org.apache.hadoop.io.Text;
 6 import org.apache.hadoop.mapreduce.Job;
 7 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
 8 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
 9 
10 
11 public class WordCount {
12     public static void main(String[] args) throws Exception {
13         Job job = new Job();
14         job.setJarByClass(WordCount.class);
15         job.setJobName("word count demo");
16         
17         FileInputFormat.addInputPath(job, new Path(args[0]));
18         FileOutputFormat.setOutputPath(job, new Path(args[1]));
19         
20         job.setMapperClass(WordMapper.class);
21         job.setReducerClass(WordReducer.class);
22         //下面一句是优化,把每个节点的map的数据用reduce合并起来,最后把所有节点的reduce输出作为最后一次合并的输入
23         job.setCombinerClass(WordReducer.class);
24         
25         job.setOutputKeyClass(Text.class);
26         job.setOutputValueClass(IntWritable.class);
27         
28         System.exit(job.waitForCompletion(true) ? 0 : 1);
29     }
30 }
wordcount.WordCount.java
 1 package wordcount;
 2 
 3 import java.io.IOException;
 4 import java.util.StringTokenizer;
 5 
 6 import org.apache.hadoop.io.IntWritable;
 7 import org.apache.hadoop.io.LongWritable;
 8 import org.apache.hadoop.io.Text;
 9 import org.apache.hadoop.mapreduce.Mapper;
10 
11 public class WordMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
12     public void map(LongWritable key, Text value, Context ctx) throws InterruptedException, IOException {
13         StringTokenizer token = new StringTokenizer(value.toString(), " ");
14         if (token.hasMoreElements()) {
15             Text name = new Text(token.nextToken());
16             IntWritable score = new IntWritable(Integer.parseInt(token.nextToken()));
17             
18             ctx.write(name, score);
19         }
20     }
21 }
wordcount.WordMapper.java
 1 package wordcount;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.hadoop.io.IntWritable;
 6 import org.apache.hadoop.io.Text;
 7 import org.apache.hadoop.mapreduce.Reducer;
 8 
 9 public class WordReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
10     public void reduce(Text keyin, Iterable<IntWritable> valuein, Context ctx) throws InterruptedException, IOException  {
11         int sum = 0;
12         int count = 0;
13         for (IntWritable score : valuein) {
14             sum += score.get();
15             count++;
16         }
17         ctx.write(keyin, new IntWritable(sum / count));
18     }
19 }
wordcount.WordReducer.java

 

mapreduce排序

 

 1 package wordcount;
 2 
 3 import java.io.IOException;
 4 import java.util.StringTokenizer;
 5 
 6 import org.apache.hadoop.io.IntWritable;
 7 import org.apache.hadoop.io.LongWritable;
 8 import org.apache.hadoop.io.Text;
 9 import org.apache.hadoop.mapreduce.Mapper;
10 
11 public class WordMapper extends Mapper<LongWritable, Text, IntWritable, Text> {
12     public void map(LongWritable key, Text value, Context ctx) throws InterruptedException, IOException {
13         String line = value.toString();
14         IntWritable data = new IntWritable();
15         data.set(Integer.parseInt(line));
16         ctx.write(data, new Text(""));
17     }
18 }
wordcount.WordMapper.java
 1 package wordcount;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.hadoop.io.IntWritable;
 6 import org.apache.hadoop.io.Text;
 7 import org.apache.hadoop.mapreduce.Reducer;
 8 
 9 public class WordReducer extends Reducer<IntWritable, Text, IntWritable, Text> {
10     public void reduce(IntWritable keyin, Iterable<Text> valuein, Context ctx) throws InterruptedException, IOException  {
11         for (Text text : valuein) {
12             ctx.write(keyin, text);
13         }
14     }
15 }
wordcount.WordReducer.java
 1 package wordcount;
 2 
 3 import org.apache.hadoop.fs.Path;
 4 import org.apache.hadoop.io.IntWritable;
 5 import org.apache.hadoop.io.Text;
 6 import org.apache.hadoop.mapreduce.Job;
 7 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
 8 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
 9 
10 
11 public class WordCount {
12     public static void main(String[] args) throws Exception {
13         Job job = new Job();
14         job.setJarByClass(WordCount.class);
15         job.setJobName("word count demo");
16         
17         FileInputFormat.addInputPath(job, new Path(args[0]));
18         FileOutputFormat.setOutputPath(job, new Path(args[1]));
19         
20         job.setMapperClass(WordMapper.class);
21         job.setReducerClass(WordReducer.class);
22         //下面一句是优化,把每个节点的map的数据用reduce合并起来,最后把所有节点的reduce输出作为最后一次合并的输入
23         job.setCombinerClass(WordReducer.class);
24         
25         job.setOutputKeyClass(IntWritable.class);
26         job.setOutputValueClass(Text.class);
27         
28         System.exit(job.waitForCompletion(true) ? 0 : 1);
29     }
30 }
wordcount.WordCount.java

map之后, reduce之前数字会自动从小到大排序,所以直接输出就行

 

hbase配置

 

1.修改conf目录下的hbase-site.xml

    <property>

        <name>hbase.rootdir</name>

        <value>hdfs://Ubuntu1:9000/hbase</value>

    </property>



2.运行bin目录的start-hbase.sh



3.进入shell模式

    运行bin目录下的hbase shell命令
View Code

 注意修改NameNode的uri,上面我的NameNode的host是Ubuntu1。

 要先打开hdfs再打开hbase

 打开hbase之前先确定hdfs不处在安全模式,否则hbase将出错,这时

 需要重新打开hbase

 

client访问hbase上的数据的过程并不需要master的参与(寻址访问zookeeper和region server, 数据读写访问region server),master仅仅维护table和region元数据信息,负载很低

 

hbase可以脱离hdfs使用,比如上面配置hbase.rootdir可以填本地目录

 

habase shell命令

进入hbase shell后的常用操作命令:

list:列出所有的table

create 'test', 'col':    创建teset表,col是列族

put 'test', 'row1', 'col:aa', 'lan':    在test表中的row key为row1、列族为col,列名为aa的数据'lan'

get 'test', 'row1': 查看test表的row1行数据


scan 'test':    查看test表的记录

scan 'test', {VERSIONS=>3}:      显示test表中所有前三个版本的数据,注意大小写,后面有s

scan 'test', {COLUMN=>'col:aa'}:    查看test表的col列族的aa列

describe 'test':    描述test的结构
View Code

在describe 'test'中可以看到VERSIONS=>3,说明test表最多保存3个版本的数据,如果重复向某个地方插入数据超过3次,前面的数据就不会被显示,即使你使用scan命令并指定VERSIONS=>10。最大保存版本数可以在建表时指定

 

使用java操作hbase

 

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.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;

public class HbaseOperate {
    private Configuration conf;
    public HbaseOperate() {
        conf = HBaseConfiguration.create();//得到Configuration
        //conf.addResource("core-site.xml");
        //conf.set("hbase.zookeeper.quorum", "127.0.0.1");//HBase 服务器地址
        //conf.set("hbase.zookeeper.property.clientPort", "*****");//端口
        //conf = new Configuration();
        
    }
    

    /**
     * 功能:根据表名、列族和版本创建一张表
     * @param name 要创建的表的表名
     * @param col 列族
     * @param version 保存数据的最大版本
     * @throws Exception
     */
    public void createTable(String name, String col, int version) throws Exception {
        HBaseAdmin admin = new HBaseAdmin(conf);
        
        if (admin.tableExists(name)) {//如果表存在
            admin.disableTable(name);//删除表需要先disable,然后delete
            admin.deleteTable(name);
        }
        HTableDescriptor tabDes = new HTableDescriptor(name);//代表一张表
        HColumnDescriptor colDes = new HColumnDescriptor(col);//代表一个列族
        colDes.setMaxVersions(version);//设置versions
        tabDes.addFamily(colDes);//添加列族
        admin.createTable(tabDes);//创建一张表
    }
    /**
     * tab_grobal param:userid
     * tab_user2id info:id
     * tab_id2user info:username info:password
     */
    
    public void createTables() throws Exception {
        //hbase存储的都是2进制数据,所以要转换成Byte。Bytes是hbase的工具类
        createTable("tab_grobal", "param", 1);
        Put put = new Put(Bytes.toBytes("row_userid"));//Put可以看成是hbase的一行,用来插入一行
        long id = 0;
        put.add(Bytes.toBytes("param"), Bytes.toBytes("userid"), Bytes.toBytes(id));//设置列族、列、id
        HTable ht = new HTable(conf, "tab_grobal");
        ht.put(put);
        
        
        createTable("tab_user2id", "info", 1);
        createTable("tab_id2user", "info", 1);
    }
    
    /**
     * 1. check use exist
     * 2. get new ID
     * 3. insert tab_user2id
     * 4. insert tab_id2user
     * @param username
     * @param password
     * @return
     * @throws Exception
     */
    public boolean createNewUser(String username, String password) throws Exception {
        HTable tab_user2id = new HTable(conf, "tab_user2id");
        HTable tab_grobal = new HTable(conf, "tab_grobal");
        HTable tab_id2user = new HTable(conf, "tab_id2user");
        if (tab_user2id.exists(new Get(username.getBytes()))) {//Get和Put差不多,前者是得到一行,后者是插入一行
            return false;
        }
        
        long id = tab_grobal.incrementColumnValue(Bytes.toBytes("row_userid"), Bytes.toBytes("param"), 
                Bytes.toBytes("userid"), 1);//id自动加1,hbase的工具,处理了多线程问题
        Put put = new Put(username.getBytes());
        put.add(Bytes.toBytes("info"), Bytes.toBytes("id"), Bytes.toBytes(id));
        tab_user2id.put(put);
        
        put = new Put(Bytes.toBytes(id));
        put.add(Bytes.toBytes("info"), Bytes.toBytes("username"), username.getBytes());
        put.add(Bytes.toBytes("info"), Bytes.toBytes("password"), password.getBytes());
        tab_id2user.put(put);
        return true;
    }
    public static void main(String[] args) throws Exception {
        HbaseOperate h = new HbaseOperate();
        System.out.println("begin create");
        h.createTables();
        h.createNewUser("liuyuefeixue", "123456");
        System.out.println("success");
    }

}
View Code

 

hbase存储数据的方式

1. 按rowkey的字典顺序排序。有相同“前缀”的数据会存储在相邻区域

2. hbase使用二叉平衡排序树快速找到rowkey

3. 每个Table分成多个Region,每个Region负责一个范围的rowkey。当Table为空时只有一个Region,当rowkey足够多时,Region开始分裂。

4. 每个Region由一个Region Server管理,每个Server对应一个结点

5. 一个HRegion对象对应一个Region,一个HRegion对象包括多个HStore对象,一个HStore对象对应一个列族,一个HStore中包含两种对象:

  MemStore(内存存储)和StoreFile(文件存储)。初始时HStore中只有一个MemStore。

6. 向表中写入数据的过程:

  写入MemStore,同时写入HLog(数据丢失时可以用这个文件恢复,只记录MemStore的数据,不记录StoreFile的数据)

  当MemStore达到一定大小时,会被flush成一个StoreFile(HFile文件),并生成一个新的MemStore

  当StoreFile数量达到一定值时,会触发compact(另开一个线程),将多个StoreFile合并成一个StoreFile

  当单个StoreFile达到一定大小时,会触发split,将当前Region分成两个Region并把其中一个分配给另一个Region Server

 

HFile存储数据的方式

1. Data Block。每个block除了开头一个Magic(用来索引的)以外有很多个key-value(即cell),key-value是排过序的

2. Meta。自定义的key-value,可有可无

3. File info

4. Data Index。可以看做data block中的key

5. Meta Index

6. Trailer。文件结尾的相关信息

 

Data Block中key-value的组成:

1. key length

2. value length

3. row length

4. row  即row key

5. column family length

6. column family

7. column qualifier  列名

8. timestramp  时间戳,即版本

9. key type  有两种类型,插入类型和删除类型(在MemStore时仅仅是标记删除。只有compact操作时会真正的删除多余的version)

10. value  存储2进制数据

 

一个key包含3~9。

一个cell包含一个row key 一个列族和一个列名

一个cell应该使用尽可能短的row key、column qualifier,可以节省空间

 

 

设计Hbase表结构

原理:  一个Region负责一定范围的row key。如果一个row key中有几千万个列,Region并不会分裂,而会变得非常大。因此如果有某一个数据(A)对应很多个数据(B)时,应该把B设计为行,A设计为列。

    比如:在微博中,名人有非常多的收听者,那么不应该把收听者作为列,不然会造成Region非常非常大。那么一个用户的收听者应该作为row key,而列存储的是该用户。反过来,一个用户去收听的用户数并不是很多,最多可能就几百个,那么可以把该用户存作row key,而收听的用户存作为列。假如设计一个用户去收听的用户数的表时,把该用户收听的用户作为row key行不行呢?只能说不太好,因为这样要查一个用户收听了哪些人,就得到很多不同的Region里去查,自然比在一个Region里查要慢得多。

 

   总结:数据量大的作为row key;数据量不大的时候,看哪些数据需要很快的查询速度,查询快的作为列

 

hbase有两张特殊的表:-ROOT-和.META,由系统创建

Zookeeper中记录了-ROOT-表的location,-ROOT-记录了.META的Region信息,-ROOT-只有一个Region

.META记录了用户表的Region信息,可以有多个Region

 

虽然用list命令不能显示出这两张表,但是可以用scan命令查看这两张表的数据

 

hbase主键的设计

1. 如果一个表中有多个列族,那么每个列族的数据都分别存在一个StoreFile中。一个row key不管有多少个列,都会存放到一个Region Server中,不会被拆分

2. 设计表的方式

  这里以存储email信息作为例子

  1)瘦高:主要用row key来存储(纵向存储),每封email的id作为一行,只有有限列来存储email内容、时间、发件人和收件人等信息。比较建议使用这种存储方式

  2)矮胖:主要用列来存储(横向存储),每封email的id作为一个列,发件人的id作为row key, 一个人可能发了很多email,因此列会很多

3. row key的设计

  1)尽量不要用递增数来做主键,这样会使得插入新的行时,会频繁的插入到某一个Region Server,不利于负载均衡。可以使用随机数、当前时间的MD5值、当前时间对Region Server数取模并且连接其他字符来做row key

  2)row key要尽量短,尽量额外存储key。但是也要保证key能足够长使得分布均匀

 

Hive

   这里使用的是0.9.0版本

create table tab_name(id int, name string) row format delimited fields terminated by ',';
#用逗号分割一行,这样可以指定一个文件来插入数据到表中.默认分隔符不能用键盘打出来

load data local inpath '/home/a.txt' into table tab_name;
#从/home/a.txt中度数据插入到tab_name表中,如果不写local,默认就从hdfs从拿到文件
#into table之前还能加一个overwrite,能把表原来的数据全部都删除,然后插入设定的数据源的数据
#hive不会转换数据,而只是把设定的a.txt简单的拷贝到hdfs中

  

 hiveQL只要有where语句,就会启动一个mapreduce任务,因此查少量数据时很慢

 hive的元数据有三种存储方式:

1. Embedded:会在运行hive的当前目录生成一个metastore_db文件夹,换一个目录时将读不到元数据。一般用来做测试

2. Local:使用其他数据库来保存元数据,比如通过JDBC连接mysql保存元数据

3. Remote:为了让其他非java的语言连接hive,使用了thrift服务。

 

配置local方式
hive-site.xml

<configuration>
  <property>
      <name>hive.metastore.local</name>
      <value>true</value>
  </property>
  <property>
      <name>javax.jdo.option.ConnectionDiverName</name>
      <value>com.mysql.jdbc.Driver</value>
  </property>
  <property>
      <name>javax.jdo.option.ConnectionURL</name>
      <value>jdbc:mysql://localhost:3306/hive?useUnicode=true&charactorEncoding=utf-8&createDatabaseIfNotExist=true</value>
  </property>
  <property>
      <name>javax.jdo.option.ConnectionUserName</name>
      <value>hive</value>
  </property>

  <property>
      <name>javax.jdo.option.ConnectionPassword</name>
      <value>hive</value>
  </property>
</configuration>
给mysql创建hive数据库要加上host、赋权限也要加上host,否则hive不能连上mysql

 hive的存储路径默认在hdfs的/user/hive/warehouse目录下

 

hive做连接查询的reduce阶段会把join关键字左边的表放到内存中,因此我们查询时尽量选择数据量较小的表放在join的左边

 

 

 

 

posted @ 2014-07-13 21:37  -六月飞雪-  阅读(710)  评论(0编辑  收藏  举报