本文包含详细的HDFS常用shell命令+MapReduce java编程+HBase常用shell命令+Spark python编程(RDD+df),
本文档纯属个人整理,为了应对大数据期末考试的20分程序填空和20分手撕代码题(一道mapreduce
java,一道spark python)
-ls
命令hdfs dfs -ls [选项] <路径>
-R
:递归显示所有子目录-d
:仅列出目录本身,不列出内容-h
:人类可读格式显示文件大小-t
:按修改时间排序,最近的在前hdfs dfs -ls /user/hadoop
-du
命令hdfs dfs -du [选项] <路径>
-s
:显示汇总值-h
:以人类可读格式显示大小-r
:递归统计子目录-t
:按大小排序输出hdfs dfs -du /data
-count
命令hdfs dfs -count [选项] <路径>
-q
:显示配额信息(配额/剩余/空间)-h
:以人类可读格式显示大小-t
:显示文件类型信息-u
:显示每个用户拥有的文件/目录数hdfs dfs -count /user
-mv
命令hdfs dfs -mv [选项] <源路径> <目的路径>
-f
:强制覆盖已存在的目标文件-p
:保留文件元数据(时间戳、权限等)-t
:指定目标目录-d
:允许迁移非空目录hdfs dfs -mv /data/old.log /backup/
-cp
命令hdfs dfs -cp [选项] <源路径> <目的路径>
-f
:强制覆盖目标文件-p
:保留文件属性(时间戳、所有权、权限)-r
:递归复制目录-d
:跳过创建临时文件-t
:指定目标目录hdfs dfs -cp -p /data/file1 /backup/
-rm
命令hdfs dfs -rm [选项] <路径>
-f
:强制删除,不提示-r
:递归删除-skipTrash
:直接删除(不进入回收站)-t
:指定删除线程数-d
:仅删除空目录hdfs dfs -rm /tmp/expired.log
-put
命令hdfs dfs -put [选项] <本地文件1> <本地文件2> ...
-f
:覆盖目标文件-p
:保留访问和修改时间、所有权和权限-l
:允许源为符号链接-d
:跳过创建临时文件-t
:指定线程数hdfs dfs -put -f log1.log log2.log /data/
-copyFromLocal
命令hdfs dfs -copyFromLocal [选项] <本地文件>
-put
相同(语义化别名)-f
:覆盖目标文件-p
:保留文件属性-l
:允许源为符号链接-d
:跳过创建临时文件hdfs dfs -copyFromLocal -p data.csv /input/
-moveFromLocal
命令hdfs dfs -moveFromLocal [选项] <本地文件>
-f
:覆盖目标文件-p
:保留元数据-t
:指定线程数-d
:跳过创建临时文件hdfs dfs -moveFromLocal -f temp.dat /storage/
-getmerge
命令hdfs dfs -getmerge [选项] <本地合并文件>
-nl
:在每个文件末尾添加换行符-skip-empty-file
:跳过空文件-r
:递归处理子目录-t
:指定临时文件目录hdfs dfs -getmerge -nl /logs/all_logs/ merged.log
-cat
命令hdfs dfs -cat [选项]
-ignoreCrc
:忽略CRC校验-t
:指定最大显示行数-h
:显示头部信息-f
:包含文件名hdfs dfs -cat /output/result.txt
-text
命令hdfs dfs -text [选项]
-a
:显示所有内容,包括非文本字节-t
:限制显示的最大行数-d
:显示详细解压信息-r
:递归处理目录hdfs dfs -text -t 100 /data/compressed.gz
-copyToLocal
命令hdfs dfs -copyToLocal [选项] <本地路径>
-p
:保留访问和修改时间、所有权、权限-ignoreCrc
:跳过校验-crc
:附带CRC校验文件-f
:覆盖目标文件-d
:创建父目录-t
:指定线程数hdfs dfs -copyToLocal -p /output/results ./
-moveToLocal
命令hdfs dfs -moveToLocal [选项] <本地路径>
-crc
:附带CRC校验文件-p
:保留元数据-f
:覆盖目标文件-d
:创建父目录hdfs dfs -moveToLocal -f /tmp/file.txt ./
-mkdir
命令hdfs dfs -mkdir [选项]
-p
:递归创建父目录-m
:设置目录权限模式-t
:指定线程数-d
:设置目录配额hdfs dfs -mkdir -p -m 755 /user/newdir/subdir
-setrep
命令hdfs dfs -setrep [选项] <副本数> <路径>
-R
:递归操作-w
:等待副本调整完成-t
:指定超时时间(毫秒)-m
:仅修改内存中的元数据-d
:在下一次写操作时异步更新hdfs dfs -setrep -R -w 3 /critical_data
-touchz
命令hdfs dfs -touchz [选项] <文件路径>
-a
:仅更新文件的访问时间(atime)-m
:仅更新文件的修改时间(mtime)-t
:指定时间戳,格式为 [[CC]YY]MMDDhhmm[.ss]-r
:将指定文件的时间戳复制到目标文件-d
:指定日期和时间,格式为 YYYY-MM-DD HH:MM:SShdfs dfs -touchz /tmp/lock.file
hdfs dfs -touchz -d "2023-12-01 08:30:00" /tmp/dated.file
hdfs dfs -touchz -a /data/logs/current.log
-stat
命令hdfs dfs -stat [选项] [format] <路径>
-t
:以可读时间格式显示-r
:递归显示目录内容-a
:显示所有属性-m
:仅显示修改时间%b
:文件大小(块大小)%y
:修改时间%r
:副本数%o
:块大小%n
:文件名%a
:访问时间%F
:文件类型hdfs dfs -stat -t "%y %n %r" /data/file
-tail
命令hdfs dfs -tail [选项]
-f
:实时追踪(类似Linux tail -f)-s
:间隔时间(以秒为单位,用于-f模式)-n
:显示的行数-d
:显示详细信息-t
:指定超时时间hdfs dfs -tail /logs/app.log
hdfs dfs -tail -f -s 5 /logs/app.log
hdfs dfs -tail -n 20 /logs/app.log
-chmod
命令hdfs dfs -chmod [选项] <权限模式> <路径>
-R
:递归操作-f
:静默模式,不显示错误信息-t
:指定超时时间-d
:仅修改目录权限,不修改文件hdfs dfs -chmod 755 /public
hdfs dfs -chmod -R 644 /data/logs
hdfs dfs -chmod -R u+x /scripts
-chown
命令hdfs dfs -chown [选项] [用户][:组] <路径>
-R
:递归操作-f
:静默模式,不显示错误信息-t
:指定超时时间-d
:仅修改目录权限,不修改文件hdfs dfs -chown hadoop /data/file.txt
hdfs dfs -chown -R hadoop:analysts /data/reports
-chgrp
命令hdfs dfs -chgrp [选项] <组名> <路径>
-R
:递归操作-f
:静默模式,不显示错误信息-t
:指定超时时间-d
:仅修改目录所属组,不修改文件hdfs dfs -chgrp datagroup /data/file.txt
hdfs dfs -chgrp -R admin /secure
-help
命令hdfs dfs -help [选项] [具体命令]
-a
:显示所有可用命令-d
:显示详细帮助信息-r
:显示相关命令-t
:以简洁格式显示hdfs dfs -help rm
hdfs dfs -help -a
注意:所有命令中的<路径>默认为HDFS路径,本地路径需显式指定协议(如file:///)
并非所有选项在所有HDFS版本中都可用,请根据您的具体版本参考官方文档确认支持的选项。
MapReduce思想在生活中处处可见。或多或少都曾接触过这种思想。MapReduce的思想核心是"分而治之",适用于大量复杂的任务处理场景(大规模数据处理场景)。
这两个阶段合起来正是MapReduce思想的体现。
还有一个比较形象的语言解释MapReduce:
我们要数图书馆中的所有书。你数1号书架,我数2号书架。这就是"Map"。我们人越多,数书就更快。
现在我们到一起,把所有人的统计数加在一起。这就是"Reduce"。
MapReduce是一个分布式运算程序的编程框架,核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在Hadoop集群上。
MapReduce设计并提供了统一的计算框架,为程序员隐藏了绝大多数系统层面的处理细节。为程序员提供一个抽象和高层的编程接口和框架。程序员仅需要关心其应用层的具体计算问题,仅需编写少量的处理应用本身计算问题的程序代码。如何具体完成这个并行计算任务所相关的诸多系统层细节被隐藏起来,交给计算框架去处理:
Map和Reduce为程序员提供了一个清晰的操作接口抽象描述。MapReduce中定义了如下的Map和Reduce两个抽象的编程接口,由用户去编程实现.Map和Reduce,MapReduce处理的数据类型是
Map: (k1; v1) → [(k2; v2)]
Reduce: (k2; [v2]) → [(k3; v3)]
一个完整的mapreduce程序在分布式运行时有三类实例进程:
MRAppMaster
负责整个程序的过程调度及状态协调MapTask
负责map阶段的整个数据处理流程ReduceTask
负责reduce阶段的整个数据处理流程MapReduce 的开发一共有八个步骤, 其中 Map 阶段分为 2 个步骤,Shuffle 阶段 4 个步骤,Reduce 阶段分为 2 个步骤
package com.wordmerger;
import java.io.IOException;
import java.util.Properties;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class WordMerger {
// Map类 - 将输入转为键值对,用于后续去重
// Step 2: 自定义Map逻辑,将输入的每行文本作为键,空字符串作为值输出
public static class Map extends Mapper<Object, Text, Text, Text> {
private static Text text = new Text();
public void map(Object key, Text value, Context context) throws IOException,
InterruptedException {
text = value; // 直接使用输入的Text值作为输出键
context.write(text, new Text("")); // 输出键值对,值为空Text对象
}
}
// Step 7: Reduce阶段 - 实现去重逻辑
public static class Reduce extends Reducer<Text, Text, Text, Text> {
public void reduce(Text key, Iterable<Text> values, Context context) throws IOException,
InterruptedException {
// Shuffle阶段已将相同键的值分组,这里只需输出每个键一次即可实现去重
context.write(key, new Text("")); // 对于每个唯一的键,只输出一次
}
}
public static void main(String[] args) throws Exception {
Properties properties = System.getProperties();
properties.setProperty("HADOOP_USER_NAME", "bduser");
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://10.40.2.147:9000");
String[] otherArgs = new String[] {
"/hadoop2/wyh/input2",
"/hadoop2/wyh/output"
};
if (otherArgs.length != 2) {
System.err.println("Usage: wordmerger and duplicate removal " );
System.exit(2);
}
// 配置Job
Job job = Job.getInstance(conf, "wordmerger and duplicate removal");
job.setJarByClass(WordMerger.class);
// Step 2: 设置Mapper类
job.setMapperClass(Map.class);
// Step 7: 设置Reducer类
job.setReducerClass(Reduce.class);
// 设置输出类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
// Step 1: 设置输入路径和InputFormat (默认为TextInputFormat)
FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
// Step 8: 设置输出路径和OutputFormat (默认为TextOutputFormat)
FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
// 提交作业并等待完成
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
package com.wordcount;
import java.io.IOException;
import java.util.Properties;
import org.apache.hadoop.conf.Configuration;
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.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
public class WordCount {
// 用于区分字符和句子的标识
public static final String CHAR_PREFIX = "CHAR:";
public static final String SENTENCE_PREFIX = "SENT:";
// Step 2: 自定义Map逻辑,处理输入并输出带前缀的键值对
public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(1);
private Text outKey = new Text();
public void map(Object key, Text value, Context context) throws IOException,
InterruptedException {
String line = value.toString();
// 处理每行作为一个句子
outKey.set(SENTENCE_PREFIX + line);
context.write(outKey, one); // 输出(句子前缀+行内容, 1)键值对
// 处理每个字符
char[] chars = line.toCharArray();
for (char c : chars) {
if (!Character.isWhitespace(c)) {
outKey.set(CHAR_PREFIX + c);
context.write(outKey, one); // 输出(字符前缀+字符, 1)键值对
}
}
}
}
// Step 3-6: Shuffle阶段会自动处理分区、排序、规约和分组
// Step 7: Reduce阶段 - 汇总统计结果
public static class IntSumReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
private IntWritable result = new IntWritable();
private Text outputKey = new Text();
public void reduce(Text key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
// 对每个键对应的所有值进行求和
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
String keyStr = key.toString();
// 对于句子,只输出出现次数大于1的
if (keyStr.startsWith(SENTENCE_PREFIX)) {
if (sum > 1) {
outputKey.set(keyStr.substring(SENTENCE_PREFIX.length()));
result.set(sum);
context.write(outputKey, result);
}
}
// 对于字符,正常输出
else if (keyStr.startsWith(CHAR_PREFIX)) {
outputKey.set(keyStr.substring(CHAR_PREFIX.length()));
result.set(sum);
context.write(outputKey, result);
}
}
}
public static void main(String[] args) throws Exception {
Properties properties = System.getProperties();
properties.setProperty("HADOOP_USER_NAME", "bduser");
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://10.40.2.147:9000");
String[] otherArgs = new String[] {
"/hadoop2/wyh/input3",
"/hadoop2/wyh/output4"
};
if (otherArgs.length < 2) {
System.err.println("Usage: wordcount [...] " );
System.exit(2);
}
// 配置Job
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
// Step 2: 设置Mapper类
job.setMapperClass(TokenizerMapper.class);
// Step 7: 设置Reducer类
job.setReducerClass(IntSumReducer.class);
// Step 1: 设置InputFormat
job.setInputFormatClass(TextInputFormat.class);
// 设置输出类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// Step 1: 添加输入路径
for (int i = 0; i < otherArgs.length - 1; ++i) {
FileInputFormat.addInputPath(job, new Path(otherArgs[i]));
}
// Step 8: 设置输出路径和OutputFormat(默认TextOutputFormat)
FileOutputFormat.setOutputPath(job, new Path(otherArgs[otherArgs.length - 1]));
// 提交作业并等待完成
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
package com.secondarysort;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Properties;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.RawComparator;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Partitioner;
import org.apache.hadoop.mapreduce.Reducer;
/**
* 这是一个Hadoop Map/Reduce应用示例。它读取包含每行两个整数的文本输入文件。
* 输出按第一个数字和第二个数字排序,并按第一个数字分组。
*/
public class SecondarySort {
// 自定义WritableComparable类型,实现二次排序的关键
public static class IntPair implements WritableComparable<IntPair> {
private int first = 0;
private int second = 0;
// 设置整数对的值
public void set(int left, int right) {
first = left;
second = right;
}
// 获取第一个元素
public int getFirst() {
return first;
}
// 获取第二个元素
public int getSecond() {
return second;
}
// 实现从DataInput读取字段的方法
@Override
public void readFields(DataInput in) throws IOException {
first = in.readInt() + Integer.MIN_VALUE;
second = in.readInt() + Integer.MIN_VALUE;
}
// 实现向DataOutput写入字段的方法
@Override
public void write(DataOutput out) throws IOException {
out.writeInt(first - Integer.MIN_VALUE);
out.writeInt(second - Integer.MIN_VALUE);
}
// 计算哈希值
@Override
public int hashCode() {
return first * 157 + second;
}
// 实现equals方法,判断两个IntPair是否相等
@Override
public boolean equals(Object right) {
if (right instanceof IntPair) {
IntPair r = (IntPair) right;
return r.first == first && r.second == second;
} else {
return false;
}
}
// Step 4: 自定义比较器,用于排序
public static class Comparator extends WritableComparator {
public Comparator() {
super(IntPair.class);
}
// 比较两个序列化的IntPair对象
public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
return compareBytes(b1, s1, l1, b2, s2, l2);
}
}
// 注册IntPair的比较器
static {
WritableComparator.define(IntPair.class, new Comparator());
}
// 实现compareTo方法,定义排序规则
@Override
public int compareTo(IntPair o) {
// 先按first排序,相同时按second排序
if (first != o.first) {
return first < o.first ? -1 : 1;
} else if (second != o.second) {
return second < o.second ? -1 : 1;
} else {
return 0;
}
}
}
// Step 3: 自定义分区器,决定数据发送到哪个Reducer
public static class FirstPartitioner extends Partitioner<IntPair, IntWritable> {
@Override
public int getPartition(IntPair key, IntWritable value, int numPartitions) {
// 只根据第一个整数决定分区
return Math.abs(key.getFirst() * 127) % numPartitions;
}
}
// Step 6: 自定义分组比较器,决定哪些键会被分到同一组传给Reducer
public static class FirstGroupingComparator implements RawComparator<IntPair> {
// 比较序列化格式
@Override
public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
// 只比较第一个整数部分(4字节)
return WritableComparator.compareBytes(b1, s1, Integer.SIZE / 8, b2, s2, Integer.SIZE / 8);
}
// 比较对象格式
@Override
public int compare(IntPair o1, IntPair o2) {
// 只比较第一个值
int l = o1.getFirst();
int r = o2.getFirst();
return l == r ? 0 : (l < r ? -1 : 1);
}
}
// Step 2: 自定义Map逻辑
public static class MapClass extends Mapper<LongWritable, Text, IntPair, IntWritable> {
private final IntPair key = new IntPair();
private final IntWritable value = new IntWritable();
@Override
public void map(LongWritable inKey, Text inValue, Context context) throws IOException,
InterruptedException {
// 从每行文本中解析出两个整数
StringTokenizer itr = new StringTokenizer(inValue.toString());
int left = 0;
int right = 0;
if (itr.hasMoreTokens()) {
left = Integer.parseInt(itr.nextToken());
if (itr.hasMoreTokens()) {
right = Integer.parseInt(itr.nextToken());
}
// 创建键值对: ((left, right), right)
key.set(left, right);
value.set(right);
context.write(key, value);
}
}
}
// Step 7: 自定义Reduce逻辑
public static class Reduce extends Reducer<IntPair, IntWritable, Text, IntWritable> {
private static final Text SEPARATOR = new Text("-----------------------------------------------");
private final Text first = new Text();
@Override
public void reduce(IntPair key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
// 输出分隔符
context.write(SEPARATOR, null);
// 将第一个整数转为文本
first.set(Integer.toString(key.getFirst()));
// 遍历所有值并输出
for (IntWritable value : values) {
context.write(first, value);
}
}
}
public static void main(String[] args) throws Exception {
Properties properties = System.getProperties();
properties.setProperty("HADOOP_USER_NAME", "bduser");
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://10.40.2.147:9000");
String[] otherArgs = new String[] {
"/hadoop2/wyh/input2",
"/hadoop2/wyh/output2"
};
if (otherArgs.length != 2) {
System.err.println("Usage: secondarysort " );
System.exit(2);
}
// 配置Job
Job job = Job.getInstance(conf, "secondary sort");
job.setJarByClass(SecondarySort.class);
// Step 2: 设置Mapper类
job.setMapperClass(MapClass.class);
// Step 7: 设置Reducer类
job.setReducerClass(Reduce.class);
// Step 3: 设置分区器
job.setPartitionerClass(FirstPartitioner.class);
// Step 6: 设置分组比较器
job.setGroupingComparatorClass(FirstGroupingComparator.class);
// 设置Map输出类型
job.setMapOutputKeyClass(IntPair.class);
job.setMapOutputValueClass(IntWritable.class);
// 设置Reduce输出类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
// Step 1: 设置输入路径和InputFormat(默认TextInputFormat)
FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
// Step 8: 设置输出路径和OutputFormat(默认TextOutputFormat)
FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
// 提交作业并等待完成
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
层级 | 说明 |
---|---|
表(Table) | 存储数据的逻辑单元,由多行组成。 |
行(Row) | 由 行键(Row Key) 唯一标识,按字典序排序。 |
列族(Column Family) | 列的集合,必须在创建表时定义(如 cf1, cf2)。列族存储在同一个 HFile 中,影响物理存储。 |
列(Column) | 动态添加,格式为 列族:列族限定符(如 cf1:name, cf1:age)。 |
单元格(Cell) | 由 行键 + 列族:列名 + 时间戳 唯一确定,存储实际数据(如 value)。 |
时间戳(Timestamp) | 数据版本控制,默认由系统自动生成,也可手动指定。 |
在HBase中,数据按列族物理存储,每个单元格由(行键, 列族:列名, 时间戳)唯一确定:
Row Key | Column Family & Column | Timestamp | Value |
---|---|---|---|
row1 | cf1:name | 1 | “Alice” |
row1 | cf1:city | 3 | “Beijing” |
row1 | cf2:age | 2 | “25” |
row2 | cf1:name | 4 | “Bob” |
row2 | cf2:age | 5 | “30” |
逻辑视图(用户视角):
Row Key | cf1:name | cf1:city | cf2:age |
---|---|---|---|
row1 | “Alice” | “Beijing” | “25” |
row2 | “Bob” | - | “30” |
注意:
功能 | 命令表达式 | 说明 |
---|---|---|
创建表 | create '表名', '列族1', '列族2', ... |
可指定多个列族 |
禁用表 | disable '表名' |
删除表前必需操作 |
删除表 | drop '表名' |
必须先禁用表 |
查看表是否存在 | exists '表名' |
|
启用表 | enable '表名' |
重新启用已禁用的表 |
功能 | 命令表达式 | 说明 |
---|---|---|
插入/更新数据 | put '表名', '行键', '列族:列名', '值' |
不存在则插入,存在则更新 |
查询单行 | get '表名', '行键' |
获取整行数据 |
带条件查询 | get '表名', '行键', {COLUMN => '列族:列名'} |
查询特定列 |
删除数据 | delete '表名', '行键', '列族:列名' |
删除指定单元格 |
删除整行 | deleteall '表名', '行键' |
删除整行所有版本 |
功能 | 命令表达式 | 说明 |
---|---|---|
全表扫描 | scan '表名' |
默认显示100条,可用LIMIT参数 |
列族扫描 | scan '表名', {COLUMNS => '列族'} |
只扫描指定列族 |
范围扫描 | scan '表名', {STARTROW => '起始行', STOPROW => '结束行'} |
左闭右开区间 |
记录计数 | count '表名' |
统计行数(可能不实时) |
快速计数 | count '表名', INTERVAL => 100000 |
每10万行显示进度 |
功能 | 命令表达式 | 说明 |
---|---|---|
查看表结构 | describe '表名' |
显示列族配置信息 |
修改表结构 | alter '表名', {NAME => '列族', VERSIONS => 3} |
需先禁用表 |
批量操作 | exec '命令文件.hbase' |
执行脚本文件 |
区域信息 | status '表名' |
查看表分区状态 |
快照操作 | snapshot '表名', '快照名' |
需先配置快照功能 |
可以使用多种命令查看数据库是否创建成功:
以列表形式显示所有数据表:
hbase(main):002:0> list
查看表的结构:
hbase(main):003:0> describe 'student'
查询表是否存在:
hbase(main):004:0> exists 'student'
查询表是否可用:
hbase(main):005:0> is_enabled 'student'
使用 put
命令向表中插入数据。HBase 中的列是由列族前缀和列的名字组成的,以冒号间隔。
hbase(main):006:0> put 'student', 'row1', 'score:a', 'value1'
hbase(main):007:0> put 'student', 'row2', 'score:b', 'value2'
hbase(main):008:0> put 'student', 'row3', 'score:c', 'value3'
可以使用 scan
和 get
命令来检查插入结果:
扫描整个表:
hbase(main):009:0> scan 'student'
获取特定行的数据:
hbase(main):010:0> get 'student', 'row1'
删除表需要两步操作:
disable
命令使表无效:hbase(main):011:0> disable 'student'
drop
命令删除表:hbase(main):012:0> drop 'student'
Resilient Distributed Dataset (弹性分布式数据集) 是 Spark 的核心概念,是一个不可变的、可分区的分布式数据集合,可以并行操作。RDD具有以下特点:
新函数:
sc.parallelize()
:将本地集合转换为分布式RDDsc.textFile()
:从文本文件创建RDD# 创建包含整数的RDD
# sc.parallelize() 将本地Python列表转换为分布式数据集
intRDD = sc.parallelize([3, 1, 2, 5, 5])
# 查看RDD内容和类型
intRDD.collect() # 输出: [3, 1, 2, 5, 5]
type(intRDD) # 输出: pyspark.rdd.RDD
# 创建包含字符串的RDD
stringRDD = sc.parallelize(["浙江省", "天津市", "北京市", "上海市", "浙江省", "广东省"])
stringRDD.collect() # 输出: ['浙江省', '天津市', '北京市', '上海市', '浙江省', '广东省']
# 从HDFS文本文件创建RDD
textFileRDD = sc.textFile("/hadoop2/zhangsan/inputEnglish/LICENSE.txt")
textFileRDD.take(2) # 显示前两行内容
转换操作创建一个新的RDD,不会触发计算。这些操作都是惰性的,只有在动作操作时才会执行。
新函数:
map()
:对每个元素应用函数filter()
:筛选满足条件的元素distinct()
:去除重复元素flatMap()
:对每个元素应用函数并扁平化结果# map: 对每个元素应用函数
# 使用具名函数
def addOne(x):
return x + 1
intRDD.map(addOne).collect() # 输出: [4, 2, 3, 6, 6]
# 使用匿名函数(lambda)
intRDD.map(lambda x: x + 1).collect() # 输出: [4, 2, 3, 6, 6]
# 对字符串使用map
stringRDD.map(lambda x: "籍贯:" + x).collect() # 输出: ['籍贯:浙江省', '籍贯:天津市', ...]
# filter: 筛选满足条件的元素
intRDD.filter(lambda x: x < 3).collect() # 输出: [1, 2]
intRDD.filter(lambda x: x == 3).collect() # 输出: [3]
intRDD.filter(lambda x: 1 < x and x < 5).collect() # 输出: [3, 2]
# 字符串筛选
stringRDD.filter(lambda x: "市" in x).collect() # 输出: ['天津市', '北京市', '上海市']
# distinct: 去除RDD中的重复元素
intRDD.distinct().collect() # 输出: [1, 2, 3, 5]
stringRDD.distinct().collect() # 输出: ['浙江省', '天津市', '北京市', '上海市', '广东省']
新函数:
randomSplit()
:随机分割RDDgroupBy()
:根据函数结果对元素分组# randomSplit: 随机分割RDD为多个子RDD
sRDD = intRDD.randomSplit([0.4, 0.6]) # 按0.4:0.6的比例分割
sRDD[0].collect() # 第一个RDD,约40%的数据
sRDD[1].collect() # 第二个RDD,约60%的数据
# groupBy: 根据函数结果对元素分组
# 按奇偶性分组
gRDD = intRDD.groupBy(lambda x: "even" if (x % 2 == 0) else "odd").collect()
print(gRDD[0][0], sorted(gRDD[0][1])) # 如: 'even' [2]
print(gRDD[1][0], sorted(gRDD[1][1])) # 如: 'odd' [1, 3, 5, 5]
# 更复杂的groupBy示例:按除以2的余数分组
rdd = sc.parallelize([1, 1, 2, 3, 8, 6, 7, 5, 8])
result = rdd.groupBy(lambda x: x % 2).collect()
[[x, sorted(y)] for (x, y) in result] # 输出如: [[0, [2, 6, 8, 8]], [1, [1, 1, 3, 5, 7]]]
新函数:
union()
:合并两个RDD,不去重intersection()
:求两个RDD的交集,会去重subtract()
:从第一个RDD中移除第二个RDD中的元素cartesian()
:计算两个RDD的笛卡尔积# 创建多个RDD进行集合操作
intRDD1 = sc.parallelize([3, 1, 2, 5, 5])
intRDD2 = sc.parallelize([5, 6])
intRDD3 = sc.parallelize([2, 7])
# union: 合并RDD (不去重)
intRDD1.union(intRDD2).union(intRDD3).collect() # 输出: [3, 1, 2, 5, 5, 5, 6, 2, 7]
# intersection: 两个RDD的交集
intRDD1.intersection(intRDD2).collect() # 输出: [5]
# subtract: 第一个RDD中存在但第二个RDD中不存在的元素
intRDD1.subtract(intRDD2).collect() # 输出: [1, 2, 3]
# cartesian: 笛卡尔积
result = intRDD1.cartesian(intRDD2).collect() # 输出类似: [(3,5), (3,6), (1,5), ...]
动作操作会触发实际计算,返回值给驱动程序或写入外部存储系统。
新函数:
collect()
:返回RDD中的所有元素到驱动程序count()
:计算RDD元素数量first()
:返回第一个元素take(n)
:返回前n个元素takeOrdered(n)
:返回自然顺序排序后的前n个元素stats()
, min()
, max()
, sum()
, mean()
:统计函数# collect: 收集所有元素到驱动程序
intRDD.collect() # 输出: [3, 1, 2, 5, 5]
# count: 计算元素数量
intRDD.count() # 输出: 5
# first: 获取第一个元素
intRDD.first() # 输出: 3
# take: 获取前n个元素
intRDD.take(2) # 输出: [3, 1]
# takeOrdered: 排序后取前n个
intRDD.takeOrdered(3) # 输出: [1, 2, 3] (自然顺序)
intRDD.takeOrdered(3, key=lambda x: -x) # 输出: [5, 5, 3] (降序)
# 统计函数
intRDD.stats() # 输出统计摘要: (count: 5, mean: 3.2, stdev: 1.6, max: 5.0, min: 1.0)
intRDD.min() # 输出: 1
intRDD.max() # 输出: 5
intRDD.sum() # 输出: 16
intRDD.mean() # 输出: 3.2
键值对RDD是包含键值对的RDD,有特殊的操作方法。
新函数:
keys()
, values()
:获取所有键或值mapValues()
:仅转换值,保持键不变sortByKey()
:按键排序reduceByKey()
:按键归约groupByKey()
:按键分组join()
, leftOuterJoin()
, rightOuterJoin()
:连接操作countByKey()
:统计每个键的出现次数lookup()
:查找指定键的所有值# 创建键值对RDD
kvRDD = sc.parallelize([("1001", "肇事逃逸"), ("1001", "无证驾驶"), ("1003", "酒后驾驶"), ("1001", "酒驾")])
# 基本操作
kvRDD.keys().collect() # 输出: ['1001', '1001', '1003', '1001']
kvRDD.values().collect() # 输出: ['肇事逃逸', '无证驾驶', '酒后驾驶', '酒驾']
# 按键筛选
kvRDD.filter(lambda kv: kv[0] < "1002").collect() # 输出: [('1001', '肇事逃逸'), ('1001', '无证驾驶'), ('1001', '酒驾')]
# 转换值
kvRDD.mapValues(lambda v: v + "情节严重").collect() # 输出: [('1001', '肇事逃逸情节严重'), ...]
# 排序
kvRDD.sortByKey().collect() # 按键升序
kvRDD.sortByKey(ascending=False).collect() # 按键降序
# 按键归约,合并相同键的值
kvRDD.reduceByKey(lambda x, y: x + "," + y).collect() # 输出: [('1003', '酒后驾驶'), ('1001', '肇事逃逸,无证驾驶,酒驾')]
# 连接操作
kvRDD2 = sc.parallelize([("1001", "于谦"), ("1002", "郭德纲")])
kvRDD.join(kvRDD2).collect() # 内连接
kvRDD.leftOuterJoin(kvRDD2).collect() # 左外连接
kvRDD.rightOuterJoin(kvRDD2).collect() # 右外连接
# 动作操作
kvRDD.countByKey() # 输出: defaultdict(int, {'1001': 3, '1003': 1})
kvRDD.lookup("1001") # 输出: ['肇事逃逸', '无证驾驶', '酒驾']
KV = kvRDD.collectAsMap() # 转换为字典(重复键会被覆盖)
WordCount是大数据处理的"Hello World"。以下是使用Spark RDD实现的词频统计:
# 1. 从文件读取文本
textFile = sc.textFile("/hadoop2/zhangsan/inputEnglish/LICENSE.txt")
# 2. 将文本分割为单词
stringRDD = textFile.flatMap(lambda line: line.split(" "))
# 3. 将每个单词映射为(单词,1)的键值对
wordPairs = stringRDD.map(lambda word: (word, 1))
# 4. 按键(单词)归约,统计每个单词出现次数
counts = wordPairs.reduceByKey(lambda x, y: x + y)
# 5. 保存结果
counts.saveAsTextFile("/spark/output/")
# 查看部分结果(可选)
counts.take(5)
DataFrame是一种分布式的数据集合,组织成命名列的形式,概念上等同于关系数据库中的表。DataFrame提供了比RDD更高级的抽象,支持结构化数据处理和SQL查询。
主要特点:
新函数:
SparkSession.builder.getOrCreate()
:获取SparkSessioncreateDataFrame()
:从RDD创建DataFrameRow()
:创建命名行对象# 1. 读取文本文件创建RDD
RawUserRDD = sc.textFile("/spark/user")
# 2. 解析数据,分割字段
userRDD = RawUserRDD.map(lambda line: line.split("|"))
# 3. 转换为Row对象(带列名)
from pyspark.sql import Row
user_Rows = userRDD.map(lambda p: Row(
userid=int(p[0]),
age=int(p[1]),
gender=p[2],
occupation=p[3]),
zipcode=p[4]
)
# 4. 创建DataFrame
sqlContext = SparkSession.builder.getOrCreate()
user_df = sqlContext.createDataFrame(user_Rows)
# 5. 查看DataFrame结构和内容
user_df.printSchema() # 显示结构
user_df.show(5) # 显示前5行
新函数:
select()
:选择列filter()
:筛选行orderBy()
:排序withColumn()
:添加或替换列show()
:显示内容# 为DataFrame创建别名(便于引用)
df = user_df.alias("df")
# select: 选择特定列
user_df_1 = df.select("userid", "occupation", "gender", "age")
user_df_1.show(5)
# 不同的列引用方式
df.select(df.userid, df.occupation, df.gender, df.age).show(5) # 使用列引用
df[['userid', 'occupation', 'gender', 'age']].show(5) # 使用列表
# filter: 筛选数据
# 方式1: 使用字符串表达式
df.filter("occupation='technician' and gender='M' and age=24").show()
# 方式2: 使用列引用和条件表达式
df.filter((df.occupation == 'technician') & (df.gender == 'M') & (df.age == 24)).show()
# orderBy: 排序
df.select("userid", "occupation", "gender", "age").orderBy("age").show(5) # 升序
df.select("userid", "occupation", "gender", "age").orderBy(df.age.desc()).show(5) # 降序
# 多字段排序
df.orderBy(["age", "gender"], ascending=[0, 1]).show(5) # 年龄降序,性别升序
# 添加计算列
df.select("userid", "gender", "age", (2021 - df.age).alias("birthyear")).show(5)
新函数:
registerTempTable()
/createOrReplaceTempView()
:注册临时表sqlContext.sql()
:执行SQL查询# 注册临时表
user_df.registerTempTable("user_table")
# 执行SQL查询
# 统计总数
sqlContext.sql("SELECT count(*) as user_count FROM user_table").show()
# 选择特定列
sqlContext.sql("SELECT userid, occupation, gender, age FROM user_table").show(5)
# 添加计算列
sqlContext.sql("SELECT userid, gender, age, 2021 - age as birthyear FROM user_table").show(5)
# 条件过滤
sqlContext.sql('SELECT * FROM user_table WHERE occupation="technician" AND gender="M" AND age=24').show()
# 排序
sqlContext.sql("SELECT userid, occupation, gender, age FROM user_table ORDER BY age DESC").show(5)
# 分组聚合
sqlContext.sql("SELECT gender, occupation, COUNT(*) as count FROM user_table GROUP BY gender, occupation ORDER BY count DESC").show(5)
新函数:
groupBy()
:分组聚合agg()
:聚合函数join()
:连接操作distinct()
:去重# 导入聚合函数
from pyspark.sql.functions import count, avg, max, min, sum, desc
# 分组聚合
df.groupBy("gender").count().show()
df.groupBy("gender", "occupation").agg(count("*").alias("人数"), avg("age").alias("平均年龄")).show()
# 连接操作
# 假设有第二个DataFrame
occupation_df = sqlContext.createDataFrame([
("technician", "技术员"),
("scientist", "科学家"),
("engineer", "工程师")
], ["occupation", "chinese_name"])
# 执行连接
df.join(occupation_df, "occupation").select("userid", "occupation", "chinese_name", "gender").show()
# 去重操作
df.select("occupation").distinct().show()