hadoop1.0.3学习笔记
最近要从网上抓取数据下来,然后hadoop来做存储和分析.
呆毛王赛高
月子酱赛高
小唯酱赛高
目录
安装hadoop1.0.3
ubuntu中安装hadoop 1.0.3
------------伪分布式安装-------------
1.安装ssh
sudo apt-get install openssh-server
如果出现E:Could not open lock file /var/lib/dpkg/lock
可能是前面没加sudo,如果加了还没用,就得配置一下dpkg:
sudo rm -rf /var/lib/dpkg/lock
sudo rm -rf /var/cache/apt/archives/lock
sudo apt-get update
sudo dpkg --configure -a
2. 安装rsync和vim(可以不做)
sudo apt-get install rsync
sudo apt-get install vim Rsync(remote synchronize)是一个远程数据同步工具,
可通过LAN/WAN快速同步多台主机间的文件。
Rsync使用所谓的“Rsync算法”来使本地和远程两个主机之间的文件达到同步,
这个算法只传送两个文件的不同部分,而不是每次都整份传送,因此速度相当快。 3. 配置ssh面密码登录
ssh-keygen -t rsa 然后一直回车
cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys 修改文件权限
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys 验证是否成功
ssh localhost
假如出现Agent admitted failure to sign using the key
说明ssh的密匙没加进来,输入命令:ssh-add ~/.ssh/id_rsa 4. 配置JDK环境和下载hadoop 1.0.3
1.修改JDK环境变量:sudo vim /etc/profile
在末尾加上export JAVA_HOME=/home/xxx/jdk1.7.0_51
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
2.修改hadoop环境变量:
同样修改profile文件
在末尾加上export HADOOP_INSTALL=/home/xxx/hadoop-1.0.3
export PATH=$PATH:$HADOOP_INSTALL/bin
3.让profile文件的修改立即生效:
source /etc/profile
4.检验JDK是否正确配置:
输入javac命令是否有提示
或输入java -version是否有jdk版本信息 5. 修改hadoop配置文件,指定JDK安装目录
vi conf/hadoop-env.sh
export JAVA_HOME=/home/xxx/jdk1.7.0_51 6. 修改Hadoop核心配置文件core-site.xml,配置HDFS的地址和端口号
vi conf/core-site.xml <configuration>
<property>
<name>hadoop.tmp.dir</name>
<value>/hadoop</value>
</property>
<property>
<name>fs.default.name</name>
<value>hdfs://localhost:9000</value>
</property>
<property>
<name>dfs.name.dir</name>
<value>/hadoop/name</value>
</property>
</configuration> 7. 修改Hadoop中HDFS的配置,修改replication
vi conf/hdfs-site.xml <configuration>
<property>
<name>dfs.data.dir</name>
<value>/hadoop/data</value>
</property>
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
</configuration> 8. 修改Hadoop中MapReduce的配置文件,配置的是JobTracker的地址和端口
vi conf/mapred-site.xml <configuration>
<property>
<name>mapred.job.tracker</name>
<value>localhost:9001</value>
</property>
</configuration> 9. 创建/hadoop目录并修改权限为777
sudo mkdir /hadoop
sudo chmod 777 /hadoop 10. 格式化Hadoop的文件系统HDFS
bin/hadoop namenode -format 11. 启动hadoop
bin/start-all.sh 最后验证Hadoop是否安装成功.打开浏览器,分别输入一下网址
方法一:
http://localhost:50030 (MapReduce的Web页面)
http://localhost:50070 (HDFS的Web页面)
如果都能查看,说明安装成功。
方法二:
输入jps命令
全都出现
18186 NameNode
18949 TaskTracker
18718 JobTracker
18643 SecondaryNameNode
18414 DataNode
19126 Jps
说明成功 -----------安装集群------------ 1. 准备2个服务器,分别为
机器名 IP地址 作用
hadoop.main 192.168.1.100 NameNode,JobTracker, DataNode, TaskTracker
hadoop.slave 192.168.1.101 DataNode, TaskTracker 两台机器的用户名必须相同 2. 分别在这两个主机上,按照单机版的安装方法,安装hadoop
第9、10、11步不需要操作
3. 在/etc/hostanem 中修改主机名
在/etc/hosts中配置主机名和IP地址对应关系
用source /etc/hostname让配置立即生效 4. 将hadoop.main节点中的~/.ssh/id_rsa.pub文件拷贝到hadoop.slave节点的~/.ssh目录下,
然后在hadoop.slave的~/.ssh目录下运行
cat ./id_rsa.pub >> authorized_keys 5. 分别修改2台主机中的hadoop配置文件masters和slaves
HDFS
概念太多了,真的讲不完。写过的类也挺多,每天加一点吧
使用下面的程序前先把hadoop1.0.3下的hadoop-core-1.0.3.jar、lib下的所有jar给加到类加载路径。并运行bin/start-all.sh
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <!-- Put site-specific property overrides in this file. --> <configuration>
<property>
<name>hadoop.tmp.dir</name>
<value>/hadoop</value>
</property>
<property>
<name>fs.default.name</name>
<value>hdfs://Ubuntu1:9000</value>
</property>
<property>
<name>dfs.name.dir</name>
<value>/hadoop/name</value>
</property>
</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
package wordcount; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class WordCount {
public static void main(String[] args) throws Exception {
Job job = new Job();
job.setJarByClass(WordCount.class);
job.setJobName("word count demo"); FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1])); job.setMapperClass(WordMapper.class);
job.setReducerClass(WordReducer.class);
//下面一句是优化,把每个节点的map的数据用reduce合并起来,最后把所有节点的reduce输出作为最后一次合并的输入
job.setCombinerClass(WordReducer.class); job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class); System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
wordcount.WordCount.java
package wordcount; import java.io.IOException;
import java.util.StringTokenizer; import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper; public class WordMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
public static IntWritable one = new IntWritable(1);
private Text word = new Text();
/**
* LongWritable: 输入,分块的第几行
* Text: 输入,该行的内容
*
* Text: 输出内容
* IntWritable:输出内容关联的数字/值
*
* **Context**:Text和Intritable的结合
*/
public void map(LongWritable key, Text value, Context ctx) throws InterruptedException, IOException {
String line = value.toString();
StringTokenizer st = new StringTokenizer(line); while (st.hasMoreElements()) {
word.set(st.nextToken());
ctx.write(word, one);
}
}
}
wordcount.WordMapper.java
package wordcount; import java.io.IOException; import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer; public class WordReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
public void reduce(Text keyin, Iterable<IntWritable> valuein, Context ctx) throws InterruptedException, IOException {
int i = 0;
for (IntWritable iw : valuein) {
i++;
}
ctx.write(keyin, new IntWritable(i));
}
}
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去重
package wordcount; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class WordCount {
public static void main(String[] args) throws Exception {
Job job = new Job();
job.setJarByClass(WordCount.class);
job.setJobName("word count demo"); FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1])); job.setMapperClass(WordMapper.class);
job.setReducerClass(WordReducer.class);
//下面一句是优化,把每个节点的map的数据用reduce合并起来,最后把所有节点的reduce输出作为最后一次合并的输入
job.setCombinerClass(WordReducer.class); job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class); System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
wordcount.WordCount.java
package wordcount; import java.io.IOException;
import java.util.StringTokenizer; import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper; public class WordMapper extends Mapper<LongWritable, Text, Text, Text> {
public void map(LongWritable key, Text value, Context ctx) throws InterruptedException, IOException {
ctx.write(value, new Text(""));
}
}
wordcount.WordMapper.java
package wordcount; import java.io.IOException; import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer; public class WordReducer extends Reducer<Text, Text, Text, Text> {
public void reduce(Text keyin, Iterable<Text> valuein, Context ctx) throws InterruptedException, IOException {
ctx.write(keyin, new Text(""));
}
}
wordcount.WordReducer.java
mapreduce算平均分
package wordcount; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class WordCount {
public static void main(String[] args) throws Exception {
Job job = new Job();
job.setJarByClass(WordCount.class);
job.setJobName("word count demo"); FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1])); job.setMapperClass(WordMapper.class);
job.setReducerClass(WordReducer.class);
//下面一句是优化,把每个节点的map的数据用reduce合并起来,最后把所有节点的reduce输出作为最后一次合并的输入
job.setCombinerClass(WordReducer.class); job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class); System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
wordcount.WordCount.java
package wordcount; import java.io.IOException;
import java.util.StringTokenizer; import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper; public class WordMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
public void map(LongWritable key, Text value, Context ctx) throws InterruptedException, IOException {
StringTokenizer token = new StringTokenizer(value.toString(), " ");
if (token.hasMoreElements()) {
Text name = new Text(token.nextToken());
IntWritable score = new IntWritable(Integer.parseInt(token.nextToken())); ctx.write(name, score);
}
}
}
wordcount.WordMapper.java
package wordcount; import java.io.IOException; import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer; public class WordReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
public void reduce(Text keyin, Iterable<IntWritable> valuein, Context ctx) throws InterruptedException, IOException {
int sum = 0;
int count = 0;
for (IntWritable score : valuein) {
sum += score.get();
count++;
}
ctx.write(keyin, new IntWritable(sum / count));
}
}
wordcount.WordReducer.java
mapreduce排序
package wordcount; import java.io.IOException;
import java.util.StringTokenizer; import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper; public class WordMapper extends Mapper<LongWritable, Text, IntWritable, Text> {
public void map(LongWritable key, Text value, Context ctx) throws InterruptedException, IOException {
String line = value.toString();
IntWritable data = new IntWritable();
data.set(Integer.parseInt(line));
ctx.write(data, new Text(""));
}
}
wordcount.WordMapper.java
package wordcount; import java.io.IOException; import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer; public class WordReducer extends Reducer<IntWritable, Text, IntWritable, Text> {
public void reduce(IntWritable keyin, Iterable<Text> valuein, Context ctx) throws InterruptedException, IOException {
for (Text text : valuein) {
ctx.write(keyin, text);
}
}
}
wordcount.WordReducer.java
package wordcount; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class WordCount {
public static void main(String[] args) throws Exception {
Job job = new Job();
job.setJarByClass(WordCount.class);
job.setJobName("word count demo"); FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1])); job.setMapperClass(WordMapper.class);
job.setReducerClass(WordReducer.class);
//下面一句是优化,把每个节点的map的数据用reduce合并起来,最后把所有节点的reduce输出作为最后一次合并的输入
job.setCombinerClass(WordReducer.class); job.setOutputKeyClass(IntWritable.class);
job.setOutputValueClass(Text.class); System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
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命令
注意修改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的结构
在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");
} }
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的左边
hadoop1.0.3学习笔记的更多相关文章
- 从零开始搭建.NET Core 2.0 API(学习笔记一)
从零开始搭建.NET Core 2.0 API(学习笔记一) 一. VS 2017 新建一个项目 选择ASP.NET Core Web应用程序,再选择Web API,选择ASP.NET Core 2. ...
- Swift 2.0 字符串学习笔记(建议掌握OC字符串知识的翻阅)
自己公司开现在使用OC语言在写,但Swift似乎是苹果更推荐使用的开发语言,估计也是未来开发的趋势,自己以前有接触swift,但又由于公司的项目赶,也没有时间去好好地学习这款开发语言.现在年底了,项目 ...
- Spring 4.0.2 学习笔记(2) - 自动注入及properties文件的使用
接上一篇继续, 学习了基本的注入使用后,可能有人会跟我一样觉得有点不爽,Programmer的每个Field,至少要有一个setter,这样spring配置文件中才能用<property> ...
- Spring 4.0.2 学习笔记(1) - 最基本的注入
1. 添加maven支持 <dependency> <groupId>org.springframework</groupId> <artifactId> ...
- Google Guava14.0 瓜娃学习笔记
Guava 是java api的增强与扩展,提供复杂的java 数据结构,使你的代码更简短精炼,具有良好的可读性.看看guava给我们提供了哪些很酷的功能: 集合创建: Map<String, ...
- 《Ruby语言入门教程v1.0》学习笔记-01
<Ruby语言入门教程v1.0> 编著:张开川 邮箱:kaichuan_zhang@126.com 想要学习ruby是因为公司的自动化测试使用到了ruby语言,但是公司关于ruby只给了一 ...
- Swift2.0 函数学习笔记
最近又有点忙,忙着找工作,忙着适应这个新环境.现在好了,上班两周周了,也适应过来了,又有时间安安静静的就行我们前面的学习了.今天这篇笔记,记录的就是函数的使用.下面这些代码基本上是理清楚了函数的额使用 ...
- Vue1.0基础学习笔记整理
最近一直在使用Vue.js开发项目,现将在学习过程中遇到的一些学习小细节总结如下: 1.只处理单次插值,今后的数据变化就不会再引起插值更新了 <span>This will never c ...
- vue2.0 路由学习笔记
昨天温故了一下vue2.0的路由 做个笔记简单记录一下! 1.首相和vue1.0一样 要使用vuejs的路由功能需要先引入vue-router.js 2.然后修改原有a标签处代码 这里以一个ul li ...
随机推荐
- 【原创】Linux下的ngix服务器安装步骤
1.首先下载ngix的源码linux版本[1.5.8版本] http://nginx.org/en/download.html 2.下载PCRE library,是安装ngix的必备包之一 [root ...
- C#生成高清缩略图 (装在自OPEN经验库)
来源 http://www.open-open.com/lib/view/open1389943861320.html 代码如下实现图片的高清缩略图 /// <summary> /// 为 ...
- STM8S103之独立看门狗和窗口看门狗
独立看门狗时钟来源为LSI:窗口看门狗时钟来源为CPU: 窗口看门狗窗口的含义是:喂狗必须在一定的窗口期内完成,不能过早也不能过晚. 总结:防止程序复位,用独立看门狗. 独立看门狗使用的流程:参见库函 ...
- RocketMQ学习笔记(2)----Broker的集群四种方式
RocketMQ推荐了几种Broker集群方式,这里的Slave不可写,但可读,类似于Mysql主备方式 1. 单个Master 这是一种风险比较大的集群方式,因为一旦Borker重启或宕机期间,将会 ...
- sql知识小记
1.在sql语句中,单引号嵌套时,使用单引号做转义
- [luogu] P2787 语文1(chin1)- 理理思维(分块)
P2787 语文1(chin1)- 理理思维 题目背景 蒟蒻HansBug在语文考场上,挠了无数次的头,可脑子里还是一片空白. 题目描述 考试开始了,可是蒟蒻HansBug脑中还是一片空白.哦不!准确 ...
- Win7+VS2010:mysql 源代码编译与调试
win7+vs2010源代码编译mysql 近期因为在实习,工作重点在于一些数据库的开发,为了更好的理解数据库的实现机制.眼下萌生了要研究一下mysql数据库源代码的想法.那么好吧,说干就干.首先我们 ...
- elasticsearch中的几个概念总结
1.Geo spatial search : 地理空间搜索,可以在搜索查询中指定的某一距离内查找所要的内容.也可以返回以当前为圆心,逐渐添加圆的半径.直到找到所匹配到的内容. 參考:http://ww ...
- SVN版本号管理工具使用中常见的代码提交冲突问题的解决方法
相信刚開始学习使用SVN的小伙伴在项目合作开发的过程中一定常常遇到一些影响到自己编写的代码的苦恼.我这里列举了几种常见的问题以及问题的解决方法: 1.误删除和误操作的问题 问题1:有A和B两个人一块合 ...
- Centos7+httpd+fastcgi+rails安装
搭建的环境: centos7 Apache/2.4.6 fastcgi2.4.6 rails4 在安装fastcgi的时候遇到了问题: 问题: .... .. In file included fro ...