hadoop1.0.3学习笔记
最近要从网上抓取数据下来,然后hadoop来做存储和分析.
呆毛王赛高
月子酱赛高
小唯酱赛高
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
概念太多了,真的讲不完。写过的类也挺多,每天加一点吧
使用下面的程序前先把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>
文件上传:
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"); } }
上面两个文件放在同一个目录就能运行了。这个程序完成了从linux上传到hdfs的操作。
Configuration类是用来读core-site.xml配置的,xml默认在conf文件夹中,FileSystem是hdfs类。先不用管匿名内部类Progressable。
IOUtils工具类用来复制文件流,因为上传到hdfs,所以要用hadoop的输出流;因为是从普通文件系统上传的,所以用java的输入流就行。
Path是hadoop实现的类似于URI的类,常用来表示hdfs中的文件(的路径)
其他的慢慢加...
使用下面的程序前先把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 }
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 }
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是最简单的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的例子不再解释,个人比较懒,没改类名
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
map之后, reduce之前数字会自动从小到大排序,所以直接输出就行
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。最大保存版本数可以在建表时指定
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"); } }
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
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,可以节省空间
原理: 一个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能足够长使得分布均匀
这里使用的是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的左边