2 MapReduce
2 MapReduce
- 1. MapReduce 介绍
- 1.1 MapReduce 设计构思
- 2. MapReduce 编程规范
- 3. Mapper以及Reducer抽象类介绍
- 1.Mapper抽象类的基本介绍
- 2.Reducer抽象类基本介绍
- 4. WordCount示例编写
- 5. MapReduce程序运行模式
- 6. MapReduce的运行机制详解
- 6.1 MapTask 工作机制
- 6.2 ReduceTask 工作机制
- 6.3 Shuffle 过程
- 7. Reduce 端实现 JOIN
- 7.1 需求
- 7.2 实现步骤
- 8. Map端实现 JOIN
- 8.1 概述
- 8.2 实现步骤
- 9. 社交粉丝数据分析
- 9.1 需求分析
- 9.2 实现步骤
- 10. 倒排索引建立
- 10.1 需求分析
- 10.2 代码实现
1. MapReduce 介绍
MapReduce思想在生活中处处可见。或多或少都曾接触过这种思想。MapReduce的思想核心是“分而治之”,适用于大量复杂的任务处理场景(大规模数据处理场景)。即使是发布过论文实现分布式计算的谷歌也只是实现了这种思想,而不是自己原创。
Map负责“分”,即把复杂的任务分解为若干个“简单的任务”来并行处理。可以进行拆分的前提是这些小任务可以并行计算,彼此间几乎没有依赖关系。
Reduce负责“合”,即对map阶段的结果进行全局汇总。
MapReduce运行在yarn集群
—1.ResourceManager
—2.NodeManager
这两个阶段合起来正是MapReduce思想的体现。
还有一个比较形象的语言解释MapReduce:
我们要数图书馆中的所有书。你数1号书架,我数2号书架。这就是“Map”。我们人越多,数书就更快。
现在我们到一起,把所有人的统计数加在一起。这就是“Reduce”。
1.1 MapReduce 设计构思
MapReduce是一个分布式运算程序的编程框架,核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在Hadoop集群上。
既然是做计算的框架,那么表现形式就是有个输入(input),MapReduce操作这个输入(input),通过本身定义好的计算模型,得到一个输出(output)。
对许多开发者来说,自己完完全全实现一个并行计算程序难度太大,而MapReduce就是一种简化并行计算的编程模型,降低了开发并行应用的入门门槛。
Hadoop MapReduce构思体现在如下的三个方面:
1.如何对付大数据处理:分而治之
对相互间不具有计算依赖关系的大数据,实现并行最自然的办法就是采取分而治之的策略。并行计算的第一个重要问题是如何划分计算任务或者计算数据以便对划分的子任务或数据块同时进行计算。不可分拆的计算任务或相互间有依赖关系的数据无法进行并行计算!
2.构建抽象模型:Map和Reduce
MapReduce借鉴了函数式语言中的思想,用Map和Reduce两个函数提供了高层的并行编程抽象模型。
Map: 对一组数据元素进行某种重复式的处理;
Reduce: 对Map的中间结果进行某种进一步的结果整理。
MapReduce中定义了如下的Map和Reduce两个抽象的编程接口,由用户去编程实现:
map: (k1; v1) → [(k2; v2)]
reduce: (k2; [v2]) → [(k3; v3)]
Map和Reduce为程序员提供了一个清晰的操作接口抽象描述。通过以上两个编程接口,大家可以看出MapReduce处理的数据类型是<key,value>键值对。
3.MapReduce框架结构
一个完整的mapreduce程序在分布式运行时有三类实例进程:
MR AppMaster:负责整个程序的过程调度及状态协调;
MapTask:负责map阶段的整个数据处理流程;
ReduceTask:负责reduce阶段的整个数据处理流程。
2. MapReduce 编程规范
MapReduce 的开发一共有八个步骤, 其中 Map 阶段分为 2 个步骤,Shuffle 阶段 4 个步骤,Reduce 阶段分为 2 个步骤
1.Map 阶段 2 个步骤:
1.1 设置 InputFormat 类, 将数据切分为 Key-Value**(K1和V1)** 对, 输入到第二步
1.2 自定义 Map 逻辑, 将第一步的结果转换成另外的 Key-Value(K2和V2) 对, 输出结果
2.Shuffle 阶段 4 个步骤:
2.1 对输出的 Key-Value 对进行分区
2.2 对不同分区的数据按照相同的 Key 排序
2.3 (可选) 对分组过的数据初步规约, 降低数据的网络拷贝
2.4 对数据进行分组, 相同 Key 的 Value 放入一个集合中
3.Reduce 阶段 2 个步骤:
3.1 对多个 Map 任务的结果进行排序以及合并, 编写 Reduce 函数实现自己的逻辑, 对输入的 Key-Value 进行处理, 转为新的 Key-Value(K3和V3)输出
3.2 设置 OutputFormat 处理并保存 Reduce 输出的 Key-Value 数据
3. Mapper以及Reducer抽象类介绍
为了开发我们的MapReduce程序,一共可以分为以上八个步骤,其中每个步骤都是一个class类,我们通过job对象将我们的程序组装成一个任务提交即可。为了简化我们的MapReduce程序的开发,每一个步骤的class类,都有一个既定的父类,让我们直接继承即可,因此可以大大简化我们的MapReduce程序的开发难度,也可以让我们快速的实现功能开发。
MapReduce编程当中,其中最重要的两个步骤就是我们的Mapper类和Reducer类
1.Mapper抽象类的基本介绍
在hadoop2.x当中Mapper类是一个抽象类,我们只需要覆写一个java类,继承自Mapper类即可,然后重写里面的一些方法,就可以实现我们特定的功能,接下来我们来介绍一下Mapper类当中比较重要的四个方法
1.setup方法: 我们Mapper类当中的初始化方法,我们一些对象的初始化工作都可以放到这个方法里面来实现
2.map方法: 读取的每一行数据,都会来调用一次map方法,这个方法也是我们最重要的方法,可以通过这个方法来实现我们每一条数据的处理
3.cleanup方法: 在我们整个maptask执行完成之后,会马上调用cleanup方法,这个方法主要是用于做我们的一些清理工作,例如连接的断开,资源的关闭等等
4.run方法: 如果我们需要更精细的控制我们的整个MapTask的执行,那么我们可以覆写这个方法,实现对我们所有的MapTask更精确的操作控制
2.Reducer抽象类基本介绍
同样的道理,在我们的hadoop2.x当中,reducer类也是一个抽象类,抽象类允许我们可以继承这个抽象类之后,重新覆写抽象类当中的方法,实现我们的逻辑的自定义控制。接下来我们也来介绍一下Reducer抽象类当中的四个抽象方法
1.setup方法: 在我们的ReduceTask初始化之后马上调用,我们的一些对象的初始化工作,都可以在这个类当中实现
2.reduce方法: 所有从MapTask发送过来的数据,都会调用reduce方法,这个方法也是我们reduce当中最重要的方法,可以通过这个方法实现我们的数据的处理
3.cleanup方法: 在我们整个ReduceTask执行完成之后,会马上调用cleanup方法,这个方法主要就是在我们reduce阶段处理做我们一些清理工作,例如连接的断开,资源的关闭等等
4.run方法: 如果我们需要更精细的控制我们的整个ReduceTask的执行,那么我们可以覆写这个方法,实现对我们所有的ReduceTask更精确的操作控制
4. WordCount示例编写
需求:在一堆给定的文本文件中统计输出每一个单词出现的总次数
node01服务器执行以下命令,准备数,数据格式准备如下:
cd /export/servers
vim wordcount.txt
#添加以下内容:
hello hello
world world
hadoop hadoop
hello world
hello flume
hadoop hive
hive kafka
flume storm
hive oozie
将数据文件上传到hdfs上面去
hdfs dfs -mkdir /wordcount/
hdfs dfs -put wordcount.txt /wordcount/
定义一个mapper类
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;import java.io.IOException;// mapper程序: 需要继承 mapper类, 需要传入 四个类型:
/* 在hadoop中, 对java的类型都进行包装, 以提高传输的效率 writablekeyin : k1 Long ---- LongWritablevalin : v1 String ------ Textkeyout : k2 String ------- Textvalout : v2 Long -------LongWritable*/public class MapTask extends Mapper<LongWritable,Text,Text,LongWritable> {/**** @param key : k1* @param value v1* @param context 上下文对象 承上启下功能* @throws IOException* @throws InterruptedException*/@Overrideprotected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {//1. 获取 v1 中数据String val = value.toString();//2. 切割数据String[] words = val.split(" ");Text text = new Text();LongWritable longWritable = new LongWritable(1);//3. 遍历循环, 发给 reducefor (String word : words) {text.set(word);context.write(text,longWritable);}}
}
定义一个reducer类
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;import java.io.IOException;/*** KEYIN : k2 -----Text* VALUEIN : v2 ------LongWritable* KEYOUT : k3 ------ Text* VALUEOUT : v3 ------ LongWritable*/
public class ReducerTask extends Reducer<Text, LongWritable, Text, LongWritable> {@Overrideprotected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {//1. 遍历 values 获取每一个值long v3 = 0;for (LongWritable longWritable : values) {v3 += longWritable.get(); //1}//2. 输出context.write(key,new LongWritable(v3));}
}
定义一个主类,用来描述job并提交job
import com.sun.org.apache.bcel.internal.generic.NEW;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.nativeio.NativeIO;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;// 任务的执行入口: 将八步组合在一起
public class JobMain extends Configured implements Tool {// 在run方法中编写组装八步@Overridepublic int run(String[] args) throws Exception {Job job = Job.getInstance(super.getConf(), "JobMain");//如果提交到集群操作. 需要添加一步 : 指定入口类job.setJarByClass(JobMain.class);//1. 封装第一步: 读取数据job.setInputFormatClass(TextInputFormat.class);TextInputFormat.addInputPath(job,new Path("hdfs://node01:8020/wordcount.txt"));//2. 封装第二步: 自定义 map程序job.setMapperClass(MapTask.class);job.setMapOutputKeyClass(Text.class);job.setMapOutputValueClass(LongWritable.class);//3. 第三步 第四步 第五步 第六步 省略//4. 第七步: 自定义reduce程序job.setReducerClass(ReducerTask.class);job.setOutputKeyClass(Text.class);job.setOutputValueClass(LongWritable.class);//5) 第八步 : 输出路径是一个目录, 而且这个目录必须不存在的job.setOutputFormatClass(TextOutputFormat.class);TextOutputFormat.setOutputPath(job,new Path("hdfs://node01:8020/output"));//6) 提交任务:boolean flag = job.waitForCompletion(true); // 成功 true 不成功 falsereturn flag ? 0 : 1;}public static void main(String[] args) throws Exception {Configuration configuration = new Configuration();JobMain jobMain = new JobMain();int i = ToolRunner.run(configuration, jobMain, args); //返回值 退出码System.exit(i); // 退出程序 0 表示正常 其他值表示有异常 1}
}
提醒:代码开发完成之后,就可以打成jar包放到服务器上面去运行了,实际工作当中,都是将代码打成jar包,开发main方法作为程序的入口,然后放到集群上面去运行
5. MapReduce程序运行模式
本地运行模式
1.mapreduce程序是被提交给LocalJobRunner在本地以单进程的形式运行
2.而处理的数据及输出结果可以在本地文件系统,也可以在hdfs上
3.怎样实现本地运行?写一个程序,不要带集群的配置文件本质是程序的conf中是否有mapreduce.framework.name=local以及yarn.resourcemanager.hostname=local参数
4.本地模式非常便于进行业务逻辑的debug,只要在idea中打断点即可
【本地模式运行代码设置】
configuration.set("mapreduce.framework.name","local");
configuration.set("yarn.resourcemanager.hostname","local");
-----------以上两个是不需要修改的,如果要在本地目录测试, 可有修改hdfs的路径-----------------
TextInputFormat.addInputPath(job,new Path("file:///D:\\wordcount\\input"));
TextOutputFormat.setOutputPath(job,new Path("file:///D:\\wordcount\\output"));
集群运行模式
1.将mapreduce程序提交给yarn集群,分发到很多的节点上并发执行
2.处理的数据和输出结果应该位于hdfs文件系统
3.提交集群的实现步骤:
将程序打成JAR包,然后在集群的任意一个节点上用hadoop命令启动 yarn jar hadoop_hdfs_operate-1.0-SNAPSHOT.jar cn.itcast.hdfs.demo1.JobMain
6. MapReduce的运行机制详解
6.1 MapTask 工作机制
整个Map阶段流程大体如上图所示。
简单概述:inputFile通过split被逻辑切分为多个split文件,通过Record按行读取内容给map(用户自己实现的)进行处理,数据被map处理结束之后交给OutputCollector收集器,对其结果key进行分区(默认使用hash分区),然后写入buffer,每个map task都有一个内存缓冲区,存储着map的输出结果,当缓冲区快满的时候需要将缓冲区的数据以一个临时文件的方式存放到磁盘,当整个map task结束后再对磁盘中这个map task产生的所有临时文件做合并,生成最终的正式输出文件,然后等待reduce task来拉数据。
详细步骤
1.读取数据组件 InputFormat (默认 TextInputFormat) 会通过 getSplits 方法对输入目录中文件进行逻辑切片规划得到 block, 有多少个 block就对应启动多少个 MapTask
2.将输入文件切分为 block 之后, 由 RecordReader 对象 (默认是LineRecordReader) 进行读取, 以 \n 作为分隔符, 读取一行数据, 返回 <key,value>. Key 表示每行首字符偏移值, Value 表示这一行文本内容
3.读取 block 返回 <key,value>, 进入用户自己继承的 Mapper 类中,执行用户重写的 map 函数, RecordReader 读取一行这里调用一次
4.Mapper 逻辑结束之后, 将 Mapper 的每条结果通过 context.write 进行collect数据收集. 在 collect 中, 会先对其进行分区处理,默认使用 HashPartitioner
MapReduce 提供 Partitioner 接口, 它的作用就是根据 Key 或 Value 及 Reducer 的数量来决定当前的这对输出数据最终应该交由哪个 Reduce task 处理, 默认对 Key Hash 后再以 Reducer 数量取模. 默认的取模方式只是为了平均 Reducer 的处理能力, 如果用户自己对 Partitioner 有需求, 可以订制并设置到 Job 上
5.接下来, 会将数据写入内存, 内存中这片区域叫做环形缓冲区, 缓冲区的作用是批量收集 Mapper 结果, 减少磁盘 IO 的影响. 我们的 Key/Value 对以及 Partition 的结果都会被写入缓冲区. 当然, 写入之前,Key 与 Value 值都会被序列化成字节数组
环形缓冲区其实是一个数组, 数组中存放着 Key, Value 的序列化数据和 Key, Value 的元数据信息, 包括 Partition, Key 的起始位置, Value 的起始位置以及 Value 的长度. 环形结构是一个抽象概念。
缓冲区是有大小限制, 默认是 100MB. 当 Mapper 的输出结果很多时, 就可能会撑爆内存, 所以需要在一定条件下将缓冲区中的数据临时写入磁盘, 然后重新利用这块缓冲区. 这个从内存往磁盘写数据的过程被称为 Spill, 中文可译为溢写. 这个溢写是由单独线程来完成, 不影响往缓冲区写 Mapper 结果的线程. 溢写线程启动时不应该阻止 Mapper 的结果输出, 所以整个缓冲区有个溢写的比例 spill.percent. 这个比例默认是 0.8, 也就是当缓冲区的数据已经达到阈值 buffer size * spill percent = 100MB * 0.8 = 80MB, 溢写线程启动, 锁定这 80MB 的内存, 执行溢写过程. Mapper 的输出结果还可以往剩下的 20MB 内存中写, 互不影响
6.当溢写线程启动后, 需要对这 80MB 空间内的 Key 做排序 (Sort). 排序是 MapReduce 模型默认的行为, 这里的排序也是对序列化的字节做的排序
如果 Job 设置过 Combiner, 那么现在就是使用 Combiner 的时候了. 将有相同 Key 的 Key/Value 对的 Value 合并在起来, 减少溢写到磁盘的数据量. Combiner 会优化 MapReduce 的中间结果, 所以它在整个模型中会多次使用 \ 那哪些场景才能使用 Combiner 呢? 从这里分析, Combiner 的输出是 Reducer 的输入, Combiner 绝不能改变最终的计算结果. Combiner 只应该用于那种 Reduce 的输入 Key/Value 与输出 Key/Value 类型完全一致, 且不影响最终结果的场景. 比如累加, 最大值等. Combiner 的使用一定得慎重, 如果用好, 它对 Job 执行效率有帮助, 反之会影响 Reducer 的最终结果
7.合并溢写文件, 每次溢写会在磁盘上生成一个临时文件 (写之前判断是否有 Combiner), 如果 Mapper 的输出结果真的很大, 有多次这样的溢写发生, 磁盘上相应的就会有多个临时文件存在. 当整个数据处理结束之后开始对磁盘中的临时文件进行 Merge 合并, 因为最终的文件只有一个, 写入磁盘, 并且为这个文件提供了一个索引文件, 以记录每个reduce对应数据的偏移量
【mapTask的一些基础设置配置】
配置 默认值 解释
mapreduce.task.io.sort.mb 100 设置环型缓冲区的内存值大小
mapreduce.map.sort.spill.percent 0.8 设置溢写的比例
mapreduce.cluster.local.dir ${hadoop.tmp.dir}/mapred/local 溢写数据目录
mapreduce.task.io.sort.factor 10 设置一次合并多少个溢写文件
6.2 ReduceTask 工作机制
Reduce 大致分为 copy、sort、reduce 三个阶段,重点在前两个阶段。copy 阶段包含一个 eventFetcher 来获取已完成的 map 列表,由 Fetcher 线程去 copy 数据,在此过程中会启动两个 merge 线程,分别为 inMemoryMerger 和 onDiskMerger,分别将内存中的数据 merge 到磁盘和将磁盘中的数据进行 merge。待数据 copy 完成之后,copy 阶段就完成了,开始进行 sort 阶段,sort 阶段主要是执行 finalMerge 操作,纯粹的 sort 阶段,完成之后就是 reduce 阶段,调用用户定义的 reduce 函数进行处理
详细步骤:
1.Copy阶段,简单地拉取数据。Reduce进程启动一些数据copy线程(Fetcher),通过HTTP方式请求maptask获取属于自己的文件。
2.Merge阶段。这里的merge如map端的merge动作,只是数组中存放的是不同map端copy来的数值。Copy过来的数据会先放入内存缓冲区中,这里的缓冲区大小要比map端的更为灵活。merge有三种形式:内存到内存;内存到磁盘;磁盘到磁盘。默认情况下第一种形式不启用。当内存中的数据量到达一定阈值,就启动内存到磁盘的merge。与map 端类似,这也是溢写的过程,这个过程中如果你设置有Combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件。第二种merge方式一直在运行,直到没有map端的数据时才结束,然后启动第三种磁盘到磁盘的merge方式生成最终的文件。
3.合并排序。把分散的数据合并成一个大的数据后,还会再对合并后的数据排序。
4.对排序后的键值对调用reduce方法,键相等的键值对调用一次reduce方法,每次调用会产生零个或者多个键值对,最后把这些输出的键值对写入到HDFS文件中。
6.3 Shuffle 过程
map 阶段处理的数据如何传递给 reduce 阶段,是 MapReduce 框架中最关键的一个流程,这个流程就叫 shuffle
shuffle: 洗牌、发牌 ——(核心机制:数据分区,排序,分组,规约,合并等过程)
shuffle 是 Mapreduce 的核心,它分布在 Mapreduce 的 map 阶段和 reduce 阶段。一般把从 Map 产生输出开始到 Reduce 取得数据作为输入之前的过程称作 shuffle。
1.Collect阶段:将 MapTask 的结果输出到默认大小为 100M 的环形缓冲区,保存的是 key/value,Partition 分区信息等。
2.Spill阶段:当内存中的数据量达到一定的阀值的时候,就会将数据写入本地磁盘,在将数据写入磁盘之前需要对数据进行一次排序的操作,如果配置了 combiner,还会将有相同分区号和 key 的数据进行排序。
3.Merge阶段:把所有溢出的临时文件进行一次合并操作,以确保一个 MapTask 最终只产生一个中间数据文件。
4.Copy阶段:ReduceTask 启动 Fetcher 线程到已经完成 MapTask 的节点上复制一份属于自己的数据,这些数据默认会保存在内存的缓冲区中,当内存的缓冲区达到一定的阀值的时候,就会将数据写到磁盘之上。
5.Merge阶段:在 ReduceTask 远程复制数据的同时,会在后台开启两个线程对内存到本地的数据文件进行合并操作。
6.Sort阶段:在对数据进行合并的同时,会进行排序操作,由于 MapTask 阶段已经对数据进行了局部的排序,ReduceTask 只需保证 Copy 的数据的最终整体有效性即可。
Shuffle 中的缓冲区大小会影响到 mapreduce 程序的执行效率,原则上说,缓冲区越大,磁盘io的次数越少,执行速度就越快
缓冲区的大小可以通过参数调整, 参数:mapreduce.task.io.sort.mb 默认100M
7. Reduce 端实现 JOIN
7.1 需求
假如数据量巨大,两表的数据是以文件的形式存储在 HDFS 中, 需要用 MapReduce 程序来实现以下 SQL 查询运算
select a.id,a.date,b.name,b.category_id,b.price from t_order a left join t_product b on a.pid = b.id
商品表
订单数据表
7.2 实现步骤
通过将关联的条件作为map输出的key,将两表满足join条件的数据并携带数据所来源的文件信息,发往同一个reduce task,在reduce中进行数据的串联
1.定义orderBean
import org.apache.hadoop.io.Writable;import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;public class OrderJoinBean implements Writable {private String id=""; // 订单idprivate String date=""; //订单时间private String pid=""; // 商品的idprivate String amount=""; // 订单的数量private String name=""; //商品的名称private String categoryId=""; // 商品的分类idprivate String price=""; //商品的价格public String getId() {return id;}public void setId(String id) {this.id = id;}public String getDate() {return date;}public void setDate(String date) {this.date = date;}public String getPid() {return pid;}public void setPid(String pid) {this.pid = pid;}public String getAmount() {return amount;}public void setAmount(String amount) {this.amount = amount;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getCategoryId() {return categoryId;}public void setCategoryId(String categoryId) {this.categoryId = categoryId;}public String getPrice() {return price;}public void setPrice(String price) {this.price = price;}@Overridepublic String toString() {return id + "\t" + date + "\t" + pid + "\t" + amount + "\t" + name + "\t" + categoryId + "\t" + price;}@Overridepublic void write(DataOutput out) throws IOException {out.writeUTF(id);out.writeUTF(date);out.writeUTF(pid);out.writeUTF(amount);out.writeUTF(name);out.writeUTF(categoryId);out.writeUTF(price);}@Overridepublic void readFields(DataInput in) throws IOException {id = in.readUTF();date = in.readUTF();pid = in.readUTF();amount = in.readUTF();name = in.readUTF();categoryId = in.readUTF();price = in.readUTF();}
}
2.定义 Mapper
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputSplit;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;import java.io.IOException;public class MapperJoinTask extends Mapper<LongWritable,Text,Text,OrderJoinBean> {@Overrideprotected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {// 通过文件片的方式获取文件的名称FileSplit fileSplit = (FileSplit) context.getInputSplit();String fileName = fileSplit.getPath().getName();//1. 获取每一行的数据String line = value.toString();//2. 切割处理String[] split = line.split(",");OrderJoinBean orderJoinBean = new OrderJoinBean();if(fileName.equals("orders.txt")){// 订单的数据orderJoinBean.setId(split[0]);orderJoinBean.setDate(split[1]);orderJoinBean.setPid(split[2]);orderJoinBean.setAmount(split[3]);}else{// 商品的数据orderJoinBean.setPid(split[0]);orderJoinBean.setName(split[1]);orderJoinBean.setCategoryId(split[2]);orderJoinBean.setPrice(split[3]);}//3. 发送给reduceTaskcontext.write(new Text(orderJoinBean.getPid()),orderJoinBean);}
}
3.定义 Reducer
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;import java.io.IOException;public class ReducerJoinTask extends Reducer<Text,OrderJoinBean,Text,OrderJoinBean> {@Overrideprotected void reduce(Text key, Iterable<OrderJoinBean> values, Context context) throws IOException, InterruptedException {//1. 遍历 : 相同的key会发给同一个reduce, 相同key的value的值形成一个集合OrderJoinBean orderJoinBean = new OrderJoinBean();for (OrderJoinBean value : values) {String id = value.getId();if(id.equals("")){// 商品的数据orderJoinBean.setPid(value.getPid());orderJoinBean.setName(value.getName());orderJoinBean.setCategoryId(value.getCategoryId());orderJoinBean.setPrice(value.getPrice());}else {// 订单数据orderJoinBean.setId(value.getId());orderJoinBean.setDate(value.getDate());orderJoinBean.setPid(value.getPid());orderJoinBean.setAmount(value.getAmount());}}//2. 输出即可context.write(key,orderJoinBean);}
}
4.定义主类
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;public class JobReduceJoinMain extends Configured implements Tool {@Overridepublic int run(String[] args) throws Exception {//1. 获取job对象Job job = Job.getInstance(super.getConf(), "jobReduceJoinMain");//2. 拼装八大步骤job.setInputFormatClass(TextInputFormat.class);TextInputFormat.addInputPath(job,new Path("file:///D:\\reduce端join\\input"));job.setMapperClass(MapperJoinTask.class);job.setMapOutputKeyClass(Text.class);job.setMapOutputValueClass(OrderJoinBean.class);job.setReducerClass(ReducerJoinTask.class);job.setOutputKeyClass(Text.class);job.setOutputValueClass(OrderJoinBean.class);job.setOutputFormatClass(TextOutputFormat.class);TextOutputFormat.setOutputPath(job,new Path("D:\\reduce端join\\out_put"));boolean b = job.waitForCompletion(true);return b?0:1;}public static void main(String[] args) throws Exception {Configuration conf = new Configuration();JobReduceJoinMain jobReduceJoinMain = new JobReduceJoinMain();int i = ToolRunner.run(conf, jobReduceJoinMain, args);System.exit(i);}
}
缺点:这种方式中,join的操作是在reduce阶段完成,reduce端的处理压力太大,map节点的运算负载则很低,资源利用率不高,且在reduce阶段极易产生数据倾斜
8. Map端实现 JOIN
8.1 概述
适用于关联表中有小表的情形.
使用分布式缓存,可以将小表分发到所有的map节点,这样,map节点就可以在本地对自己所读到的大表数据进行join并输出最终结果,可以大大提高join操作的并发度,加快处理速度
8.2 实现步骤
先在mapper类中预先定义好小表,进行join
引入实际场景中的解决方案:一次加载数据库
1.定义Mapper
import org.apache.hadoop.filecache.DistributedCache;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;public class MapperTask extends Mapper<LongWritable, Text, Text, Text> {private Map<String,String> map = new HashMap<>();// 初始化的方法, 只会被初始化一次@Overrideprotected void setup(Context context) throws IOException, InterruptedException {URI[] cacheFiles = DistributedCache.getCacheFiles(context.getConfiguration());URI fileURI = cacheFiles[0];FileSystem fs = FileSystem.get(fileURI, context.getConfiguration());FSDataInputStream inputStream = fs.open(new Path(fileURI));BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));String readLine ="";while ((readLine = bufferedReader.readLine() ) != null ) {// readlLine: product一行数据String[] split = readLine.split(",");String pid = split[0];map.put(pid,split[1]+"\t"+split[2]+"\t"+split[3]);}}@Overrideprotected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {//1. 读取一行数据: orders数据String line = value.toString();//2. 切割String[] split = line.split(",");String pid = split[2];//3. 到map中获取商品信息:String product = map.get(pid);//4. 发送给reduce: 输出context.write(new Text(pid),new Text(split[0]+"\t"+split[1]+"\t"+product +"\t"+split[3]));}
}
2.定义主类
import com.itheima.join.reduce.JobReduceJoinMain;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.filecache.DistributedCache;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;import java.net.URI;public class JobMapperJoinMain extends Configured implements Tool{@Overridepublic int run(String[] args) throws Exception {//设置缓存的位置, 必须在run的方法的最前, 如果放置在job任务创建后, 将无效// 缓存文件的路径, 必须存储在hdfs上, 否则也是无效的DistributedCache.addCacheFile(new URI("hdfs://node01:8020/cache/pdts.txt"),super.getConf());//1. 获取job 任务Job job = Job.getInstance(super.getConf(), "jobMapperJoinMain");job.setInputFormatClass(TextInputFormat.class);TextInputFormat.addInputPath(job,new Path("E:\\传智工作\\上课\\北京大数据30期\\大数据第六天\\资料\\map端join\\map_join_iput"));job.setMapperClass(MapperTask.class);job.setMapOutputKeyClass(Text.class);job.setMapOutputValueClass(Text.class);job.setOutputFormatClass(TextOutputFormat.class);TextOutputFormat.setOutputPath(job,new Path("E:\\传智工作\\上课\\北京大数据30期\\大数据第六天\\资料\\map端join\\out_put_map"));boolean b = job.waitForCompletion(true);return b?0:1;}public static void main(String[] args) throws Exception {Configuration conf = new Configuration();JobMapperJoinMain jobMapperJoinMain = new JobMapperJoinMain();int i = ToolRunner.run(conf, jobMapperJoinMain, args);System.exit(i);}
}
9. 社交粉丝数据分析
9.1 需求分析
以下是qq的好友列表数据,冒号前是一个用户,冒号后是该用户的所有好友(数据中的好友关系是单向的)
A:B,C,D,F,E,O
B:A,C,E,K
C:A,B,D,E,I
D:A,E,F,L
E:B,C,D,M,L
F:A,B,C,D,E,O,M
G:A,C,D,E,F
H:A,C,D,E,O
I:A,O
J:B,O
K:A,C,D
L:D,E,F
M:E,F,G
O:A,H,I,J
求出哪些人两两之间有共同好友,及他俩的共同好友都有谁?
【解题思路】
第一步
map
读一行 A:B,C,D,F,E,O
输出 <B,A><C,A><D,A><F,A><E,A><O,A>
在读一行 B:A,C,E,K
输出 <A,B><C,B><E,B><K,B>
REDUCE
拿到的数据比如<C,A><C,B><C,E><C,F><C,G>......
输出:
<A-B,C>
<A-E,C>
<A-F,C>
<A-G,C>
<B-E,C>
<B-F,C>.....
第二步
map
读入一行<A-B,C>
直接输出<A-B,C>
reduce
读入数据 <A-B,C><A-B,F><A-B,G>.......
输出: A-B C,F,G,.....
9.2 实现步骤
第一个MapReduce代码实现
【Mapper类】
public class Step1Mapper extends Mapper<LongWritable,Text,Text,Text> {@Overrideprotected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {//1:以冒号拆分行文本数据: 冒号左边就是V2String[] split = value.toString().split(":");String userStr = split[0];//2:将冒号右边的字符串以逗号拆分,每个成员就是K2String[] split1 = split[1].split(",");for (String s : split1) {//3:将K2和v2写入上下文中context.write(new Text(s), new Text(userStr));}}
}
【Reducer类】
public class Step1Reducer extends Reducer<Text,Text,Text,Text> {@Overrideprotected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {//1:遍历集合,并将每一个元素拼接,得到K3StringBuffer buffer = new StringBuffer();for (Text value : values) {buffer.append(value.toString()).append("-");}//2:K2就是V3//3:将K3和V3写入上下文中context.write(new Text(buffer.toString()), key);}
}
JobMain:
public class JobMain extends Configured implements Tool {@Overridepublic int run(String[] args) throws Exception {//1:获取Job对象Job job = Job.getInstance(super.getConf(), "common_friends_step1_job");//2:设置job任务//第一步:设置输入类和输入路径job.setInputFormatClass(TextInputFormat.class);TextInputFormat.addInputPath(job, new Path("file:///D:\\input\\common_friends_step1_input"));//第二步:设置Mapper类和数据类型job.setMapperClass(Step1Mapper.class);job.setMapOutputKeyClass(Text.class);job.setMapOutputValueClass(Text.class);//第三,四,五,六//第七步:设置Reducer类和数据类型job.setReducerClass(Step1Reducer.class);job.setOutputKeyClass(Text.class);job.setOutputValueClass(Text.class);//第八步:设置输出类和输出的路径job.setOutputFormatClass(TextOutputFormat.class);TextOutputFormat.setOutputPath(job, new Path("file:///D:\\out\\common_friends_step1_out"));//3:等待job任务结束boolean bl = job.waitForCompletion(true);return bl ? 0: 1;}public static void main(String[] args) throws Exception {Configuration configuration = new Configuration();//启动job任务int run = ToolRunner.run(configuration, new JobMain(), args);System.exit(run);}
}
第二个MapReduce代码实现
【Mapper类】
public class Step2Mapper extends Mapper<LongWritable,Text,Text,Text> {/*K1 V10 A-F-C-J-E- B----------------------------------K2 V2A-C BA-E BA-F BC-E B*/@Overrideprotected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {//1:拆分行文本数据,结果的第二部分可以得到V2String[] split = value.toString().split("\t");String friendStr =split[1];//2:继续以'-'为分隔符拆分行文本数据第一部分,得到数组String[] userArray = split[0].split("-");//3:对数组做一个排序Arrays.sort(userArray);//4:对数组中的元素进行两两组合,得到K2/*A-E-C -----> A C EA C EA C E*/for (int i = 0; i <userArray.length -1 ; i++) {for (int j = i+1; j < userArray.length ; j++) {//5:将K2和V2写入上下文中context.write(new Text(userArray[i] +"-"+userArray[j]), new Text(friendStr));}}}
}
【Reducer类】
public class Step2Reducer extends Reducer<Text,Text,Text,Text> {@Overrideprotected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {//1:原来的K2就是K3//2:将集合进行遍历,将集合中的元素拼接,得到V3StringBuffer buffer = new StringBuffer();for (Text value : values) {buffer.append(value.toString()).append("-");}//3:将K3和V3写入上下文中context.write(key, new Text(buffer.toString()));}
}
【JobMain】
public class JobMain extends Configured implements Tool {@Overridepublic int run(String[] args) throws Exception {//1:获取Job对象Job job = Job.getInstance(super.getConf(), "common_friends_step2_job");//2:设置job任务//第一步:设置输入类和输入路径job.setInputFormatClass(TextInputFormat.class);TextInputFormat.addInputPath(job, new Path("file:///D:\\out\\common_friends_step1_out"));//第二步:设置Mapper类和数据类型job.setMapperClass(Step2Mapper.class);job.setMapOutputKeyClass(Text.class);job.setMapOutputValueClass(Text.class);//第三,四,五,六//第七步:设置Reducer类和数据类型job.setReducerClass(Step2Reducer.class);job.setOutputKeyClass(Text.class);job.setOutputValueClass(Text.class);//第八步:设置输出类和输出的路径job.setOutputFormatClass(TextOutputFormat.class);TextOutputFormat.setOutputPath(job, new Path("file:///D:\\out\\common_friends_step2_out"));//3:等待job任务结束boolean bl = job.waitForCompletion(true);return bl ? 0: 1;}public static void main(String[] args) throws Exception {Configuration configuration = new Configuration();//启动job任务int run = ToolRunner.run(configuration, new JobMain(), args);System.exit(run);}
}
10. 倒排索引建立
10.1 需求分析
需求:有大量的文本(文档、网页),需要建立搜索索引
思路分析:
首选将文档的内容全部读取出来,加上文档的名字作为key,文档的value为1,组织成这样的一种形式的数据
map端数据输出:
hello-a.txt 1hello-a.txt 1hello-a.txt 1
reduce端数据输出:
hello-a.txt 3
10.2 代码实现
public class IndexCreate extends Configured implements Tool {public static void main(String[] args) throws Exception {ToolRunner.run(new Configuration(),new IndexCreate(),args);}@Overridepublic int run(String[] args) throws Exception {Job job = Job.getInstance(super.getConf(), IndexCreate.class.getSimpleName());job.setInputFormatClass(TextInputFormat.class);TextInputFormat.addInputPath(job,new Path("file:///D:\\倒排索引\\input"));job.setMapperClass(IndexCreateMapper.class);job.setMapOutputKeyClass(Text.class);job.setMapOutputValueClass(IntWritable.class);job.setReducerClass(IndexCreateReducer.class);job.setOutputKeyClass(Text.class);job.setOutputValueClass(IntWritable.class);job.setOutputFormatClass(TextOutputFormat.class);TextOutputFormat.setOutputPath(job,new Path("file:///D:\\倒排索引\\outindex"));boolean bool = job.waitForCompletion(true);return bool?0:1;}public static class IndexCreateMapper extends Mapper<LongWritable,Text,Text,IntWritable>{Text text = new Text();IntWritable v = new IntWritable(1);@Overrideprotected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {//获取文件切片FileSplit fileSplit = (FileSplit) context.getInputSplit();//通过文件切片获取文件名String name = fileSplit.getPath().getName();String line = value.toString();String[] split = line.split(" ");//输出 单词--文件名作为key value是1for (String word : split) {text.set(word+"--"+name);context.write(text,v);}}}public static class IndexCreateReducer extends Reducer<Text,IntWritable,Text,IntWritable>{IntWritable value = new IntWritable();@Overrideprotected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {int count = 0;for (IntWritable value : values) {count += value.get();}value.set(count);context.write(key,value);}}
}
相关文章:

2 MapReduce
2 MapReduce 1. MapReduce 介绍1.1 MapReduce 设计构思 2. MapReduce 编程规范3. Mapper以及Reducer抽象类介绍1.Mapper抽象类的基本介绍2.Reducer抽象类基本介绍 4. WordCount示例编写5. MapReduce程序运行模式6. MapReduce的运行机制详解6.1 MapTask 工作机制6.2 ReduceTask …...

如何用函数去计算x年x月x日是(C#)
如何用函数去计算x年x月x日是? 由于现在人工智能的普及,我们往往会用计算机去算,或者去记录事情 1.计算某一年某一个月有多少天 2.计算某年某月某日是周几 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threadin…...

开发过程中如何减少属性注释?
一、注释冗余 举个例子,我们在开发项目中肯定会有状态字段,现在有个工单状态枚举 StatusEnum.java package cn.zxj.note;/*** author: Administrator* since: 2025/1/30 14:40* description:*/ public enum StatusEnum {TO_BE_SUBMITTED(1,"待提交…...

NX/UG二次开发—CAM—快速查找程序参数名称
使用UF_PARAM_XXX读取或设置参数时,会发现程序中有一个INT类型参数param_index,这个就是对应程序中的参数,比如读取程序余量,则param_index = UF_PARAM_STOCK_PART,读取程序的加工坐标系则param_index = UF_PARAM_MCS等等。 你需要读取什么参数,只要只能在uf_param_indic…...

socket实现HTTP请求,参考HttpURLConnection源码解析
背景 有台服务器,网卡绑定有2个ip地址,分别为: A:192.168.111.201 B:192.168.111.202 在这台服务器请求目标地址 C:192.168.111.203 时必须使用B作为源地址才能访问目标地址C,在这台服务器默认…...

访问CMOS RAM
实验内容、程序清单及运行结果 访问CMOS RAM(课本实验14) 代码如下: assume cs:code data segment time db yy/mm/dd hh:mm:ss$ ;int 21h 显示字符串,要求以$结尾 table db 9,8,7,4,2,0 ;各时间量的存放单元 data ends cod…...

解决AnyConnect开机自启动问题
文章目录 一、问题描述二、解决方案 (Windows)1.开启-设置2.点击“应用”3.点击“启动”,选择“关” 三、参考文章 一、问题描述 学校指定的VPN总是开机自启动,然而 设置-Preferences 中却没有取消开机自启的选项。 似乎开机自启是必然的,我…...

芯片AI深度实战:进阶篇之vim内verilog实时自定义检视
【痛点】 传统Verilog开发中,工程师不断"编码→仿真→查错"的循环。本文整合AST解析与Vim编辑器,在编码阶段即实现: ✔️ 自动标记逻辑问题 ✔️ AI+ 发现涉及多模块逻辑错误 ✔️ 强制代码风格 【解决方案】 1️⃣ 基于AST的精准模式匹配 - 深度集成…...

数据结构实战之线性表(一)
一.线性表的定义和特点 线性表的定义 线性表是一种数据结构,它包含了一系列具有相同特性的数据元素,数据元素之间存在着顺序关系。例如,26个英文字母的字符表 ( (A, B, C, ....., Z) ) 就是一个线性表,其中每个字母就是一个数据…...

jdk8项目升级到jdk17——岁月云实战
由于很早之前就升级springboot版本到2.7.9,以前做好了铺垫,相对升级要容易一些。 1 项目打包成exe 1.1 jpackage打包jar C:\Users\39305\Desktop\数量核对>jpackage ^ More? --type exe ^ More? --name zp-server ^ More? --input C:\Use…...

商品列表及商品详情展示
前言 本文将展示一段结合 HTML、CSS 和 JavaScript 的代码,实现了一个简单的商品展示页面及商品详情,涵盖数据获取、渲染、搜索及排序等功能。 效果展示 点击不同的商品会展示对应的商品详情。 代码部分 代码总体实现 <!DOCTYPE html> <htm…...

使用where子句筛选记录
默认情况下,SearchCursor将返回一个表或要素类的所有行.然而在很多情况下,常常需要某些条件来限制返回行数. 操作方法: 1.打开IDLE,加载先前编写的SearchCursor.py脚本 2.添加where子句,更新SearchCursor()函数,查找记录中有<>文本的<>字段 with arcpy.da.Searc…...

SQL Server查询计划操作符(7.3)——查询计划相关操作符(5)
7.3. 查询计划相关操作符 38)Flow Distinct:该操作符扫描其输入并对其去重。该操作符从其输入得到每行数据时即将其返回(除非其为重复数据行,此时,该数据行会被抛弃),而Distinct操作符在产生任何输出前将消费所有输入。该操作符为逻辑操作符。该操作符具体如图7.2-38中…...

C++中常用的十大排序方法之4——希尔排序
成长路上不孤单😊😊😊😊😊😊 【😊///计算机爱好者😊///持续分享所学😊///如有需要欢迎收藏转发///😊】 今日分享关于C中常用的排序方法之4——希尔排序的相…...

扶摇计划--从失业的寒冬,慢慢的走出来
作为资深 Java 开发工程师,你有丰富的技术经验和解决问题的能力,即使暂时失业,也可以通过多种方式赚取收入。以下是结合你的技能和市场需求的具体建议,分阶段规划实现: 第一阶段:快速变现(短期,1-3个月) 自由职业与远程工作 平台接单:在 Upwork、Freelancer 或国内平…...

unity学习24:场景scene相关生成,加载,卸载,加载进度,异步加载场景等
目录 1 场景数量 SceneManager.sceneCount 2 直接代码生成新场景 SceneManager.CreateScene 3 场景的加载 3.1 用代码加载场景,仍然build setting里先加入配置 3.2 卸载场景 SceneManager.UnloadSceneAsync(); 3.3 同步加载场景 SceneManager.LoadScene 3.3.…...

[cg] 使用snapgragon 对UE5.3抓帧
最近想要抓opengl 的api,renderdoc在起应用时会闪退(具体原因还不知道),试了下snapgraon, 还是可以的 官网需要注册登录后下载,官网路径:Developer | Qualcomm 为了方便贴上已经下载好的exe安装包&#x…...

一元函数微积分的几何应用:二维平面光滑曲线的曲率公式
文章目录 前言曲率和曲率半径的定义曲率计算公式参数方程形式直角坐标显式方程形式极坐标形式向量形式 前言 本文将介绍二维平面光滑曲线的曲率定义以及不同形式的曲率及曲率半径公式的推导。 曲率和曲率半径的定义 (关于二维平面光滑曲线的定义以及弧长公式请参…...

ISBN 号码——蓝桥杯
1.题目描述 每一本正式出版的图书都有一个 ISBN 号码与之对应,ISBN 码包括 9 位数字、1 位识别码和 3 位分隔符,其规定格式如 “x-xxx-xxxxx-x”,其中符号“-”是分隔符(键盘上的减号),最后一位是识别码&a…...

Spring Boot - 数据库集成06 - 集成ElasticSearch
Spring boot 集成 ElasticSearch 文章目录 Spring boot 集成 ElasticSearch一:前置工作1:项目搭建和依赖导入2:客户端连接相关构建3:实体类相关注解配置说明 二:客户端client相关操作说明1:检索流程1.1&…...

51单片机CLD1602显示万年历+闹钟+农历+整点报时
1. 硬件设计 硬件是我自己设计的一个通用的51单片机开发平台,可以根据需要自行焊接模块,这是用立创EDA画的一个双层PCB板,所以模块都是插针式,不是表贴的。电路原理图在文末的链接里,PCB图暂时不选择开源。 B站上传的…...

C++ 中的类(class)和对象(object)
在 C 中,类(class)和对象(object)是面向对象编程(OOP)的核心概念。类是一种用户自定义的数据类型,它将数据(成员变量)和操作这些数据的函数(成员函…...

安卓通过网络获取位置的方法
一 方法介绍 1. 基本权限设置 首先需要在 AndroidManifest.xml 中添加必要权限: xml <uses-permission android:name"android.permission.INTERNET" /> <uses-permission android:name"android.permission.ACCESS_NETWORK_STATE" /&g…...

2025 年,链上固定收益领域迈向新时代
“基于期限的债券市场崛起与 Secured Finance 的坚定承诺” 2025年,传统资产——尤其是股票和债券——大规模涌入区块链的浪潮将创造历史。BlackRock 首席执行官 Larry Fink 近期在彭博直播中表示,代币化股票和债券将逐步融入链上生态,将进一…...

npm启动前端项目时报错(vue) error:0308010C:digital envelope routines::unsupported
vue 启动项目时,npm run serve 报下面的错: error:0308010C:digital envelope routines::unsupported at new Hash (node:internal/crypto/hash:67:19) at Object.createHash (node:crypto:133:10) at FSReqCallback.readFileAfterClose [as on…...

11.QT控件:输入类控件
1. Line Edit(单行输入框) QLineEdit表示单行输入框,用来输入一段文本,但是不能换行。 核心属性: 核心信号: 2. Text Edit(多行输入框) QTextEdit表示多行输入框,也是一个富文本 & markdown编辑器。并且能在内容超…...

deepseek核心技术:MLA架构-多头潜在注意力
deepseek核心技术:MLA架构-多头潜在注意力 MLA架构即Multi-Head Latent Attention(多头潜在注意力)架构,是一种优化后的注意力机制。以下是对其及相关示例的具体介绍: 工作原理 输入嵌入:将输入序列中的每个元素转换为向量表示,即嵌入向量。例如在处理文本时,将文本中…...

讯飞星火大模型API使用Python调用
本文仅仅为简单API调用,更多复杂使用方法请参见接口文档 先在科大讯飞开放平台注册账号,点击控制台,在我的应用中创建新应用,新应用的名称可以自定义,这里我写的是ai对话: 在这里我们使用的模型为Speak Ul…...

C#面试常考随笔7:什么是匿名⽅法?还有Lambda表达式?
匿名方法本质上是一种没有显式名称的方法,它可以作为参数传递给需要委托类型的方法,常用于事件处理、回调函数等场景,能够让代码更加简洁和紧凑。 使用场景 事件处理:在处理事件时,不需要为每个事件处理程序单独定义…...

Elasticsearch:如何搜索含有复合词的语言
作者:来自 Elastic Peter Straer 复合词在文本分析和标记过程中给搜索引擎带来挑战,因为它们会掩盖词语成分之间的有意义的联系。连字分解器标记过滤器等工具可以通过解构复合词来帮助解决这些问题。 德语以其长复合词而闻名:Rindfleischetik…...