Hadoop之MapReduce 详细教程

MapReduce仅作了解,生产上很少使用该计算程序

1、MapReduce介绍

MapReduce 思想在生活中处处可见。或多或少都曾接触过这种思想。MapReduce的思想核心是“分而治之”,适用于大量复杂的任务处理场景(大规模数据处理场景)。即使是发布过论文实现分布式计算的谷歌也只是实现了这种思想,而不是自己原创。

1、Map 负责“分”,即把复杂的任务分解为若干个“简单的任务”来并行处理。可以进行拆分的前提是这些小任务可以并行计算,彼此间几乎没有依赖关系。

2、Reduce 负责“合”,即对 map 阶段的结果进行全局汇总。

3、MapReduce 运行在 yarn 集群

  • ResourceManager
  • NodeManager

这两个阶段合起来正是 MapReduce 思想的体现。
在这里插入图片描述
还有一个比较形象的语言解释 MapReduce:
我们要数图书馆中的所有书。你数 1 号书架,我数 2 号书架。这就是“Map”。我们人越多,数书就更快。现在我们到一起,把所有人的统计数加在一起。这就是“Reduce”。

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类

Mapper 抽象类的基本介绍
在 hadoop2.x 当中 Mapper 类是一个抽象类,我们只需要覆写一个 java 类,继承自 Mapper 类即可,然后重写里面的一些方法,就可以实现我们特定的功能,接下来我们来介绍一下 Mapper 类当中比较重要的四个方法

  1. setup 方法: 我们 Mapper 类当中的初始化方法,我们一些对象的初始化工作都可以放到这个方法里面来实现
  2. map 方法: 读取的每一行数据,都会来调用一次 map 方法,这个方法也是我们最重要的方法,可以通过这个方法来实现我们每一条数据的处理
  3. cleanup 方法: 在我们整个 maptask 执行完成之后,会马上调用 cleanup方法,这个方法主要是用于做我们的一些清理工作,例如连接的断开,资源的关闭等等
  4. run 方法: 如果我们需要更精细的控制我们的整个 MapTask 的执行,那么我们可以覆写这个方法,实现对我们所有的 MapTask 更精确的操作控制

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/

1、定义一个 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 的类型都进行包装, 以提高传输的效率 writable
keyin : k1 Long ---- LongWritable
valin : v1 String ------ Text
keyout : k2 String ------- Text
valout : 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);}}
}

2、定义一个 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));}
}

3、定义一个主类,用来描述 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 的运行机制详解

1、MapTask工作机制

在这里插入图片描述整个 Map 阶段流程大体如上图所示。

简单概述:
inputFile 通过 split 被逻辑切分为多个 split 文件,通过 Record按行读取内容给 map(用户自己实现的)进行处理,数据被 map 处理结束之后交给 OutputCollector 收集器,对其结果 key 进行分区(默认使用 hash 分区),然后写入 buffer,每个 map task 都有一个内存缓冲区(环形缓冲区),存储着 map 的输出结果,当缓冲区快满的时候需要将缓冲区的数据以一个临时文件的方式存放到磁盘,当整个 map task 结束后再对磁盘中这个 map task 产生的所有临时文件做合并,生成最终的正式输出文件,然后等待 reduce task 来拉数据

总结:加载文件,文件切割,按行读取,map拆分转换,map结果输出mapTask的环形缓冲区(100m)(临时存放),80m触发线程溢写到磁盘,在此期间会进行 排序,预combiner(可选)(结果预合并,减少写磁盘数据量),溢写文件聚合

详细步骤

  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 中, 会先对其进行分区处理
    默认使用=HashPartitionerMapReduce 提供 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 合并在起来, 减少溢写到磁盘的数据量.

  7. Combiner会优化 MapReduce 的中间结果, 所以它在整个模型中会多次使用 \ 那哪些场景才能使用 Combiner 呢?从这里分析, Combiner 的输出是 Reducer 的输入,Combiner 绝不能改变最终的计算结果. Combiner 只应该用于那种 Reduce 的输入Key/Value 与输出 Key/Value 类型完全一致, 且不影响最终结果的场景. 比如累加, 最大值等. Combiner 的使用一定得慎重, 如果用好, 它对 Job 执行效率有帮助, 反之会影响 Reducer 的最终结果

  8. 合并溢写文件, 每次溢写会在磁盘上生成一个临时文件 (写之前判断是否有Combiner), 如果 Mapper 的输出结果真的很大, 有多次这样的溢写发生, 磁盘上相应的就会有多个临时文件存在. 当整个数据处理结束之后开始对磁盘中的临时文件进行 Merge 合并, 因为最终的文件只有一个, 写入磁盘, 并且为这个文件提供了一个索引文件, 以记录每个 reduce 对应数据的偏移量

mapTask 的一些基础设置配置:

在这里插入图片描述
在这里插入图片描述

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 文件中。

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 的数据的最终整体有效性即可。

  7. Shuffle 中的缓冲区大小会影响到 mapreduce 程序的执行效率,原则上说,缓
    冲区越大,磁盘 io 的次数越少,执行速度就越快
    缓冲区的大小可以通过参数调整, 参数:mapreduce.task.io.sort.mb 默认 100M

转载自:五分钟学大数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/595479.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Centos 磁盘挂载和磁盘扩容(新加硬盘方式)

步骤总结如下 一、对磁盘进行分区 二、对磁盘进行格式化 三、将磁盘挂载到对应目录 四、做开机自动挂载磁盘 磁盘分区 1.使用命令&#xff1a;fdisk -l 查看磁盘&#xff08;注&#xff1a;正常在Centos7中第一块数据盘标识一般是/dev/sda,第二块数据盘标识一般是/dev/sdb&…

WebGIS开发的常见框架及优缺点

WebGIS开发引擎的发展历程&#xff1a; 内容来自公众号&#xff1a;Spatial Data 地图API分类 WebGIS系统通常都围绕地图进行内容表达&#xff0c;但并不是有地图就一定是WebGIS&#xff0c;所以下面要讨论下基于Web的地图API分类及应用场景。Web上的Map API主要分类&#xff…

基于Java+SpringBoot+vue+elementUI私人健身教练预约管理系统设计实现

基于JavaSpringBootvueelementUI私人健身教练预约管理系统设计实现 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目录 基于JavaSpringBootvueelementUI私人健身教练预约管理系统设计实现一、前言介绍&#xff1a;二、系统设计&#xff1a;2.1 性能需求分析2.2 B/S架构&…

typora的笔记丢失了怎么办?

突然打开电脑发现原本保存的typora的笔记全部不见了&#xff0c;头痛…… 解决方法&#xff1a;点击typora中的文件&#xff0c;选择偏好设置&#xff0c;点击[恢复未保存的草稿]&#xff0c;然后可以在文件夹中找到最后一次保存该文件的版本。 自己又将自动保存勾选上了&…

SQL中 Group by Grouping Sets 分组的用法

文章目录 1. 用法2. 语法3. 实际应用3.1 求总和与小计3.2 按多个维度分组3.3 标记小计和总计 1. 用法 将Grouping Sets 运算符添加到Group by 子句中&#xff0c;使用Grouping Set 可以在一个查询中指定数据的多个分组&#xff0c;其结果与针对指定的组执行union all 运算等效…

基于LLM+RAG的问答

每日推荐一篇专注于解决实际问题的外文&#xff0c;精准翻译并深入解读其要点&#xff0c;助力读者培养实际问题解决和代码动手的能力。 欢迎关注公众号 原文标题&#xff1a;LLMRAG based Question Answering 原文地址&#xff1a;https://teemukanstren.com/2023/12/25/llm…

14.7-时序反馈移位寄存器建模

时序反馈移位寄存器建模 1&#xff0c;阻塞赋值实现的LFSR&#xff0c;实际上并不具有LFSR功能1.1.1&#xff0c;RTL设计&#xff0c;阻塞赋值1.1.2&#xff0c;tb测试代码1.1.3&#xff0c;波形仿真输出&#xff0c;SIM输出&#xff0c;没实现LFSR1.2.1&#xff0c;RTL设计&am…

imgaug库指南(二):从入门到精通的【图像增强】之旅

文章目录 引言前期回顾代码示例小结结尾 引言 在深度学习和计算机视觉的世界里&#xff0c;数据是模型训练的基石&#xff0c;其质量与数量直接影响着模型的性能。然而&#xff0c;获取大量高质量的标注数据往往需要耗费大量的时间和资源。正因如此&#xff0c;数据增强技术应…

【零基础入门TypeScript】TypeScript - 概述

目录 什么是 TypeScript&#xff1f; TypeScript 的特点 TypeScript 和 ECMAScript 为什么使用 TypeScript&#xff1f; TypeScript 的组件 声明文件 JavaScript 是作为客户端语言引入的。Node.js 的发展也标志着 JavaScript 成为一种新兴的服务器端技术。然而&#xff0…

婴儿专用洗衣机哪个牌子比较好?高性价比婴儿洗衣机推荐

随着家中新生命的降临&#xff0c;一大批婴儿专用的物品也陆续进入到家中&#xff0c;虽然有很多东西确实能在育儿之路上提供很多帮助&#xff0c;不过也有一些东西“很坑”。婴儿洗衣机这一产品是专为有婴幼儿的家庭或者有特殊需求的人士所研发的&#xff0c;它的容量以及体积…

【教学类-43-15】 20240103 (5宫格数独:内存数据不够计算) 不重复的基础模板数量:未知

背景需求&#xff1a; 测试5宫格有多少种不重复的基础模板&#xff08;只测试所有的25数字一组有多少个&#xff09; # 测试11*11格&#xff0c;2*2一共4套3*3 宫格目的&#xff1a;数独14 5宫格有不同的基础模板 作者&#xff1a;阿夏 时间&#xff1a;2024年01月04日 13:…

unity学习笔记----游戏练习04

一、开发阳光生产功能 向日葵的生产过程需要动画和时间 1.生产动画 选中Sunflower&#xff0c;然后选中窗口再选中 创建新的剪辑开始制作动画&#xff0c;向日葵生产动画的过程是一个从暗到亮然后持续一段时间再到暗的过程。因此只需要在对应的时间改变颜色即可。 为了保证是…

使用IDEA创建使用 JDK8 的 2.x.x 版本的 Spring Boot 项目以及 Spring Boot 项目如何修改JDK版本

目录 一、在阿里云上官网上创建项目 二、将 IDEA 中创建项目的源地址修改为阿里云官网 三、创建 3.x.x 的项目之后修改配置降低至 2.7.x 版本和使用 JDK8&#xff08;修改 Spring Boot 的 JDK 版本同理&#xff09; 从上面的 Spring Boot 官网的截图中可以发现&#xff0c;自…

爬取豆瓣电影top250的电影名称(完整代码与解释)

在爬取豆瓣电影top250的电影名称之前&#xff0c;需要在安装两个第三方库requests和bs4&#xff0c;方法是在终端输入&#xff1a; pip install requestspip install bs4 截几张关键性图片&#xff1a; 豆瓣top250电影网页 运行结果 测试html文件标签的各个方法的作用&#xf…

wy的leetcode刷题记录_Day71

wy的leetcode刷题记录_Day71 声明 本文章的所有题目信息都来源于leetcode 如有侵权请联系我删掉! 时间&#xff1a;2024-1-3&#xff08;补&#xff09; 前言 目录 wy的leetcode刷题记录_Day71声明前言2487. 从链表中移除节点题目介绍思路代码收获 509. 斐波那契数题目介绍思…

低压浪涌保护防雷器的应用方案

浪涌是指电力系统中出现的瞬时过电压或过电流&#xff0c;它们的持续时间很短&#xff0c;但幅值很高&#xff0c;可能达到几千伏或几千安。浪涌的主要来源有两种&#xff1a;一是雷电冲击&#xff0c;二是电力系统的切换操作。浪涌会对电气设备造成严重的损坏&#xff0c;甚至…

跨境电商源码技术大揭秘:9个最常见的问题及解决方案

跨境电商发展迅猛&#xff0c;源码技术成为关键。作为跨境电商源码领域的专家&#xff0c;我将向大家揭秘9个最常见的问题及解决方案&#xff0c;帮助您更好地应对挑战&#xff0c;实现技术优化。 问题一&#xff1a;跨境电商源码性能瓶颈如何解决&#xff1f; 跨境电商源码性…

【数据库原理】(7)关系数据库的完整性约束

关系模型的完整性规则是为了确保数据的唯一性和数据之间的关系的准确性。 有三类完整性约束:实体完整性、参照完整性和用户定义完整性。 其中实体完整性和参照完整性是必须满足的完整性约束条件,应该由关系系统自动支持。 实体完整性 实体完整性的核心概念 唯一性&#xf…

陪诊系统|北京陪诊小程序提升陪诊服务效果

随着科技的不断发展&#xff0c;人们对于医疗服务的需求也越来越高。在过去&#xff0c;陪诊师和陪诊公司通常需要通过电话或传真等传统方式与医院进行沟通和安排。然而&#xff0c;现在有了陪诊小程序&#xff0c;这些问题得到了解决。本文将介绍陪诊小程序的开发流程和功能&a…

【python高级用法】线程

前言 Python通过标准库的 threading 模块来管理线程。这个模块提供了很多不错的特性&#xff0c;让线程变得无比简单。实际上&#xff0c;线程模块提供了几种同时运行的机制&#xff0c;实现起来非常简单。 线程模块 线程对象Lock对象RLock对象信号对象条件对象事件对象 简单…