使用root用户登录
(1) 设置静态ip
在centos桌面右上角的图标上,右键修改
重启网卡,执行命令service network restart
验证:执行命令ifconfig
(2) 修改主机名
<1>修改当前回话中的主机名,执行命令hostname hadoop
<2>修改配置文件中的主机名,执行命令vi /etc/sysconfig/network
验证:重启机器
(3) 把主机名和ip绑定
执行命令 vi /etc/hosts, 增加一行内容
192.168.80.100 hadoop
保存退出
验证:ping hadoop 《ctrl+c结束》
(4) 关闭防火墙
执行命令 service iptables stop
验证: service iptables status
(5) 关闭防火墙自动运行
执行命令 chkconfig iptables off
验证: chkconfig --list | grep iptables
(6) SSH(secure shell)的免密码登录
<1>执行命令 ssh-keygen -t rsa 长生秘钥,位于~/.ssh文件夹中
cd ~ cd .ssh/
<2>执行命令 cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys
验证:ssh localhost
(7) 安装jdk
<1>执行命令rm -rf /usr/local/* 删除所有内容
<2>使用winscp把jdk文件冲windows复制到/usr/local目录下
<3>执行命令 chmod u+x jdk-6u24-linux-i586.bin 授予执行权限
<4>执行命令 ./jdk-6u24-linux-i586.bin 解压缩 (在当前文件/usr/local下)
<5>执行命令 mv jdk1.6.0_24 jdk 重命名
<6>执行命令 vi /etc/profile 设置环境变量,增加2行内容
export JAVA_HOME=/usr/local/jdk
export PATH=.:$JAVA_HOME/bin:$PATH 保存退出
执行命令 source /etc/profile 让该设置立即生效
验证:java -version
(8) 安装hadoop
<1>执行命令 tar -zxvf hadoop-1.1.2.tar.gz进行解压缩
<2>执行命令 mv hadoop-1.1.2 hadoop 重命名
<3>执行命令 vi /etc/profile 设置环境变量,增加一行内容
export HADOOP_HOME=/usr/local/hadoop
修改了一行内容
export PATH=.:$HADOOP_HOME:$JAVA_HOME/bin:$PATH
保存退出
执行命令 source /etc/profile 让该设置立即生效
<4> 修改hadoop的配置文件,位于$HADOOP_HOME/conf的目录下
修改4个配置文件,分别是:
hadoop-env.sh
core-site.xml
hdfs-site.xml
mapred-site.xml
1.hadoop-env.sh 修改第九行 export JAVA_HOME=/usr/local/jdk/ 2.core-site.xml <configuration> <property> <name>fs.default.name</name> <value>hdfs://hadoop:9000</value> <description>change your own hostname</description> </property> <property> <name>hadoop.tmp.dir</name> <value>/usr/local/hadoop/tmp</value> </property> </configuration> 3.hdfs-site.xml <configuration> <property> <name>dfs.replication</name> <value>1</value> </property> <property> <name>dfs.permissions</name> <value>false</value> </property> </configuration> 4.mapred-site.xml <configuration> <property> <name>mapred.job.tracker</name> <value>hadoop:9001</value> <description>change your own hostname</description> </property> </configuration>
<5> 执行命令 hadoop namenode -format 对hadoop进行格式化
<6> 执行命令 start-all.sh 启动
验证:(1)执行命令jps,发现5个java进程
分别是:NameNode,DataNode,SecondaryNameNode,JobTracker,TaskTracker
(2)通过浏览器 http://hadoop:50070 和 http:hadoop:50030
可以修改windows的C:\Windows\System32\drivers\etc 的hosts文件
(1) 分布结构 主节点(1个,是hadoop0):NameNode、JobTracker、SecondaryNameNode
从节点(2个,是hadoop1、hadoop2):DataNode、TaskTracker
(2) 各节点重新产生ssh加密文件
<1>执行命令 ssh-keygen -t rsa 长生秘钥,位于~/.ssh文件夹中
cd ~ cd .ssh/
<2>执行命令 cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys
(3) 编辑各个节点的/etc/hosts,在该文件中含有所有节点的ip与hostname的映射信息
(4) 两两节点之间的SSH免密码登陆
ssh-copy-id -i hadoop1
scp /root/.ssh/authorized_keys hadoop1:/root/.ssh/
(5) 把hadoop0的hadoop目录下的logs和tmp删除
(6) 把hadoop0中的jdk、hadoop文件夹复制到hadoop1和hadoop2节点
scp -r /usr/local/jdk hadoop1:/usr/local/
(7) 把hadoop0的/etc/profile复制到hadoop1和hadoop2节点,在目标节点中执行source /etc/profile
(8) 编辑hadoop0的配置文件slaves,改为从节点的hostname,分别是hadoop1和hadoop2
(9) 格式化,在hadoop0节点执行hadoop namenode -format
(10) 启动,在hadoop0节点执行start-all.sh
****注意:对于配置文件core-site.xml和mapred-site.xml在所有节点中都是相同的内容。
(1) 配置新节点的环境
(2) 把新节点的hostname配置到主节点的slaves文件中
(3) 在新节点,启动进程
hadoop-daemon.sh start datanode
hadoop-daemon.sh start tasktracker
(4) 在主节点执行脚本 hadoop dfsadmin -refreshNodes
(1) 把电源,网线
(2) kill -9 进程号
hadoop fs -ls / 查看hdfs的根目录下的内容的
hadoop fs -lsr / 递归查看hdfs的根目录下的内容的
hadoop fs -mkdir /d1 在hdfs上创建文件夹d1
hadoop fs -put <linux source> <hdfs destination> 把数据从linux上传到hdfs的特定路径中
hadoop fs -get <hdfs source> <linux destination> 把数据从hdfs下载到linux的特定路径下
hadoop fs -text <hdfs文件> 查看hdfs中的文件
hadoop fs -rm 删除hdfs中文件
hadoop fs -rmr 删除hdfs中的文件夹
(2) hdfs在对数据存储进行block划分时,如果文件大小超过block,那么按照block大小进行划分;不如block size的,划分为一个块,是实际数据大小。
*****PermissionDenyException 权限不足**********
是整个文件系统的管理节点。它维护着整个文件系统的文件目录树,文件/目录的元信息和每个文件对应的数据块列表。接收用户的操作请求。
文件包括:
fsimage:元数据镜像文件。存储某一时段NameNode内存元数据信息。
edits:操作日志文件。
fstime:保存最近一次checkpoint的时间
以上这些文件是保存在linux的文件系统中。
提供真实文件数据的存储服务。
文件块(block):最基本的存储单位。对于文件内容而言,一个文件的长度大小是size,那么从文件的0偏移开始,按照固定的大小,顺序对文件进行划分并编号,划分好的每一个块称一个Block。HDFS默认Block大小是64MB,以一个256MB文件,共有256/64=4个Block.
不同于普通文件系统的是,HDFS中,如果一个文件小于一个数据块的大小,并不占用整个数据块存储空间
Replication。多复本,默认是三个。
HA的一个解决方案。但不支持热备。配置即可。
执行过程:从NameNode上下载元数据信息(fsimage,edits),然后把二者合并,生成新的fsimage,在本地保存,并将其推送到NameNode,同时重置NameNode的edits.
默认在安装在NameNode节点上,但这样...不安全!
(1)
public class App1 { static final String PATH = "hdfs://hadoop:9000/hello"; public static void main(String[] args) throws Exception { URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory()); //hdfs://hadoop:9000 URL url = new URL(PATH); InputStream in = url.openStream(); IOUtils.copyBytes(in, System.out, 1024, true); } }
(2)
public class App2 { static final String PATH = "hdfs://hadoop:9000/"; static final String DIR = "hdfs://hadoop:9000/d1"; static final String FILE = "/d1/hello"; //FileSystem public static void main(String[] args) throws Exception { FileSystem fileSystem = getFileSystem(); //创建文件夹 hadoop fs -mkdir /d1 mkdir(fileSystem); //删除文件夹 hadoop fs -rm /d1 //remove(fileSystem); //上传文件 hadoop fs -put src des putData(fileSystem); //下载文件 hadoop fs -get src des //getData(fileSystem); //浏览文件夹 list(fileSystem); } private static void list(FileSystem fileSystem) throws IOException { FileStatus[] listStatus = fileSystem.listStatus(new Path("/")); for (FileStatus fileStatus : listStatus) { String idDir = fileStatus.isDir()?"文件夹":"文件"; String permission = fileStatus.getPermission().toString(); short replication = fileStatus.getReplication(); long len = fileStatus.getLen(); String path = fileStatus.getPath().toString(); System.out.println(idDir+"\t"+permission+"\t"+replication+"\t"+len+"\t"+path); } } private static void getData(FileSystem fileSystem) throws IOException { FSDataInputStream in = fileSystem.open(new Path(FILE)); IOUtils.copyBytes(in, System.out,1024, true); } private static void putData(FileSystem fileSystem) throws IOException, FileNotFoundException { FSDataOutputStream out = fileSystem.create(new Path(FILE)); FileInputStream in = new FileInputStream("G:/baiduyun/初级班全套视频/初级班-2-伪分布模式安装hadoop/readme.txt"); IOUtils.copyBytes(in, out, 1024, true); } private static void remove(FileSystem fileSystem) throws IOException { fileSystem.delete(new Path(DIR), true); } private static void mkdir(FileSystem fileSystem) throws IOException { fileSystem.mkdirs(new Path(DIR)); } private static FileSystem getFileSystem() throws IOException, URISyntaxException { FileSystem fileSystem = FileSystem.get(new URI(PATH), new Configuration()); return fileSystem; } }
1.1 RPC (remote procedure call)远程过程调用.
远程过程指的是不是同一个进程。
1.2 RPC至少有两个过程。调用方(client),被调用方(server)。
1.3 client主动发起请求,调用指定ip和port的server中的方法,把调用结果返回给client。
1.4 RPC是hadoop构建的基础。
通过例子获得的认识?
2.1 RPC是一个远程过程调用。
2.2 客户端调用服务端的方法,意味着调用服务端的对象中的方法。
2.3 如果服务端的对象允许客户端调用,那么这个对象必须实现接口。
2.4 如果客户端能够调用到服务端对象的方法,那么这些方法一定位于对象的接口中。
MapReduce是一种分布式计算模型,由Google提出,主要用于搜索领域,解决海量数据的计算问题.
MR由两个阶段组成:Map和Reduce,用户只需要实现map()和reduce()两个函数,即可实现分布式计算,非常简单。
这两个函数的形参是key、value对,表示函数的输入信息。
执行步骤:
1.1 读取输入文件内容,解析成key、value对。对输入文件的每一行,解析成key、value对。每一个键值对调用一次map函数。
1.2 写自己的逻辑,对输入的key、value处理,转换成新的key、value输出。
1.3 对输出的key、value进行分区。
1.4 对不同分区的数据,按照key进行排序、分组。相同key的value放到一个集合中。
1.5 (可选)分组后的数据进行归约。
2.1 对多个map任务的输出,按照不同的分区,通过网络copy到不同的reduce节点。
2.2 对多个map任务的输出进行合并、排序。写reduce函数自己的逻辑,对输入的key、value处理,转换成新的key、value输出。
2.3 把reduce的输出保存到文件中。
public class WordCountApp { static final String INPUT_PATH = "hdfs://hadoop0:9000/hello"; static final String OUT_PATH = "hdfs://hadoop0:9000/out"; public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); final FileSystem fileSystem = FileSystem.get(new URI(INPUT_PATH), conf); final Path outPath = new Path(OUT_PATH); if(fileSystem.exists(outPath)){ fileSystem.delete(outPath, true); } final Job job = new Job(conf , WordCountApp.class.getSimpleName()); //1.1指定读取的文件位于哪里 FileInputFormat.setInputPaths(job, INPUT_PATH); //指定如何对输入文件进行格式化,把输入文件每一行解析成键值对 //job.setInputFormatClass(TextInputFormat.class); //1.2 指定自定义的map类 job.setMapperClass(MyMapper.class); //map输出的<k,v>类型。如果<k3,v3>的类型与<k2,v2>类型一致,则可以省略 //job.setMapOutputKeyClass(Text.class); //job.setMapOutputValueClass(LongWritable.class); //1.3 分区 //job.setPartitionerClass(HashPartitioner.class); //有一个reduce任务运行 //job.setNumReduceTasks(1); //1.4 TODO 排序、分组 //1.5 TODO 规约 //2.2 指定自定义reduce类 job.setReducerClass(MyReducer.class); //指定reduce的输出类型 job.setOutputKeyClass(Text.class); job.setOutputValueClass(LongWritable.class); //2.3 指定写出到哪里 FileOutputFormat.setOutputPath(job, outPath); //指定输出文件的格式化类 //job.setOutputFormatClass(TextOutputFormat.class); //把job提交给JobTracker运行 job.waitForCompletion(true); } /** * KEYIN即k1表示行的偏移量 * VALUEIN即v1表示行文本内容 * KEYOUT即k2表示行中出现的单词 * VALUEOUT即v2表示行中出现的单词的次数,固定值1 */ static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable>{ protected void map(LongWritable k1, Text v1, Context context) throws java.io.IOException ,InterruptedException { final String[] splited = v1.toString().split("\t"); for (String word : splited) { context.write(new Text(word), new LongWritable(1)); } }; } /** * KEYIN即k2表示行中出现的单词 * VALUEIN即v2表示行中出现的单词的次数 * KEYOUT即k3表示文本中出现的不同单词 * VALUEOUT即v3表示文本中出现的不同单词的总次数 * */ static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable>{ protected void reduce(Text k2, java.lang.Iterable<LongWritable> v2s, Context ctx) throws java.io.IOException ,InterruptedException { long times = 0L; for (LongWritable count : v2s) { times += count.get(); } ctx.write(k2, new LongWritable(times)); }; } }
函数 |
输入键值对 |
输出键值对 |
map() |
<k1,v1> |
<k2,v2> |
reduce() |
<k2,{v2}> |
<k3,v3> |
问:在eclipse中的写的代码如何提交作业到JobTracker中的哪?
答:(1)在eclipse中调用的job.waitForCompletion(true)实际上执行如下方法
connect();
info = jobClient.submitJobInternal(conf);
(2)在connect()方法中,实际上创建了一个JobClient对象。
在调用该对象的构造方法时,获得了JobTracker的客户端代理对象JobSubmissionProtocol。
JobSubmissionProtocol的实现类是JobTracker。
(3)在jobClient.submitJobInternal(conf)方法中,调用了
JobSubmissionProtocol.submitJob(...),
即执行的是JobTracker.submitJob(...)。
1.Hadoop的数据类型要求必须实现Writable接口。
2.java基本类型与Hadoop常见基本类型的对照
Long LongWritable
Integer IntWritable
Boolean BooleanWritable
String Text
问:java类型如何转化为hadoop基本类型?
答:调用hadoop类型的构造方法,或者调用set()方法。
new LongWritable(123L);
问:hadoop基本类型如何转化为java类型?
答:对于Text,需要调用toString()方法,其他类型调用get()方法。
public class KpiApp { private static final String INPUT_PATH = "hdfs://hadoop:9000/wlan"; private static final String OUT_PATH = "hdfs://hadoop:9000/out"; public static void main(String[] args) throws Exception { Job job = new Job(new Configuration(), KpiApp.class.getSimpleName()); //1.1 指定输入文件路径 FileInputFormat.setInputPaths(job, INPUT_PATH); //指定哪个类用来格式化输入文件 job.setInputFormatClass(TextInputFormat.class); //1.2 指定自定义的Mapper类 job.setMapperClass(MyMapper.class); //指定输入<k2,v2>的类型 job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(KpiWritable.class); //1.3 指定分区类 job.setPartitionerClass(HashPartitioner.class); job.setNumReduceTasks(1); //1.4 排序、分区 //1.5 合并 //2.2 指定自定义的reduce类 job.setReducerClass(MyReducer.class); //指定输出<k3,v3>的类型 job.setOutputKeyClass(Text.class); job.setOutputValueClass(KpiWritable.class); //2.3 指定输出到哪里 FileOutputFormat.setOutputPath(job, new Path(OUT_PATH)); //设定输出文件的格式化类 job.setOutputFormatClass(TextOutputFormat.class); //把代码嫁给JobTracker执行 job.waitForCompletion(true); } static class MyMapper extends Mapper<LongWritable, Text, Text, KpiWritable>{ @Override protected void map(LongWritable key, Text value, org.apache.hadoop.mapreduce.Mapper<LongWritable, Text, Text, KpiWritable>.Context context) throws IOException, InterruptedException { // TODO Auto-generated method stub String[] splited = value.toString().split("\t"); String msisdn = splited[1]; Text k2 = new Text(msisdn); KpiWritable v2 = new KpiWritable(splited[6],splited[7],splited[8],splited[9]); context.write(k2,v2); } } static class MyReducer extends Reducer<Text, KpiWritable, Text, KpiWritable>{ @Override protected void reduce(Text k2, Iterable<KpiWritable> v2s, org.apache.hadoop.mapreduce.Reducer<Text, KpiWritable, Text, KpiWritable>.Context context) throws IOException, InterruptedException { // TODO Auto-generated method stub long upPackNum = 0L; long downPackNum = 0L; long upPayLoad = 0L; long downPayLoad = 0L; for (KpiWritable kpiWritable : v2s) { upPackNum += kpiWritable.upPackNum; downPackNum += kpiWritable.downPackNum; upPayLoad += kpiWritable.upPayLoad; downPayLoad += kpiWritable.downPayLoad; } KpiWritable v3 = new KpiWritable(upPackNum+"",downPackNum+"", upPayLoad+"", downPayLoad+""); context.write(k2,v3); } } } class KpiWritable implements Writable{ long upPackNum; long downPackNum; long upPayLoad; long downPayLoad; public KpiWritable(){} public KpiWritable(String upPackNum, String downPackNum, String upPayLoad, String downPayLoad){ this.upPackNum = Long.parseLong(upPackNum); this.downPackNum = Long.parseLong(downPackNum); this.upPayLoad = Long.parseLong(upPayLoad); this.downPayLoad = Long.parseLong(downPayLoad); } @Override public void readFields(DataInput in) throws IOException { // TODO Auto-generated method stub this.upPackNum = in.readLong(); this.downPackNum = in.readLong(); this.upPayLoad = in.readLong(); this.downPayLoad = in.readLong(); } @Override public void write(DataOutput out) throws IOException { // TODO Auto-generated method stub out.writeLong(upPackNum); out.writeLong(downPackNum); out.writeLong(upPayLoad); out.writeLong(downPayLoad); } @Override public String toString() { // TODO Auto-generated method stub return upPackNum + "\t" + downPackNum + "\t" + upPayLoad + "\t" + downPayLoad; } }
public class WordCountApp extends Configured implements Tool { static String INPUT_PATH = ""; static String OUT_PATH = ""; @Override public int run(String[] arg0) throws Exception { INPUT_PATH = arg0[0]; OUT_PATH = arg0[1]; Configuration conf = new Configuration(); final FileSystem fileSystem = FileSystem.get(new URI(INPUT_PATH), conf); final Path outPath = new Path(OUT_PATH); if(fileSystem.exists(outPath)){ fileSystem.delete(outPath, true); } final Job job = new Job(conf , WordCountApp.class.getSimpleName()); //打包运行必须执行的秘密方法 job.setJarByClass(WordCountApp.class); //1.1指定读取的文件位于哪里 FileInputFormat.setInputPaths(job, INPUT_PATH); //指定如何对输入文件进行格式化,把输入文件每一行解析成键值对 //job.setInputFormatClass(TextInputFormat.class); //1.2 指定自定义的map类 job.setMapperClass(MyMapper.class); //map输出的<k,v>类型。如果<k3,v3>的类型与<k2,v2>类型一致,则可以省略 //job.setMapOutputKeyClass(Text.class); //job.setMapOutputValueClass(LongWritable.class); //1.3 分区 //job.setPartitionerClass(HashPartitioner.class); //有一个reduce任务运行 //job.setNumReduceTasks(1); //1.4 TODO 排序、分组 //1.5 TODO 规约 //2.2 指定自定义reduce类 job.setReducerClass(MyReducer.class); //指定reduce的输出类型 job.setOutputKeyClass(Text.class); job.setOutputValueClass(LongWritable.class); //2.3 指定写出到哪里 FileOutputFormat.setOutputPath(job, outPath); //指定输出文件的格式化类 //job.setOutputFormatClass(TextOutputFormat.class); //把job提交给JobTracker运行 job.waitForCompletion(true); return 0; } public static void main(String[] args) throws Exception { ToolRunner.run(new WordCountApp(), args); }
static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable>{ protected void map(LongWritable k1, Text v1, Context context) throws java.io.IOException ,InterruptedException { Counter helloCounter = context.getCounter("Sensitive Words", "hello"); String line = v1.toString(); if(line.contains("hello")){ //记录敏感词出现在一行中 helloCounter.increment(1L); } final String[] splited = line.toString().split("\t"); for (String word : splited) { context.write(new Text(word), new LongWritable(1)); } }; }
/** * 问:为什么使用Combiner? * 答:Combiner发生在Map端,对数据进行规约处理,数据量变小了,传送到reduce端的数据量变小了,传输时间变短,作业的整体时间变短。 * * 问:为什么Combiner不作为MR运行的标配,而是可选步骤哪? * 答:因为不是所有的算法都适合使用Combiner处理,例如求平均数。 * * 问:Combiner本身已经执行了reduce操作,为什么在Reducer阶段还要执行reduce操作哪? * 答:combiner操作发生在map端的,处理一个任务所接收的文件中的数据,不能跨map任务执行;只有reduce可以接收多个map任务处理的数据。 * */ ..... //1.5 TODO 规约 job.setCombinerClass(MyCombiner.class); ..... static class MyCombiner extends Reducer<Text, LongWritable, Text, LongWritable>{ protected void reduce(Text k2, java.lang.Iterable<LongWritable> v2s, Context ctx) throws java.io.IOException ,InterruptedException { long times = 0L; for (LongWritable count : v2s) { times += count.get(); } ctx.write(k2, new LongWritable(times)); }; }
/** * 分区的例子必须打成jar运行 * 用处: 1.根据业务需要,产生多个输出文件 * 2.多个reduce任务在运行,提高整体job的运行效率 */ ..... //1.3 指定分区类 job.setPartitionerClass(KpiPartitioner.class); job.setNumReduceTasks(2); ..... static class KpiPartitioner extends HashPartitioner<Text, LongWritable>{ @Override public int getPartition(Text key, LongWritable value, int numReduceTasks) { // TODO Auto-generated method stub return (key.toString().length()==11)?0:1; } }
.....
public class TopKnum { static final String INPUT_PATH = "hdfs://hadoop0:9000/input"; static final String OUT_PATH = "hdfs://hadoop0:9000/out"; static final int K=100; public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); final FileSystem fileSystem = FileSystem.get(new URI(INPUT_PATH), conf); final Path outPath = new Path(OUT_PATH); if(fileSystem.exists(outPath)){ fileSystem.delete(outPath, true); } final Job job = new Job(conf , TopKnum.class.getSimpleName()); FileInputFormat.setInputPaths(job, INPUT_PATH); job.setMapperClass(MyMapper.class); job.setMapOutputKeyClass(LongWritable.class); job.setMapOutputValueClass(LongWritable.class); job.setReducerClass(MyReducer.class); job.setOutputKeyClass(LongWritable.class); job.setOutputValueClass(NullWritable.class); FileOutputFormat.setOutputPath(job, outPath); job.waitForCompletion(true); } static class MyMapper extends Mapper<LongWritable, Text, LongWritable, LongWritable>{ private Long[] top = new Long[K]; { for(int j=0;j<100;j++) top[j]=0L; } @Override protected void map(LongWritable k1, Text v1, org.apache.hadoop.mapreduce.Mapper<LongWritable, Text, LongWritable, LongWritable>.Context context) throws IOException, InterruptedException { // TODO Auto-generated method stub Long temp=Long.parseLong(v1.toString()); if(temp>top[0]){ top[0]=temp; int i=0; for(;i<99&&temp>top[i+1];i++){ top[i]=top[i+1]; } top[i]=temp; } } @Override protected void cleanup( org.apache.hadoop.mapreduce.Mapper<LongWritable, Text, LongWritable, LongWritable>.Context context) throws IOException, InterruptedException { // TODO Auto-generated method stub for(int i=0;i<100;i++){ context.write(new LongWritable(top[i]),new LongWritable(top[i])); } } } static class MyReducer extends Reducer<LongWritable, LongWritable, LongWritable, NullWritable>{ private Long[] top = new Long[K]; { for(int j=0;j<100;j++) top[j]=0L; } @Override protected void reduce(LongWritable k2, Iterable<LongWritable> v2s, org.apache.hadoop.mapreduce.Reducer<LongWritable, LongWritable, LongWritable, NullWritable>.Context arg2) throws IOException, InterruptedException { // TODO Auto-generated method stub for (LongWritable v2 : v2s) { Long temp=v2.get(); if(temp>top[0]) { int i=0; for(;i<99&&temp>top[i+1];i++){ top[i]=top[i+1]; } top[i]=temp; } } } @Override protected void cleanup( org.apache.hadoop.mapreduce.Reducer<LongWritable, LongWritable, LongWritable, NullWritable>.Context context) throws IOException, InterruptedException { // TODO Auto-generated method stub for(int i=0;i<100;i++){ context.write(new LongWritable(top[i]), NullWritable.get()); } } } }
是一个为分布式应用所设计的分布的、开源的协调服务,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,简化分布式应用协调及其管理的难度,提供高性能的分布式服务。zk可以用来保证数据在zk集群之间的数据的事务性一致。
(1) zk服务器集群规模不小于3个节点,要求各服务器之间系统时间要保持一致。
(2) 在hadoop0的/usr/local目录下,解压缩zk....tar.gz,设置环境变量
(3) 在conf目录下,修改文件 vi zoo_sample.cfg zoo.cfg
(4) 编辑该文件,执行vi zoo.cfg
修改dataDir=/usr/local/zk/data
新增server.0=hadoop0:2888:3888
server.1=hadoop1:2888:3888
server.2=hadoop2:2888:3888
(5) 创建文件夹mkdir /usr/local/zk/data
(6) 在data目录下,创建文件myid,值为0
(7) 把zk目录复制到hadoop1和hadoop2中
scp -r zk/ hadoop1:/usr/local/
scp -r zk/ hadoop2:/usr/local/
scp /etc/profile hadoop1:/etc
scp /etc/profile hadoop2:/etc
记得:source /etc/profile
(8) 把hadoop1中相应的myid的值改为1
把hadoop2中相应的myid的值改为2
(9) 启动,在三个节点上分别执行命令zkServer.sh start
(10) 检验,在三个节点上分别执行命令zkServer.sh status
zkCli.sh
是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBase技术可在廉价PC Server上搭建起大规模结构化存储集群。HBase不同于一般的关系数据库,其一,HBase适用于存储非结构化数据的数据库;其二,HBase是基于列而不是基于行的模式。
1、数据类型:HBase只有简单的字符串类型,所有类型都是交由用户自己处理的,它只保存字符串。而关系数据库有丰富的类型选择和存储方式。
2、数据操作:HBase操作只有简单的插入、查询、删除、清空等操作,表和表之间是分离的,没有复杂的表和表之间的关系,所以不能也没有必要实现表和表之间的关联等。而传统的关系数据通常有各种各样的函数、连接操作等。
3、存储模式:HBase是基于列存储的,每个列都由几个文件保存,不同列族的文件是分离的。传统的关系数据库是基于表格结构和行模式保存的。
4、数据维护:确切的说,HBase的更新操作应该不叫跟新,虽然一个主键或列会对应新的版本,但它的旧版本仍然会保留,所以实际上是插入了新的数据,而不是传统关系数据库里面的替换修改
5、可伸缩性:HBase这类分布式数据库就是为了这个目的而开发出来的,所以它能够轻易地增加或减少硬件的数量,并且对错误的兼容性比较高。而传统的关系数据库通常需要增加中间层才能实现类似的功能。
1.1 表(table),是存储管理数据的。
1.2 行键(row key),类似于MySQL中的主键。
行键是HBase表天然自带的。
1.3 列族(column family),列的集合。
HBase中列族是需要在定义表时指定的,列是在插入记录时动态增加的。
HBase表中的数据,每个列族单独一个文件。
1.4 时间戳(timestamp),列(也称作标签、修饰符)的一个属性。
行键和列确定的单元格,可以存储多个数据,每个数据含有时间戳属性,数据具有版本特性。
如果不指定时间戳或者版本,默认取最新的数据。
1.5 存储的数据都是字节数组。
1.6 表中的数据是按照行键的顺序物理存储的。
2.1 HBase是适合海量数据(如20PB)的秒级简单查询的数据库。
2.2 HBase表中的记录,按照行键进行拆分, 拆分成一个个的region。
许多个region存储在region server(单独的物理机器)中的。
这样,对表的操作转化为对多台region server的并行查询。
3.1 HBase是主从式结构,HMaster、HRegionServer
4.1 解压缩、重命名、设置环境变量
4.2 修改$HBASE_HOME/conf/hbase-env.sh,修改内容如下:
export JAVA_HOME=/usr/local/jdk
export HBASE_MANAGES_ZK=true
4.2 修改$HBASE_HOME/conf/hbase-site.xml,修改内容如下:
<property> <name>hbase.rootdir</name> <value>hdfs://hadoop0:9000/hbase</value> </property> <property> <name>hbase.cluster.distributed</name> <value>true</value> </property> <property> <name>hbase.zookeeper.quorum</name> <value>hadoop0</value> </property> <property> <name>dfs.replication</name> <value>1</value> </property>
4.3 (可选)文件regionservers的内容为hadoop0
4.4 启动hbase,执行命令start-hbase.sh
******启动hbase之前,确保hadoop是运行正常的,并且可以写入文件*******
4.5 验证:(1)执行jps,发现新增加了3个java进程,分别是HMaster、HRegionServer、HQuorumPeer
(2)使用浏览器访问http://hadoop0:60010
1.1 集群结构,主节点(hmaster)是hadoop0,从节点(region server)是hadoop1和hadoop2
1.2 修改hadoop0上的hbase的几个文件
(1)修改hbase-env.sh的最后一行export HBASE_MANAGES_ZK=false
(2)修改hbase-site.xml文件的hbase.zookeeper.quorum的值为hadoop0,hadoop1,hadoop2
(3)修改regionservers文件(存放的region server的hostname),内容修改为hadoop1、hadoop2
1.3 复制hadoop0中的hbase文件夹到hadoop1、hadoop2中
scp -r hbase hadoop1:/usr/local/
scp -r hbase hadoop2:/usr/local/
复制hadoop0中的/etc/profile到hadoop1、hadoop2中,在hadoop1、hadoop2上执行source /etc/profile
scp -r /etc/profile hadoop1:/etc
scp -r /etc/profile hadoop1:/etc
1.4 首先启动hadoop,然后启动zookeeper集群。
最后在hadoop0上启动hbase集群。
start-all.sh
在三个节点上分别执行命令zkServer.sh start
start-hbase.sh
创建表 |
create '表名称', '列族名称1','列族名称2','列族名称N' |
添加记录 |
put '表名称', '行名称', '列名称:', '值' |
查看记录 |
get '表名称', '行名称' |
查看表中的记录总数 |
count '表名称' |
删除记录 |
delete '表名' ,'行名称' , '列名称' |
删除一张表 |
先要屏蔽该表,才能对该表进行删除,第一步 disable '表名称' 第二步 drop '表名称' |
查看所有记录 |
scan "表名称" |
查看某个表某个列中所有数据 |
scan "表名称" , {COLUMNS=>'列族名称:列名称'} |
更新记录 |
就是重写一遍进行覆盖 |
创建表
>create 'users','user_id','address','info'
表users,有三个列族user_id,address,info
列出全部表
>list
得到表的描述
>describe 'users'
创建表
>create 'users_tmp','user_id','address','info'
删除表
>disable 'users_tmp'
>drop 'users_tmp'
添加记录
put 'users','xiaoming','info:age','24';
put 'users','xiaoming','info:birthday','1987-06-17';
put 'users','xiaoming','info:company','alibaba';
put 'users','xiaoming','address:contry','china';
put 'users','xiaoming','address:province','zhejiang';
put 'users','xiaoming','address:city','hangzhou';
put 'users','zhangyifei','info:birthday','1987-4-17';
put 'users','zhangyifei','info:favorite','movie';
put 'users','zhangyifei','info:company','alibaba';
put 'users','zhangyifei','address:contry','china';
put 'users','zhangyifei','address:province','guangdong';
put 'users','zhangyifei','address:city','jieyang';
put 'users','zhangyifei','address:town','xianqiao';
获取一条记录
1.取得一个id的所有数据
>get 'users','xiaoming'
2.获取一个id,一个列族的所有数据
>get 'users','xiaoming','info'
3.获取一个id,一个列族中一个列的
所有数据
get 'users','xiaoming','info:age'
更新记录
>put 'users','xiaoming','info:age' ,'29'
>get 'users','xiaoming','info:age'
>put 'users','xiaoming','info:age' ,'30'
>get 'users','xiaoming','info:age'
获取单元格数据的版本数据
>get 'users','xiaoming',{COLUMN=>'info:age',VERSIONS=>1}
>get 'users','xiaoming',{COLUMN=>'info:age',VERSIONS=>2}
>get 'users','xiaoming',{COLUMN=>'info:age',VERSIONS=>3}
获取单元格数据的某个版本数据
〉get 'users','xiaoming',{COLUMN=>'info:age',TIMESTAMP=>1364874937056}
全表扫描
>scan 'users'
删除xiaoming值的'info:age'字段
>delete 'users','xiaoming','info:age'
>get 'users','xiaoming'
删除整行
>deleteall 'users','xiaoming'
统计表的行数
>count 'users'
清空表
>truncate 'users'
public class HBaseApp { private static final String TABLE_NAME = "table1"; private static final String FAMILY_NAME = "family1"; private static final String ROW_KEY = "rowkey1"; //创建别表、插入记录、查询一条记录、遍历所有记录、删除表 public static void main(String[] args) throws Exception { Configuration conf = HBaseConfiguration.create(); conf.set("hbase.rootdir", "hdfs://hadoop0:9000/hbase"); //使用eclipse时必须添加这个,否则无法定位 conf.set("hbase.zookeeper.quorum", "hadoop0"); //创建表、删除表HBaseAdmin HBaseAdmin hBaseAdmin = new HBaseAdmin(conf); createTable(hBaseAdmin); //deleteTable(hBaseAdmin); //插入记录、查询一条记录、遍历所有的记录HTable HTable hTable = new HTable(conf , TABLE_NAME); //putRecord(hTable); //getRecord(hTable); scanTable(hTable); hTable.close(); } private static void scanTable(HTable hTable) throws IOException { Scan scan = new Scan(); ResultScanner scanner = hTable.getScanner(scan); for (Result result : scanner) { byte[] value = result.getValue(FAMILY_NAME.getBytes(), "age".getBytes()); System.out.println(result+"\t"+ new String(value)); } } private static void getRecord(HTable hTable) throws IOException { Get get = new Get(ROW_KEY.getBytes()); Result result = hTable.get(get); byte[] value = result.getValue(FAMILY_NAME.getBytes(), "age".getBytes()); System.out.println(result+"\t"+ new String(value)); } private static void putRecord(HTable hTable) throws IOException { Put put = new Put(ROW_KEY.getBytes()); put.add(FAMILY_NAME.getBytes(), "age".getBytes(), "25".getBytes()); hTable.put(put); } private static void deleteTable(HBaseAdmin hBaseAdmin) throws IOException { hBaseAdmin.disableTable(TABLE_NAME); hBaseAdmin.deleteTable(TABLE_NAME); } private static void createTable(HBaseAdmin hBaseAdmin) throws IOException { if(!hBaseAdmin.tableExists(TABLE_NAME)) { HTableDescriptor tableDescriptor = new HTableDescriptor(TABLE_NAME); HColumnDescriptor family = new HColumnDescriptor(FAMILY_NAME); tableDescriptor.addFamily(family); hBaseAdmin.createTable(tableDescriptor); } } }
public class BatchImport { static class BatchImportMapper extends Mapper<LongWritable, Text, LongWritable, Text>{ SimpleDateFormat dateformat1=new SimpleDateFormat("yyyyMMddHHmmss"); Text v2 = new Text(); protected void map(LongWritable key, Text value, Context context) throws java.io.IOException ,InterruptedException { final String[] splited = value.toString().split("\t"); try { final Date date = new Date(Long.parseLong(splited[0].trim())); final String dateFormat = dateformat1.format(date); String rowKey = splited[1]+":"+dateFormat; v2.set(rowKey+"\t"+value.toString()); context.write(key, v2); } catch (NumberFormatException e) { final Counter counter = context.getCounter("BatchImport", "ErrorFormat"); counter.increment(1L); System.out.println("出错了"+splited[0]+" "+e.getMessage()); } }; } static class BatchImportReducer extends TableReducer<LongWritable, Text, NullWritable>{ protected void reduce(LongWritable key, java.lang.Iterable<Text> values, Context context) throws java.io.IOException ,InterruptedException { for (Text text : values) { final String[] splited = text.toString().split("\t"); final Put put = new Put(Bytes.toBytes(splited[0])); put.add(Bytes.toBytes("cf"), Bytes.toBytes("date"), Bytes.toBytes(splited[1])); put.add(Bytes.toBytes("cf"), Bytes.toBytes("msisdn"), Bytes.toBytes(splited[2])); //省略其他字段,调用put.add(....)即可 context.write(NullWritable.get(), put); } }; } public static void main(String[] args) throws Exception { final Configuration configuration = new Configuration(); //设置zookeeper configuration.set("hbase.zookeeper.quorum", "hadoop0"); //设置hbase表名称 configuration.set(TableOutputFormat.OUTPUT_TABLE, "wlan_log"); //将该值改大,防止hbase超时退出 configuration.set("dfs.socket.timeout", "180000"); final Job job = new Job(configuration, "HBaseBatchImport"); job.setMapperClass(BatchImportMapper.class); job.setReducerClass(BatchImportReducer.class); //设置map的输出,不设置reduce的输出类型 job.setMapOutputKeyClass(LongWritable.class); job.setMapOutputValueClass(Text.class); job.setInputFormatClass(TextInputFormat.class); //不再设置输出路径,而是设置输出格式类型 job.setOutputFormatClass(TableOutputFormat.class); FileInputFormat.setInputPaths(job, "hdfs://hadoop0:9000/input"); job.waitForCompletion(true); } }
是一个基于Hadoop的大规模数据分析工具,它提供的SQL-LIKE语言叫Pig Latin,该语言的编译器会把类SQL的数据分析请求转换为一系列经过优化处理的MapReduce运算。
1.Pig是基于hadoop的一个数据处理的框架。
MapReduce是使用java进行开发的,Pig有一套自己的数据处理语言,Pig的数据处理过程要转化为MR来运行。
2.Pig的数据处理语言是数据流方式的,类似于初中做的数学题。
3.Pig基本数据类型:int、long、float、double、chararry、bytearray
复合数据类型:Map、Tuple、Bag
Bag的类型如{('age',31),('name','张三')}
4.1 把pig-0.11.1.tar.gz复制到/usr/local下
4.2 使用命令tar -zxvf pig-0.11.1.tar.gz解压缩
4.3 使用命令mv pig-0.11.1 pig 进行重命名
4.4 编辑文件vi /etc/profile 设置环境变量
export $PIG_HOME=/usr/local/bin
export PATH =......$PIG_HOME/bin....
保存,然后执行source /etc/profile
4.5 编辑文件$PIG_HOME/conf/pig.properties,增加两行如下内容
fs.default.name=hdfs://hadoop0:9000
mapred.job.tracker=hadoop0:9001
5.1 把待处理的数据上传到HDFS中
5.2 把HDFS中的数据转换为pig可以处理的模式
A = LOAD '/wlan' AS (t0:long, msisdn:chararray, t2:chararray, t3:chararray, t4:chararray, t5:chararray, t6:long, t7:long, t8:long, t9:long, t10:chararray);
5.3 把里面的有用的字段抽取出来
B = FOREACH A GENERATE msisdn, t6, t7, t8, t9;
5.4 分组数据
C = GROUP B BY msisdn;
5.5 流量汇总
D = FOREACH C GENERATE group, SUM(B.t6), SUM(B.t7), SUM(B.t8), SUM(B.t9);
5.6 存储到HDFS中
STORE D INTO '/wlan_result';
是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,通过类SQL语句快速实现简单的MapReduce统计,不必开发专门的MapReduce应用,十分适合数据仓库的统计分析。
1.1在hadoop生态圈中属于数据仓库的角色。他能够管理hadoop中的数据,同时可以查询hadoop中的数据。
本质上讲,hive是一个SQL解析引擎。Hive可以把SQL查询转换为MapReduce中的job来运行。
hive有一套映射工具,可以把SQL转换为MapReduce中的job,可以把SQL中的表、字段转换为HDFS中的文件(夹)以及文件中的列。
这套映射工具称之为metastore,一般存放在derby、mysql中。
1.2 hive在hdfs中的默认位置是/user/hive/warehouse,是由配置文件hive-site.xml中属性hive.metastore.warehouse.dir决定的。
(1)解压缩、重命名、设置环境变量
(2)在目录$HIVE_HOME/conf/下,执行命令mv hive-default.xml.template hive-site.xml重命名
在目录$HIVE_HOME/conf/下,执行命令mv hive-env.sh.template hive-env.sh重命名
(3)修改hadoop的配置文件hadoop-env.sh,修改内容如下:
export HADOOP_CLASSPATH=.:$CLASSPATH:$HADOOP_CLASSPATH:$HADOOP_HOME/bin
(4)在目录$HIVE_HOME/bin下面,修改文件hive-config.sh,增加以下内容:
export JAVA_HOME=/usr/local/jdk
export HIVE_HOME=/usr/local/hive
export HADOOP_HOME=/usr/local/hadoop
(1)删除linux上已经安装的mysql相关库信息。rpm -e xxxxxxx --nodeps
执行命令rpm -qa |grep mysql 检查是否删除干净
(2)执行命令 rpm -i mysql-server-******** 安装mysql服务端
(3)启动mysql 服务端,执行命令 mysqld_safe &
(4)执行命令 rpm -i mysql-client-******** 安装mysql客户端
(5)执行命令mysql_secure_installation设置root用户密码
(1)把mysql的jdbc驱动放置到hive的lib目录下
(2)修改hive-site.xml文件,修改内容如下:
<property> <name>javax.jdo.option.ConnectionURL</name> <value>jdbc:mysql://hadoop0:3306/hive?createDatabaseIfNotExist=true</value> </property> <property> <name>javax.jdo.option.ConnectionDriverName</name> <value>com.mysql.jdbc.Driver</value> </property> <property> <name>javax.jdo.option.ConnectionUserName</name> <value>root</value> </property> <property> <name>javax.jdo.option.ConnectionPassword</name> <value>admin</value> </property>
CREATE TABLE t1(id int);
LOAD DATA LOCAL INPATH '/root/id' INTO TABLE t1;
CREATE TABLE t2(id int, name string) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
CREATE TABLE t3(id int) PARTITIONED BY (day int);
LOAD DATA LOCAL INPATH '/root/id' INTO TABLE t3 PARTITION (day=22);
create table t4(id int) clustered by(id) into 4 buckets;
set hive.enforce.bucketing = true;
insert into table t4 select id from t3;
create external table t5(id int) location '/external';
grant all on hive.* to 'root'@'%' identified by 'admin';
flush privileges;
public class App { public static void main(String[] args) throws Exception { Class.forName("org.apache.hadoop.hive.jdbc.HiveDriver"); Connection conn = DriverManager.getConnection("jdbc:hive://hadoop0:10000/default","",""); Statement stmt = conn.createStatement(); String querySql = "select * from default.t1"; ResultSet res = stmt.executeQuery(querySql); while(res.next()) { System.out.println(res.getInt(1)); } } }
是一个用来将Hadoop和关系型数据库中的数据相互转移的工具,可以将一个关系型数据库(MySQL ,Oracle ,Postgres等)中的数据导进到Hadoop的HDFS中,也可以将HDFS的数据导进到关系型数据库中。
SQOOP是用于对数据进行导入导出的。
(1)把MySQL、Oracle等数据库中的数据导入到HDFS、Hive、HBase中
(2)把HDFS、Hive、HBase中的数据导出到MySQL、Oracle等数据库中
sqoop import --connect jdbc:mysql://hadoop0:3306/hive --username root --password admin --table TBLS --fields-terminated-by '\t' --null-string '**' -m 1 --append --hive-import
sqoop import --connect jdbc:mysql://hadoop0:3306/hive --username root --password admin --table TBLS --fields-terminated-by '\t' --null-string '**' -m 1 --append --hive-import --check-column 'TBL_ID' --incremental append --last-value 6
sqoop export --connect jdbc:mysql://hadoop0:3306/hive --username root --password admin --table ids --fields-terminated-by '\t' --export-dir '/ids'
sqoop job --create myjob -- import --connect jdbc:mysql://hadoop0:3306/hive --username root --password admin --table TBLS --fields-terminated-by '\t' --null-string '**' -m 1 --append --hive-import
是一个分布的、可靠的、高可用的海量日志聚合的系统,可用于日志数据收集,日志数据处理,日志数据传输。
1.flume是分布式的日志收集系统,把收集来的数据传送到目的地去。
2.flume里面有个核心概念,叫做agent。agent是一个java进程,运行在日志收集节点。
3.agent里面包含3个核心组件:source、channel、sink。
3.1 source组件是专用于收集日志的,可以处理各种类型各种格式的日志数据,包括avro、thrift、exec、jms、spooling directory、netcat、sequence generator、syslog、http、legacy、自定义。
source组件把数据收集来以后,临时存放在channel中。
3.2 channel组件是在agent中专用于临时存储数据的,可以存放在memory、jdbc、file、自定义。
channel中的数据只有在sink发送成功之后才会被删除。
3.3 sink组件是用于把数据发送到目的地的组件,目的地包括hdfs、logger、avro、thrift、ipc、file、null、hbase、solr、自定义。
4.在整个数据传输过程中,流动的是event。事务保证是在event级别。
5.flume可以支持多级flume的agent,支持扇入(fan-in)、扇出(fan-out)。
6.书写配置文件example
#agent1表示代理名称
agent1.sources=source1
agent1.sinks=sink1
agent1.channels=channel1
#Spooling Directory是监控指定文件夹中新文件的变化,一旦新文件出现,就解析该文件内容,然后写入到channle。写入完成后,标记该文件已完成或者删除该文件。
#配置source1
agent1.sources.source1.type=spooldir
agent1.sources.source1.spoolDir=/root/hmbbs
agent1.sources.source1.channels=channel1
agent1.sources.source1.fileHeader = false
agent1.sources.source1.interceptors = i1
agent1.sources.source1.interceptors.i1.type = timestamp
#配置sink1
agent1.sinks.sink1.type=hdfs
agent1.sinks.sink1.hdfs.path=hdfs://hadoop0:9000/hmbbs
agent1.sinks.sink1.hdfs.fileType=DataStream
agent1.sinks.sink1.hdfs.writeFormat=TEXT
agent1.sinks.sink1.hdfs.rollInterval=1
agent1.sinks.sink1.channel=channel1
agent1.sinks.sink1.hdfs.filePrefix=%Y-%m-%d
#配置channel1
agent1.channels.channel1.type=file
agent1.channels.channel1.checkpointDir=/root/hmbbs_tmp/123
agent1.channels.channel1.dataDirs=/root/hmbbs_tmp/
7.执行命令bin/flume-ng agent -n agent1 -c conf -f conf/example -Dflume.root.logger=DEBUG,console
1.1 黑马论坛日志,数据分为两部分组成,原来是一个大文件,是56GB;以后每天生成一个文件,大约是150-200MB之间;
1.2 日志格式是apache common日志格式;
1.3 分析一些核心指标,供运营决策者使用;
1.4 开发该系统的目的是分了获取一些业务相关的指标,这些指标在第三方工具中无法获得的;
2.1 把日志数据上传到HDFS中进行处理
如果是日志服务器数据较小、压力较小,可以直接使用shell命令把数据上传到HDFS中;
如果是日志服务器数据较大、压力较答,使用NFS在另一台服务器上上传数据;
如果日志服务器非常多、数据量大,使用flume进行数据处理;
2.2 使用MapReduce对HDFS中的原始数据进行清洗;
2.3 使用Hive对清洗后的数据进行统计分析;
2.4 使用Sqoop把Hive产生的统计结果导出到mysql中;
2.5 如果用户需要查看详细数据的话,可以使用HBase进行展现;
3.1 使用shell命令把数据从linux磁盘上传到HDFS中
(根目录)创建apache_logs文件夹 mkdir /apache_logs 把日志文件放入其中
3.1.1 在hdfs中创建目录,命令如下
$HADOOP_HOME/bin/hadoop fs -mkdir /hmbbs_logs
3.1.2 (在/apache_logs)写一个shell脚本,叫做upload_to_hdfs.sh,内容大体如下
yesterday=`date --date='1 days ago' +%Y_%m_%d`
hadoop fs -put /apache_logs/access_${yesterday}.log /hmbbs_logs
3.1.3 把脚本upload_to_hdfs.sh配置到crontab中,执行命令crontab -e, 写法如下
* 1 * * * upload_to_hdfs.sh
3.2 使用MapReduce对数据进行清洗,把原始处理清洗后,放到hdfs的/hmbbs_cleaned目录下,每天产生一个子目录。
hadoop jar /apache_logs/cleaned.jar /hmbbs_logs/access_${yesterday}.log /hmbbs_cleaned/${yesterday} 1>/dev/null ?
/dev/null 代表空设备文件
> 代表重定向到哪里,例如:echo "123" > /home/123.txt
1 表示stdout标准输出,系统默认值是1,所以">/dev/null"等同于"1>/dev/null"
2 表示stderr标准错误
& 表示等同于的意思,2>&1,表示2的输出重定向等同于1
1>/dev/null 首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,说白了就是不显示任何信息。
2>&1 接着,标准错误输出重定向等同于 标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。
3.3 使用hive对清洗后的数据进行统计。
3.3.1 建立一个外部分区表,脚本如下
CREATE EXTERNAL TABLE hmbbs(ip string, atime string, url string) PARTITIONED BY (logdate string) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LOCATION '/hmbbs_cleaned';
3.3.2 增加分区,脚本如下
ALTER TABLE hmbbs ADD PARTITION(logdate='2013_05_30') LOCATION '/hmbbs_cleaned/2013_05_30';
把代码增加到upload_to_hdfs.sh中,内容如下
hive -e "ALTER TABLE hmbbs ADD PARTITION(logdate='${yesterday}') LOCATION '/hmbbs_cleaned/${yesterday}';"
3.3.3 统计每日的pv,代码如下
CREATE TABLE hmbbs_pv_2013_05_30 AS SELECT COUNT(1) AS PV FROM hmbbs WHERE logdate='2013_05_30';
统计每日的注册用户数,代码如下
CREATE TABLE hmbbs_reguser_2013_05_30 AS SELECT COUNT(1) AS REGUSER FROM hmbbs WHERE logdate='2013_05_30' AND INSTR(url,'member.php?mod=register')>0;
统计每日的独立ip,代码如下
CREATE TABLE hmbbs_ip_2013_05_30 AS SELECT COUNT(DISTINCT ip) AS IP FROM hmbbs WHERE logdate='2013_05_30';
统计每日的跳出用户,代码如下
CREATE TABLE hmbbs_jumper_2013_05_30 AS SELECT COUNT(1) AS jumper FROM (SELECT COUNT(ip) AS times FROM hmbbs WHERE logdate='2013_05_30' GROUP BY ip HAVING times=1) e;
把每天统计的数据放入一张表
CREATE TABLE hmbbs_2013_05_30 AS SELECT '2013_05_30', a.pv, b.reguser, c.ip, d.jumper FROM hmbbs_pv_2013_05_30 a JOIN hmbbs_reguser_2013_05_30 b ON 1=1 JOIN hmbbs_ip_2013_05_30 c ON 1=1 JOIN hmbbs_jumper_2013_05_30 d ON 1=1 ;
3.4 使用sqoop把数据导出到mysql中
sqoop export --connect jdbc:mysql://hadoop0:3306/hmbbs --username root --password admin --table hmbbs_logs_stat --fields-terminated-by '\001' --export-dir '/hive/hmbbs_2013_05_30'