大数据实验
实验1
熟悉常用的Linux操作和Hadoop操作
1.实验目的
Hadoop运行在Linux系统上,因此,需要学习实践一些常用的Linux命令。本实验旨在熟悉常用的Linux操作和Hadoop操作,为顺利开展后续其他实验奠定基础。
2.实验平台
(1)操作系统:Linux(建议Ubuntu16.04或Ubuntu18.04);
(2)Hadoop版本:3.1.3。
3.实验步骤
1.熟悉常用的Linux操作
1)cd命令:切换目录
(1) 切换到目录“/usr/local”
(2) 切换到当前目录的上一级目录
(3) 切换到当前登录Linux系统的用户的自己的主文件夹
2)ls命令:查看文件与目录
查看目录“/usr”下的所有文件和目录
3)mkdir命令:新建目录
(1)进入“/tmp”目录,创建一个名为“a”的目录,并查看“/tmp”目录下已经存在哪些目录
(2)进入“/tmp”目录,创建目录“a1/a2/a3/a4”
4)rmdir命令:删除空的目录
(1)将上面创建的目录a(在“/tmp”目录下面)删除
(2)删除上面创建的目录“a1/a2/a3/a4” (在“/tmp”目录下面),然后查看“/tmp”目录下面存在哪些目录
5)cp命令:复制文件或目录
(1)将当前用户的主文件夹下的文件.bashrc复制到目录“/usr”下,并重命名为bashrc1
(2)在目录“/tmp”下新建目录test,再把这个目录复制到“/usr”目录下
6)mv命令:移动文件与目录,或更名
(1)将“/usr”目录下的文件bashrc1移动到“/usr/test”目录下
(2)将“/usr”目录下的test目录重命名为test2
7)rm命令:移除文件或目录
(1)将“/usr/test2”目录下的bashrc1文件删除
(2)将“/usr”目录下的test2目录删除
8)cat命令:查看文件内容
查看当前用户主文件夹下的.bashrc文件内容
9)tac命令:反向查看文件内容
反向查看当前用户主文件夹下的.bashrc文件的内容
10)more命令:一页一页翻动查看
翻页查看当前用户主文件夹下的.bashrc文件的内容
11)head命令:取出前面几行
(1)查看当前用户主文件夹下.bashrc文件内容前20行
(2)查看当前用户主文件夹下.bashrc文件内容,后面50行不显示,只显示前面几行
12)tail命令:取出后面几行
(1)查看当前用户主文件夹下.bashrc文件内容最后20行
(2)查看当前用户主文件夹下.bashrc文件内容,并且只列出50行以后的数据
13)touch命令:修改文件时间或创建新文件
(1)在“/tmp”目录下创建一个空文件hello,并查看文件时间
(2)修改hello文件,将文件时间整为5天前
14)chown命令:修改文件所有者权限
将hello文件所有者改为root帐号,并查看属性
15)find命令:文件查找
找出主文件夹下文件名为.bashrc的文件
16)tar命令:压缩命令
(1)在根目录“/”下新建文件夹test,然后在根目录“/”下打包成test.tar.gz
(2)把上面的test.tar.gz压缩包,解压缩到“/tmp”目录
17)grep命令:查找字符串
从“~/.bashrc”文件中查找字符串'examples'
18)配置环境变量
(1)请在“~/.bashrc”中设置,配置Java环境变量
(2)查看JAVA_HOME变量的值
2.熟悉常用的Hadoop操作
(1)使用hadoop用户登录Linux系统,启动Hadoop(Hadoop的安装目录为“/usr/local/hadoop”),为hadoop用户在HDFS中创建用户目录“/user/hadoop”
(2)接着在HDFS的目录“/user/hadoop”下,创建test文件夹,并查看文件列表
(3)将Linux系统本地的“~/.bashrc”文件上传到HDFS的test文件夹中,并查看test
(4)将HDFS文件夹test复制到Linux系统本地文件系统的“/usr/local/hadoop”目录下
4.实验报告
题目: 熟悉常用的Linux操作和Hadoop操作 姓名 闫浩 日期
2024/11/24
实验环境:
操作系统:Ubuntu 18.04
Hadoop版本:3.1.3
系统配置:4GB RAM
实验内容与完成情况:
1.熟悉常用的Linux操作
1)cd命令:切换目录
(4) 切换到目录“/usr/local”
(5) 切换到当前目录的上一级目录
(6) 切换到当前登录Linux系统的用户的自己的主文件夹
2)ls命令:查看文件与目录
查看目录“/usr”下的所有文件和目录
3)mkdir命令:新建目录
(1)进入“/tmp”目录,创建一个名为“a”的目录,并查看“/tmp”目录下已经存在哪些目录
(2)进入“/tmp”目录,创建目录“a1/a2/a3/a4”
4)rmdir命令:删除空的目录
(1)将上面创建的目录a(在“/tmp”目录下面)删除
(2)删除上面创建的目录“a1/a2/a3/a4” (在“/tmp”目录下面),然后查看“/tmp”目录下面存在哪些目录
5)cp命令:复制文件或目录
(1)将当前用户的主文件夹下的文件.bashrc复制到目录“/usr”下,并重命名为bashrc1
(2)在目录“/tmp”下新建目录test,再把这个目录复制到“/usr”目录下
6)mv命令:移动文件与目录,或更名
(1)将“/usr”目录下的文件bashrc1移动到“/usr/test”目录下
(2)将“/usr”目录下的test目录重命名为test2
7)rm命令:移除文件或目录
(1)将“/usr/test2”目录下的bashrc1文件删除
(2)将“/usr”目录下的test2目录删除
8)cat命令:查看文件内容
查看当前用户主文件夹下的.bashrc文件内容
9)tac命令:反向查看文件内容
反向查看当前用户主文件夹下的.bashrc文件的内容
10)more命令:一页一页翻动查看
翻页查看当前用户主文件夹下的.bashrc文件的内容
11)head命令:取出前面几行
(1)查看当前用户主文件夹下.bashrc文件内容前20行
(2)查看当前用户主文件夹下.bashrc文件内容,后面50行不显示,只显示前面几行
12)tail命令:取出后面几行
(1)查看当前用户主文件夹下.bashrc文件内容最后20行
(2)查看当前用户主文件夹下.bashrc文件内容,并且只列出50行以后的数据
13)touch命令:修改文件时间或创建新文件
(1)在“/tmp”目录下创建一个空文件hello,并查看文件时间
(2)修改hello文件,将文件时间整为5天前
14)chown命令:修改文件所有者权限
将hello文件所有者改为root帐号,并查看属性
找出主文件夹下文件名为.bashrc的文件
16)tar命令:压缩命令
(1)在根目录“/”下新建文件夹test,然后在根目录“/”下打包成test.tar.gz
(2)把上面的test.tar.gz压缩包,解压缩到“/tmp”目录
17)grep命令:查找字符串
从“~/.bashrc”文件中查找字符串'examples'
18)配置环境变量
(1)请在“~/.bashrc”中设置,配置Java环境变量
(2)查看JAVA_HOME变量的值
2.熟悉常用的Hadoop操作
(1)使用hadoop用户登录Linux系统,启动Hadoop(Hadoop的安装目录为“/usr/local/hadoop”),为hadoop用户在HDFS中创建用户目录“/user/hadoop”
(2)接着在HDFS的目录“/user/hadoop”下,创建test文件夹,并查看文件列表
(3)将Linux系统本地的“~/.bashrc”文件上传到HDFS的test文件夹中,并查看test
(4)将HDFS文件夹test复制到Linux系统本地文件系统的“/usr/local/hadoop”目录下
(1):
(2):
(3):
(4):
出现的问题:
没有配置好Hadoop环境,导致启动Hadoop时出现错误。
解决方案(列出遇到的问题和解决办法,列出没有解决的问题):
问题:没有配置好Hadoop环境,导致启动Hadoop时出现错误。
解决方案:确认 hadoop-env.sh 和其他配置文件中的路径设置正确。
问题:上传文件到HDFS时权限问题。
解决方案:确保当前用户有上传权限,或者切换到hadoop用户执行相关命令。
问题:在创建目录时,缺少相应权限。
解决方案:使用 sudo 权限执行 mkdir 命令。
实验2
熟悉常用的HDFS操作
1.实验目的
(1)理解HDFS在Hadoop体系结构中的角色;
(2)熟练使用HDFS操作常用的Shell命令;
(3)熟悉HDFS操作常用的Java API。
2. 实验平台
(1)操作系统:Linux(建议Ubuntu16.04或Ubuntu18.04);
(2)Hadoop版本:3.1.3;
(3)JDK版本:1.8;
(4)Java IDE:Eclipse。
3. 实验步骤
(一)编程实现以下功能,并利用Hadoop提供的Shell命令完成相同任务:
(1) 向HDFS中上传任意文本文件,如果指定的文件在HDFS中已经存在,则由用户来指定是追加到原有文件末尾还是覆盖原有的文件;
(2) 从HDFS中下载指定文件,如果本地文件与要下载的文件名称相同,则自动对下载的文件重命名;
(3) 将HDFS中指定文件的内容输出到终端中;
(4) 显示HDFS中指定的文件的读写权限、大小、创建时间、路径等信息;
(5) 给定HDFS中某一个目录,输出该目录下的所有文件的读写权限、大小、创建时间、路径等信息,如果该文件是目录,则递归输出该目录下所有文件相关信息;
(6) 提供一个HDFS内的文件的路径,对该文件进行创建和删除操作。如果文件所在目录不存在,则自动创建目录;
(7) 提供一个HDFS的目录的路径,对该目录进行创建和删除操作。创建目录时,如果目录文件所在目录不存在,则自动创建相应目录;删除目录时,由用户指定当该目录不为空时是否还删除该目录;
(8) 向HDFS中指定的文件追加内容,由用户指定内容追加到原有文件的开头或结尾;
(9) 删除HDFS中指定的文件;
(10) 在HDFS中,将文件从源路径移动到目的路径。
(二)编程实现一个类“MyFSDataInputStream”,该类继承“org.apache.hadoop.fs.FSDataInputStream”,要求如下:实现按行读取HDFS中指定文件的方法“readLine()”,如果读到文件末尾,则返回空,否则返回文件一行的文本。
(三)查看Java帮助手册或其它资料,用“java.net.URL”和“org.apache.hadoop.fs.FsURLStreamHandlerFactory”编程完成输出HDFS中指定文件的文本到终端中。
4.实验报告
题目: 熟悉常用的HDFS操作 姓名 闫浩 日期
2024/12/3
实验环境:
操作系统:Ubuntu 24.04
Hadoop版本:3.3.6
JDK版本:1.8
实验内容与完成情况:
(一)编程实现以下功能,并利用Hadoop提供的Shell命令完成相同任务:
(1) 向HDFS中上传任意文本文件,如果指定的文件在HDFS中已经存在,则由用户来指定是追加到原有文件末尾还是覆盖原有的文件;
(2) 从HDFS中下载指定文件,如果本地文件与要下载的文件名称相同,则自动对下载的文件重命名;
(3) 将HDFS中指定文件的内容输出到终端中;
(4) 显示HDFS中指定的文件的读写权限、大小、创建时间、路径等信息;
(5) 给定HDFS中某一个目录,输出该目录下的所有文件的读写权限、大小、创建时间、路径等信息,如果该文件是目录,则递归输出该目录下所有文件相关信息;
(6) 提供一个HDFS内的文件的路径,对该文件进行创建和删除操作。如果文件所在目录不存在,则自动创建目录;
(7) 提供一个HDFS的目录的路径,对该目录进行创建和删除操作。创建目录时,如果目录文件所在目录不存在,则自动创建相应目录;删除目录时,由用户指定当该目录不为空时是否还删除该目录;
(8) 向HDFS中指定的文件追加内容,由用户指定内容追加到原有文件的开头或结尾;
(9) 删除HDFS中指定的文件;
(10) 在HDFS中,将文件从源路径移动到目的路径。
(二)编程实现一个类“MyFSDataInputStream”,该类继承“org.apache.hadoop.fs.FSDataInputStream”,要求如下:实现按行读取HDFS中指定文件的方法“readLine()”,如果读到文件末尾,则返回空,否则返回文件一行的文本。
package test2;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import java.io.*;
public class MyFSDataInputStream extends FSDataInputStream {
public MyFSDataInputStream(InputStream in) {
super(in);
}
/**
*实现按行读取
每次读入一个字符,遇到"n"结束,返回一行内容
/
public static String readline(BufferedReader br) throws IOException {
char[] data = new char[1024];
int read = -1;
int off = 0; //循环执行时,br每次会从上- -次读取结束的位置继续读取,因此该函数里,off每次都从0开始
while ( (read = br.read(data, off, 1)) != -1 ) {
if (String.valueOf(data[off]).equals("\n") ) {
off += 1;
break;
}
off += 1;
}
if (off> 0) {
return String.valueOf( data);
} else {
return null;
}
}
/
读取文件内容
/
public static void cat(Configuration conf, String remoteFilePath) throws IOException {
FileSystem fs = FileSystem.get(conf);
Path remotePath = new Path(remoteFilePath);
FSDataInputStream in = fs.open(remotePath);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line = null;
while ( (line = MyFSDataInputStream.readline(br)) != null ) {
System.out.println(line);
}
br.close();
in.close();
fs.close();
}
/
*主函数
*/
public static void main(String[] args) {
Configuration conf = new Configuration();
conf.set(" fs.default.name", "hdfs://localhost:9000");
String remoteFilePath = "/user/hadoop/file/text.txt"; // HDFS路径
try {
MyFSDataInputStream.cat(conf,remoteFilePath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
(三)查看Java帮助手册或其它资料,用“java.net.URL”和“org.apache.hadoop.fs.FsURLStreamHandlerFactory”编程完成输出HDFS中指定文件的文本到终端中。
package test2;
import org.apache.hadoop.fs.;
import org.apache.hadoop.io.IOUtils;
import java.io.;
import java.net.URL;
public class HDFSApi {
static {
URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
}
/**
* 主函数
*/
public static void main(String[] args) throws Exception {
String remoteFilePath = "hdfs://localhost:9000/user/hadoop/file/text.txt"; // HDFS 文件
InputStream in = null;
try {
/* 通过 URL 对象打开数据流,从中读取数据 */
in = new URL(remoteFilePath).openStream();
IOUtils.copyBytes(in, System.out, 4096, false);
} finally {
IOUtils.closeStream(in);
}
}
}
出现的问题:
文件上传时的权限问题:在尝试上传文件到HDFS时,遇到权限不足的问题,导致文件无法成功上传。
解决方案(列出遇到的问题和解决办法,列出没有解决的问题):
文件上传时的权限问题:
解决办法:通过检查HDFS权限设置,确保上传文件的用户具有写入权限。使用hdfs dfs -chmod命令调整权限,并确保操作时使用的是具有权限的Hadoop用户。
下载文件时文件重命名失败:
解决办法:修改下载文件时的逻辑,检查本地是否存在同名文件,如果存在,则通过日期或数字后缀重命名文件,避免文件名冲突。
HDFS命令执行时出现路径错误:
解决办法:确保HDFS路径正确,使用绝对路径避免路径错误。通过hdfs dfs -ls命令检查路径是否存在,并确保HDFS文件系统正确配置。
自定义FSDataInputStream类时出现NullPointerException:
解决办法:检查代码中读取文件的逻辑,确保在读取每一行内容时,readline()方法返回的line对象不为null,并在读取文件内容时进行适当的空值检查
实验3
熟悉常用的HBase操作
1.实验目的
(1)理解HBase在Hadoop体系结构中的角色;
(2)熟练使用HBase操作常用的Shell命令;
(3)熟悉HBase操作常用的Java API。
2.实验平台
(1)操作系统:Ubuntu24.04;
(2)Hadoop版本:3.3.6;
(3)HBase版本:2.2.2;
(4)JDK版本:1.8;
(5)Java IDE:Eclipse。
3. 实验步骤
(一)编程实现以下指定功能,并用Hadoop提供的HBase Shell命令完成相同任务:
(1) 列出HBase所有的表的相关信息,例如表名;
(2) 在终端打印出指定的表的所有记录数据;
(3) 向已经创建好的表添加和删除指定的列族或列;
(4) 清空指定的表的所有记录数据;
(5) 统计表的行数。
(二)HBase数据库操作
- 现有以下关系型数据库中的表和数据(见表14-3到表14-5),要求将其转换为适合于HBase存储的表并插入数据:
表14-3 学生表(Student)
学号(S_No) 姓名(S_Name) 性别(S_Sex) 年龄(S_Age)
2015001 Zhangsan male 23
2015002 Mary female 22
2015003 Lisi male 24
表14-4 课程表(Course)
课程号(C_No) 课程名(C_Name) 学分(C_Credit)
123001 Math 2.0
123002 Computer Science 5.0
123003 English 3.0
表14-5 选课表(SC)
学号(SC_Sno) 课程号(SC_Cno) 成绩(SC_Score)
2015001 123001 86
2015001 123003 69
2015002 123002 77
2015002 123003 99
2015003 123001 98
2015003 123002 95
- 请编程实现以下功能:
(1)createTable(String tableName, String[] fields)
创建表,参数tableName为表的名称,字符串数组fields为存储记录各个字段名称的数组。要求当HBase已经存在名为tableName的表的时候,先删除原有的表,然后再创建新的表。
(2)addRecord(String tableName, String row, String[] fields, String[] values)
向表tableName、行row(用S_Name表示)和字符串数组fields指定的单元格中添加对应的数据values。其中,fields中每个元素如果对应的列族下还有相应的列限定符的话,用“columnFamily:column”表示。例如,同时向“Math”、“Computer Science”、“English”三列添加成绩时,字符串数组fields为{“Score:Math”, ”Score:Computer Science”, ”Score:English”},数组values存储这三门课的成绩。
(3)scanColumn(String tableName, String column)
浏览表tableName某一列的数据,如果某一行记录中该列数据不存在,则返回null。要求当参数column为某一列族名称时,如果底下有若干个列限定符,则要列出每个列限定符代表的列的数据;当参数column为某一列具体名称(例如“Score:Math”)时,只需要列出该列的数据。
(4)modifyData(String tableName, String row, String column)
修改表tableName,行row(可以用学生姓名S_Name表示),列column指定的单元格的数据。
(5)deleteRow(String tableName, String row)
删除表tableName中row指定的行的记录。
4.实验报告
题目: 熟悉常用的HBase操作 姓名 闫浩 日期
2024/12/4
实验环境:
操作系统:Ubuntu 22.04
Hadoop版本:3.3.6
HBase版本:2.2.2
JDK版本:1.8
实验内容与完成情况:
(一)编程实现以下指定功能,并用Hadoop提供的HBase Shell命令完成相同任务:
(1) 列出HBase所有的表的相关信息,例如表名;
(2) 在终端打印出指定的表的所有记录数据;
(3) 向已经创建好的表添加和删除指定的列族或列;
(4) 清空指定的表的所有记录数据;
(5) 统计表的行数。
(二)HBase数据库操作
-
现有以下关系型数据库中的表和数据(见表14-3到表14-5),要求将其转换为适合于HBase存储的表并插入数据:
学生表 Student
Row Key: 学号 (S_No)
列族: personal_info
列: S_Name, S_Sex, S_Age
课程表 Course
HBase表结构
Row Key: 课程号 (C_No)
列族: course_info
列: C_Name, C_Credit
选课表 SC
Row Key: 由学生学号 (SC_Sno) 和课程号 (SC_Cno) 组成的复合键 (SC_Sno#SC_Cno)
列族: score
列: SC_Score -
请编程实现以下功能:
(1)createTable(String tableName, String[] fields)
创建表,参数tableName为表的名称,字符串数组fields为存储记录各个字段名称的数组。要求当HBase已经存在名为tableName的表的时候,先删除原有的表,然后再创建新的表。
package com.example.common;
import org.apache.hadoop.conf.configuration;
import org.apache.hadoop.hbase.;
import org.apache,hadoop.hbase.client.;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.I0Exception;
public class test6 {
public static Configuration configuration;
public static connection connection;
public static Admin admin;
public static void init() {
configuration = HBaseconfiguration.create();
configuration, set("Hbase.rootdir", "hdfs://localhost:9000/hbase");
try {
connection = ConnectionFactory, createconnection(configuration);
admin = connection.getAdmin();
} catch (IoException e) {
e.printstackTrace();
}
}
public static void close () {
try {
if (admin != null) {
admin.close();
}catch(I0Exception e){
e.printstackTrace();
}
}
}
public static void createTable(String myTableName, string[] fields){
I0Exception {throws
TableName tableName = TableName.value0f(myTableName);
if (admin.tableExists(tableName)) {
System.out.println("table exist");
admin.disableTable(tableName);
admin.deleteTable(tableName);
TableDescriptorBuilder tableDescriptor = TableDescriptorBuilder.newBuilder(tableName),
for (string str : fields) {
ColumnfamilyDescriptor family = columnfamilyDescriptorBuilder.newBuilder(Bytes, toBytes(str)).build();
tableDescriptor.setColumnFamily(family);
admin.createTable(tableDescriptor.build());
System.out.println("table created");
}
}
public static void main (string[]args)throws IoException {
init();
createTable(myTableName:"table2", new string[]{"score"});
close();
}
}
(2)addRecord(String tableName, String row, String[] fields, String[] values)
向表tableName、行row(用S_Name表示)和字符串数组fields指定的单元格中添加对应的数据values。其中,fields中每个元素如果对应的列族下还有相应的列限定符的话,用“columnFamily:column”表示。例如,同时向“Math”、“Computer Science”、“English”三列添加成绩时,字符串数组fields为{“Score:Math”, ”Score:Computer Science”, ”Score:English”},数组values存储这三门课的成绩。
package com.example.common;
import org.apache.hadoop.conf.configuration;
import org.apache.hadoop.hbase.HBaseconfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import java.io.I0Exception;
public class test6 {
public static configuration configuration;
public static connection connection;
public static Admin admin;
public static void addRecord(String tableName,string row,stringl] fields,stringl] values) throws
I0Exception {
init():
Table table = connection.getTable(TableName.value0f(tableName));
for(int i =o;i != fields.length;i++){
Put put = new Put(row.getBytes());
String[] cols = fields[i].split(regex:"😊;
put, addcolumn(cols[0].getBytes(), cols[1].getBytes(), values[i].getBytes());
table.put(put);
}
table.close();
close();
}
public static void init() {
configuration = HBaseconfiguration.create();
configuration.set("hbase.rootdir","hdfs://localhost:9000/hbase");
try {
connection = ConnectionFactory.createConnection(configuration);
admin = connection.getAdmin();
} catch (IExceptione) {
e.printstackTrace();
}
}
public static void close() {
try {
if (admin != null)
admin.close();
if (null != connection)
connection.close();
} catch ( 0E xception e){
e.printstackTrace();
}
}
public static void main(string[] args) {
String[] fields = {"Score:Math", "Score:Computer Science", "Score:English"};
String[] values = {"99","8","100"};
try {
addRecord(tableName:"student", row:"S Name", fields, values);
catch(IException e){
e.printstackTrace();
}
}
}
}
(3)scanColumn(String tableName, String column)
浏览表tableName某一列的数据,如果某一行记录中该列数据不存在,则返回null。要求当参数column为某一列族名称时,如果底下有若干个列限定符,则要列出每个列限定符代表的列的数据;当参数column为某一列具体名称(例如“Score:Math”)时,只需要列出该列的数据。
package com.example.common;
import org.apache.hadoop.conf.configuration;
import org.apache.hadoop.hbase.;
import org.apache.hadoop.hbase.client.;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.I0Exception;
public class scancolumn {
public static configuration configuration;
public static connection connection;
public static Admin admin;
public static void init() {
configuration = HBaseconfiguration.create();
configuration, set("Hbase.rootdir", "hdfs://localhost:9000/hbase");
try {
connection = ConnectionFactory.createconnection(configuration);
admin = connection.getAdmin();
} catch (IoException e) {
e.printstackTrace();
public static void close () {
try {
if (admin != null) {
admin.close();
}catch(I0xception e){
e.printstackTrace();
}
}
}
}
}
public static void scancolumn(string tableName, String column) throws IoException {
Table table = connection.getTable(TableName.valueof(tableName));
Scan scan = new scan();
scan.getAllowPartialResults();
ResultScanner resultScanner = table.getScanner(scan);
for (Result result = resultScanner, next (); result != null ;
result = resultScanner.next())
Cell[] cells = result.rawcells();
for (cell cell : cells) {
System.out.print("行键:" + Bytes.tostring(cellutil.cloneRow(cell)));
System.out.print("\t列族" + Bytes.tostring(cellutil.cloneFamily(cell)));
System.out.print("\t列:" + Bytes, tostring(cellutil.cloneQualifier(cell)));
System.out.println("\t值:" + Bytes, tostring(cellutil.cloneValue(cell)));
System, out.println("\t时间戳:" + cell.getTimestamp());
table.close();
}
}
public static void main (string[]args)throws IoException {
init();
scanColumn(tableName:"student", column:"s Name");
close();
}
}
(4)modifyData(String tableName, String row, String column)
修改表tableName,行row(可以用学生姓名S_Name表示),列column指定的单元格的数据。
package com.example.common;
import org.apache.hadoop.conf.configuration;
import org.apache.hadoop.hbase.;
import org.apache.hadoop.hbase.client.;
import java.io.IoException;
import java.util.scanner;
public class modifyData {
public static configuration configuration;
public static connection connection;
public static Admin admin;
public static long ts;
public static void init() {
configuration = HBaseconfiguration.create();
configuration.set("Hbase.rootdir", "hdfs://localhost:9000/hbase");
try {
connection = ConnectionFactory.createconnection(configuration);
admin = connection.getAdmin();
} catch (IoException e) {
e.printstackTrace();
}
}
public static void close() {
try {
if (admin != null) {
admin.close();
}catch(IoException e){
e.printstackTrace():
}
}
}
public static void modifyData (string tableName, string row, string column, string val)throws
I0Exception {
init();
Table table = connection.getTable(TableName.value0f(tableName));
Put put = new Put(row.getBytes());
put, addcolumn(column.getBytes(), qualifier:null, val.getBytes());
table.put(put);
table.close();
close();
}
public static void main (string[]args)throws IoException {
init();
modifyData(tableName:"Student", row:"row 1", column:"S Name", va:"Name 1");
close();
}
}
(5)deleteRow(String tableName, String row)
删除表tableName中row指定的行的记录。
package com.example.common;
import org.apache.hadoop.conf.configuration;
import org.apache.hadoop.hbase.;
import org.apache.hadoop.hbase.client.;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IoException;
public class deleteRow {
public static configuration configuration;
public static connection connection;
public static Admin admin;
public static void init() {
configuration = HBaseConfiguration.create();
configuration, set("Hbase.rootdir", "hdfs://localhost:9000/hbase");
try {
connection = ConnectionFactory.createconnection(configuration);
admin = connection.getAdmin();
} catch (I0Exception e) {
e.printstackTrace();
}
}
public static void close() {
try {
if (admin != null) {
admin.close();
}catch(I0Exception e){
e.printstackTrace();
}
}
}
public static void deleteRow(string tableName,String row)throws IoException {
init();
Table table = connection.getTable(TableName.value0f(tableName));
Delete delete = new Delete(row.getBytes());
table.delete(delete);
table.close();
close();
}
public static void main(string[] args)throws IoException {
init();
deleteRow(tableName:"student", row:"sc score");
close();
}
}
出现的问题:
-
HBase连接问题
在进行操作时,我遇到过连接HBase时的异常,主要是由于HBase配置错误或者HBase服务未启动。连接配置未正确指定HBase的root目录,导致连接失败。 -
表结构设计问题
在设计表时,我曾未考虑到复合行键的设计导致后期在插入数据时出现错误。HBase要求行键(Row Key)的设计非常重要,特别是在多列数据时,复合行键的设计至关重要。
解决方案(列出遇到的问题和解决办法,列出没有解决的问题):
- HBase连接问题:检查HBase配置文件,确保配置正确。通过查看hbase-site.xml文件确保hbase.rootdir配置项指向正确的HDFS路径。启动HBase集群并验证HBase Web UI是否正常工作。
- 表结构设计问题:在设计HBase表时,特别是复合行键的设计要慎重。针对实际情况,设计合理的行键方案。例如,SC_Sno#SC_Cno作为选课表的复合行键。我在设计学生表、课程表时,合理安排了列族和列的命名,以确保数据存储的高效性和查询的便捷性。
- Java代码中拼写错误:在编写代码时,注意使用IDE中的自动提示和代码检查功能,避免拼写错误。及时编译和运行,发现问题后及时修正。
- 删除表时操作不当:在进行表删除操作时,确保先调用disableTable()禁用表,再执行deleteTable()删除表。使用HBase shell命令验证表的状态是否正常。
实验4
NoSQL和关系数据库的操作比较
1.实验目的
(1)理解四种数据库(MySQL、HBase、Redis和MongoDB)的概念以及不同点;
(2)熟练使用四种数据库操作常用的Shell命令;
(3)熟悉四种数据库操作常用的Java API。
2.实验平台
(1)操作系统:Linux(建议Ubuntu16.04或Ubuntu18.04);
(2)Hadoop版本:3.1.3;
(3)MySQL版本:5.6;
(4)HBase版本:2.2.2;
(5)Redis版本:5.0.5;
(6)MongoDB版本:4.0.16;
(7)JDK版本:1.8;
(8)Java IDE:Eclipse;
3.实验步骤
(一) MySQL数据库操作
学生表如14-7所示。
表14-7 学生表Student
Name English Math Computer
zhangsan 69 86 77
lisi 55 100 88
- 根据上面给出的Student表,在MySQL数据库中完成如下操作:
(1)在MySQL中创建Student表,并录入数据;
(2)用SQL语句输出Student表中的所有记录;
(3)查询zhangsan的Computer成绩;
(4)修改lisi的Math成绩,改为95。
2.根据上面已经设计出的Student表,使用MySQL的JAVA客户端编程实现以下操作:
(1)向Student表中添加如下所示的一条记录:
scofield 45 89 100
(2)获取scofield的English成绩信息
(二)HBase数据库操作
学生表Student如表14-8所示。
表14-8 学生表Student
name score
English Math Computer
zhangsan 69 86 77
lisi 55 100 88
- 根据上面给出的学生表Student的信息,执行如下操作:
(1)用Hbase Shell命令创建学生表Student;
(2)用scan命令浏览Student表的相关信息;
(3)查询zhangsan的Computer成绩;
(4)修改lisi的Math成绩,改为95。
2.根据上面已经设计出的Student表,用HBase API编程实现以下操作:
(1)添加数据:English:45 Math:89 Computer:100
scofield 45 89 100
(2)获取scofield的English成绩信息。
(三)Redis数据库操作
Student键值对如下:
zhangsan:{
English: 69
Math: 86
Computer: 77
}
lisi:{
English: 55
Math: 100
Computer: 88
}
- 根据上面给出的键值对,完成如下操作:
(1)用Redis的哈希结构设计出学生表Student(键值可以用student.zhangsan和student.lisi来表示两个键值属于同一个表);
(2)用hgetall命令分别输出zhangsan和lisi的成绩信息;
(3)用hget命令查询zhangsan的Computer成绩;
(4)修改lisi的Math成绩,改为95。
2.根据上面已经设计出的学生表Student,用Redis的JAVA客户端编程(jedis),实现如下操作:
(1)添加数据:English:45 Math:89 Computer:100
该数据对应的键值对形式如下:
scofield:{
English: 45
Math: 89
Computer: 100
}
(2)获取scofield的English成绩信息
(四)MongoDB数据库操作
Student文档如下:
{
“name”: “zhangsan”,
“score”: {
“English”: 69,
“Math”: 86,
“Computer”: 77
}
}
{
“name”: “lisi”,
“score”: {
“English”: 55,
“Math”: 100,
“Computer”: 88
}
}
1.根据上面给出的文档,完成如下操作:
(1)用MongoDB Shell设计出student集合;
(2)用find()方法输出两个学生的信息;
(3)用find()方法查询zhangsan的所有成绩(只显示score列);
(4)修改lisi的Math成绩,改为95。
2.根据上面已经设计出的Student集合,用MongoDB的Java客户端编程,实现如下操作:
(1)添加数据:English:45 Math:89 Computer:100
与上述数据对应的文档形式如下:
{
“name”: “scofield”,
“score”: {
“English”: 45,
“Math”: 89,
“Computer”: 100
}
}
(2)获取scofield的所有成绩成绩信息(只显示score列)
4.实验报告
题目: NoSQL与关系数据库的操作比较 姓名 闫浩 日期
2024/12/6
实验环境:
操作系统:Linux(建议Ubuntu 16.04或Ubuntu 18.04)
Hadoop版本:3.1.3
MySQL版本:5.6
HBase版本:2.2.2
Redis版本:5.0.5
MongoDB版本:4.0.16
JDK版本:1.8
开发环境:Eclipse
实验内容与完成情况:
(一) MySQL数据库操作
学生表如14-7所示。
1根据上面给出的Student表,在MySQL数据库中完成如下操作:
(1) 在MySQL中创建Student表,并录入数据;
(2) 用SQL语句输出Student表中的所有记录;
(3) 查询zhangsan的Computer成绩;
(4) 修改lisi的Math成绩,改为95。
3.根据上面已经设计出的Student表,使用MySQL的JAVA客户端编程实现以下操作:
(1)向Student表中添加如下所示的一条记录:
scofield 45 89 100
package Main;
import java.sql.*;
import com.mysql.jdbc.Driver;
public class main{
static final String DRIVER="com.mysql.jdbc.Driver";
static final String DB="jdbc:mysql://localhost/student?useSSL=false";
static final String USER="root";
static final String PASSWD="123456";
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
try {
//加载驱动程序
Class.forName(DRIVER);
System.out.println("Connecting to a selected database...");
//打开一个连接
conn=DriverManager.getConnection(DB, USER, PASSWD);
//执行一个查询
stmt=conn.createStatement();
String sql="insert into student values('scofield',45,89,100)";
stmt.executeUpdate(sql);
System.out.println("Inserting records into the table successfully!");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e) {
e.printStackTrace();
}finally
{
if(stmt!=null)
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
if(conn!=null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
运行结果:
(2)获取scofield的English成绩信息
package Main;
import java.sql.*;
import com.mysql.jdbc.Driver;
public class main {
static final String DRIVER="com.mysql.jdbc.Driver";
static final String DB="jdbc:mysql://localhost/student?useSSL=false";
//Database auth
static final String USER="root";
static final String PASSWD="root";
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try {
Class.forName(DRIVER);
System.out.println("Connecting to a selected database...");
conn=DriverManager.getConnection(DB, USER, PASSWD);
stmt=conn.createStatement();
String sql="select name,English from student where name='scofield' ";
rs=stmt.executeQuery(sql);
System.out.println("name"+"\t\t"+"English");
while(rs.next())
{
System.out.print(rs.getString(1)+"\t\t");
System.out.println(rs.getInt(2));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e) {
e.printStackTrace();
}finally
{
if(rs!=null)
try {
rs.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
if(stmt!=null)
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
if(conn!=null)
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
运行结果:
(二)HBase数据库操作
根据上面给出的学生表Student的信息,执行如下操作:
(1) 用Hbase Shell命令创建学生表Student;
(2) 用scan命令浏览Student表的相关信息;
(3) 查询zhangsan的Computer成绩;
(4)修改lisi的Math成绩,改为95。
2.根据上面已经设计出的Student表,用HBase API编程实现以下操作:
(1)添加数据:English:45 Math:89 Computer:100
scofield 45 89 100
package Main;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Table;
public class main {
public static Configuration configuration;
public static Connection connection;
public static Admin admin;
public static void main(String[] args) {
// TODO Auto-generated method stub
configuration = HBaseConfiguration.create();
configuration.set("hbase.rootdir","hdfs://127.0.0.1:8020/hbase");
try{
connection = ConnectionFactory.createConnection(configuration);
admin = connection.getAdmin();
}catch (IOException e){
e.printStackTrace();
}
try {
insertRow("student","scofield","score","English","45");
insertRow("student","scofield","score","Math","89");
insertRow("student","scofield","score","Computer","100");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
close();
}
public static void insertRow(String tableName,String rowKey,String colFamily,
String col,String val) throws IOException {
Table table = connection.getTable(TableName.valueOf(tableName));
Put put = new Put(rowKey.getBytes());
put.addColumn(colFamily.getBytes(), col.getBytes(), val.getBytes());
table.put(put);
table.close();
}
public static void close(){
try{
if(admin != null){
admin.close();
}
if(null != connection){
connection.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
运行结果:
(2)获取scofield的English成绩信息。
package Main;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.Table;
public class main {
public static Configuration configuration;
public static Connection connection;
public static Admin admin;
public static void main(String[] args) {
// TODO Auto-generated method stub
configuration = HBaseConfiguration.create();
configuration.set("hbase.rootdir", "hdfs://127.0.0.1:8020/hbase");
try {
connection = ConnectionFactory.createConnection(configuration);
admin = connection.getAdmin();
} catch (IOException e) {
e.printStackTrace();
}
try {
getData("student", "scofield", "score", "English");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
close();
}
public static void getData(String tableName, String rowKey, String colFamily,
String col) throws IOException {
Table table = connection.getTable(TableName.valueOf(tableName));
Get get = new Get(rowKey.getBytes());
get.addColumn(colFamily.getBytes(), col.getBytes());
Result result = table.get(get);
showCell(result);
table.close();
}
public static void showCell(Result result) {
Cell[] cells = result.rawCells();
for (Cell cell : cells) {
System.out.println("RowName:" + new String(CellUtil.cloneRow(cell)) + " ");
System.out.println("Timetamp:" + cell.getTimestamp() + " ");
System.out.println("column Family:" + new String(CellUtil.cloneFamily(cell)) + " ");
System.out.println("row Name:" + new String(CellUtil.cloneQualifier(cell)) + " ");
System.out.println("value:" + new String(CellUtil.cloneValue(cell)) + " ");
}
}
public static void close() {
try {
if (admin != null) {
admin.close();
}
if (null != connection) {
connection.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
(三)Redis数据库操作
Student键值对如下:
zhangsan:{
English: 69
Math: 86
Computer: 77
}
lisi:{
English: 55
Math: 100
Computer: 88
}
- 根据上面给出的键值对,完成如下操作:
(1)用Redis的哈希结构设计出学生表Student(键值可以用student.zhangsan和student.lisi来表示两个键值属于同一个表);
(2)用hgetall命令分别输出zhangsan和lisi的成绩信息;
(3)用hget命令查询zhangsan的Computer成绩;
(4)修改lisi的Math成绩,改为95。
2.根据上面已经设计出的学生表Student,用Redis的JAVA客户端编程(jedis),实现如下操作:
(1)添加数据:English:45 Math:89 Computer:100
该数据对应的键值对形式如下:
scofield:{
English: 45
Math: 89
Computer: 100
}
import java.util.Map;
import redis.clients.jedis.Jedis;
public class jedis_test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Jedis jedis = new Jedis("localhost");
//输入scofield的成绩信息
jedis.hset("student.scofield", "English","45");
jedis.hset("student.scofield", "Math","89");
jedis.hset("student.scofield", "Computer","100");
Map<String,String> value = jedis.hgetAll("student.scofield");
for(Map.Entry<String, String> entry:value.entrySet())
{
//输出scofield的成绩信息
System.out.println(entry.getKey()+":"+entry.getValue());
}
}
}
运行结果:
(2)获取scofield的English成绩信息
import java.util.Map;
import redis.clients.jedis.Jedis;
public class jedis_query {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Jedis jedis = new Jedis("localhost");
//获得scofield的英语成绩信息
String value=jedis.hget("student.scofield", "English");
//输出scofield的英语成绩信息
System.out.println("scofield's English score is: "+value);
}
}
运行结果:
(四)MongoDB数据库操作
Student文档如下:
{
“name”: “zhangsan”,
“score”: {
“English”: 69,
“Math”: 86,
“Computer”: 77
}
}
{
“name”: “lisi”,
“score”: {
“English”: 55,
“Math”: 100,
“Computer”: 88
}
}
1.根据上面给出的文档,完成如下操作:
(1)用MongoDB Shell设计出student集合;
(2)用find()方法输出两个学生的信息;
(3)用find()方法查询zhangsan的所有成绩(只显示score列);
(4)修改lisi的Math成绩,改为95。
2.根据上面已经设计出的Student集合,用MongoDB的Java客户端编程,实现如下操作:
(1)添加数据:English:45 Math:89 Computer:100
与上述数据对应的文档形式如下:
{
“name”: “scofield”,
“score”: {
“English”: 45,
“Math”: 89,
“Computer”: 100
}
}
import java.util.ArrayList;
import java.util.List;
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
public class mongo_insert {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//实例化一个mongo客户端
MongoClient mongoClient=new MongoClient("localhost",27017);
//实例化一个mongo数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase("Stu");
//获取数据库中某个集合
MongoCollection<Document> collection = mongoDatabase.getCollection("student");
//实例化一个文档,内嵌一个子文档
Document document=new Document("name","scofield").
append("score", new Document("English",45).
append("Math", 89).
append("Computer", 100));
List<Document> documents = new ArrayList<Document>();
documents.add(document);
//将文档插入集合中
collection.insertMany(documents);
System.out.println("文档插入成功");
}
}
(2)获取scofield的所有成绩成绩信息(只显示score列)
import java.util.ArrayList;
import java.util.List;
import org.bson.Document;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import static com.mongodb.client.model.Filters.eq;
public class mongo_query {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//实例化一个mongo客户端
MongoClient mongoClient=new MongoClient("localhost",27017);
//实例化一个mongo数据库
MongoDatabase mongoDatabase = mongoClient.getDatabase("Stu");
//获取数据库中某个集合
MongoCollection<Document> collection = mongoDatabase.getCollection("student");
//进行数据查找,查询条件为name=scofield, 对获取的结果集只显示score这个域
MongoCursor<Document> cursor=collection.find( new Document("name","scofield")).
projection(new Document("score",1).append("_id", 0)).iterator();
while(cursor.hasNext())
System.out.println(cursor.next().toJson());
}
}
运行结果:
出现的问题:
MySQL部分:在初次配置JDBC连接时,忘记配置数据库连接的SSL设置,导致无法连接到MySQL数据库。需要将useSSL=false添加到连接URL中。
HBase部分:HBase命令行工具中,执行create命令时没有正确设置列族名称,导致创建表失败。需要检查列族是否正确添加。在Java代码中使用HBase API时,未正确关闭表连接,导致内存泄漏。需要在操作完成后手动关闭表和连接。
解决方案(列出遇到的问题和解决办法,列出没有解决的问题):
MySQL部分:
修改数据库连接URL,加入useSSL=false,并重新测试连接。
HBase部分:
检查HBase命令中的列族配置,并确保创建表时添加了正确的列族。在Java代码中,每次操作数据库后都关闭连接和表。
Redis部分:
启动Redis服务,确保客户端配置正确,连接Redis服务器时使用jedis.ping()测试连接。
实验5
MapReduce初级编程实践
1.实验目的
(1)通过实验掌握基本的MapReduce编程方法;
(2)掌握用MapReduce解决一些常见的数据处理问题,包括数据去重、数据排序和数据挖掘等。
2.实验平台
(1)操作系统:Linux(建议Ubuntu16.04或Ubuntu18.04)
(2)Hadoop版本:3.1.3
3.实验步骤
(一)编程实现文件合并和去重操作
对于两个输入文件,即文件A和文件B,请编写MapReduce程序,对两个文件进行合并,并剔除其中重复的内容,得到一个新的输出文件C。下面是输入文件和输出文件的一个样例供参考。
输入文件A的样例如下:
20170101 x
20170102 y
20170103 x
20170104 y
20170105 z
20170106 x
输入文件B的样例如下:
20170101 y
20170102 y
20170103 x
20170104 z
20170105 y
根据输入文件A和B合并得到的输出文件C的样例如下:
20170101 x
20170101 y
20170102 y
20170103 x
20170104 y
20170104 z
20170105 y
20170105 z
20170106 x
(二)编写程序实现对输入文件的排序
现在有多个输入文件,每个文件中的每行内容均为一个整数。要求读取所有文件中的整数,进行升序排序后,输出到一个新的文件中,输出的数据格式为每行两个整数,第一个数字为第二个整数的排序位次,第二个整数为原待排列的整数。下面是输入文件和输出文件的一个样例供参考。
输入文件1的样例如下:
33
37
12
40
输入文件2的样例如下:
4
16
39
5
输入文件3的样例如下:
1
45
25
根据输入文件1、2和3得到的输出文件如下:
1 1
2 4
3 5
4 12
5 16
6 25
7 33
8 37
9 39
10 40
11 45
(三)对给定的表格进行信息挖掘
下面给出一个child-parent的表格,要求挖掘其中的父子辈关系,给出祖孙辈关系的表格。
输入文件内容如下:
child parent
Steven Lucy
Steven Jack
Jone Lucy
Jone Jack
Lucy Mary
Lucy Frank
Jack Alice
Jack Jesse
David Alice
David Jesse
Philip David
Philip Alma
Mark David
Mark Alma
输出文件内容如下:
grandchild grandparent
Steven Alice
Steven Jesse
Jone Alice
Jone Jesse
Steven Mary
Steven Frank
Jone Mary
Jone Frank
Philip Alice
Philip Jesse
Mark Alice
Mark Jesse
4.实验报告
题目: MapReduce初级编程实践 姓名 闫浩 日期
2024/12/7
实验环境:
操作系统:Ubuntu 22.04
Hadoop版本:3.3.6
JDK版本:1.8
编程语言:Java
命令行工具:HDFS和MapReduce命令
实验内容与完成情况:
(一)编程实现文件合并和去重操作
对于两个输入文件,即文件A和文件B,请编写MapReduce程序,对两个文件进行合并,并剔除其中重复的内容,得到一个新的输出文件C。
进入 Hadoop 安装目录,启动 hadoop:
cd /usr/local/hadoop
sbin/start-dfs.sh
新建文件夹,创建文件 A、B:
sudo mkdir MapReduce && cd MapReduce
sudo vim A
sudo vim B
编写 Java 文件实现 MapReduce:
sudo vim Merge.java
编译 Merge.java:
javac Merge.java
打包生成的 class 文件为 jar 包:
jar -cvf Merge.jar *.class
实现的 Java 代码如下:
import java.io.IOException;
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.util.GenericOptionsParser;
public class Merge {
/**
* @param args
* 对A,B两个文件进行合并,并剔除其中重复的内容,得到一个新的输出文件C
*/
//重载map函数,直接将输入中的value复制到输出数据的key上
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;
context.write(text, new Text(""));
}
}
//重载reduce函数,直接将输入中的key复制到输出数据的key上
public static class Reduce extends Reducer<Text, Text, Text, Text>{
public void reduce(Text key, Iterable<Text> values, Context context ) throws IOException,InterruptedException{
context.write(key, new Text(""));
}
}
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
Configuration conf = new Configuration();
conf.set("fs.default.name","hdfs://localhost:9000");
String[] otherArgs = new String[]{"input","output"}; /* 直接设置输入参数 */
if (otherArgs.length != 2) {
System.err.println("Usage: wordcount
System.exit(2);
}
Job job = Job.getInstance(conf,"Merge and duplicate removal");
job.setJarByClass(Merge.class);
job.setMapperClass(Map.class);
job.setCombinerClass(Reduce.class);
job.setReducerClass(Reduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
创建 Hadoop 主目录为 /user/hadoop 并创建 input 文件夹:
/usr/local/hadoop/bin/hdfs dfs -mkdir -p /user/hadoop
/usr/local/hadoop/bin/hdfs dfs -mkdir input
上传 A、B 文件到 input 文件夹中:
/usr/local/hadoop/bin/hdfs dfs -put ./A input
/usr/local/hadoop/bin/hdfs dfs -put ./B input
使用我们刚生成的 Merge.jar 包:
/usr/local/hadoop/bin/hadoop jar Merge.jar Merge
查看输出结果:
/usr/local/hadoop/bin/hdfs dfs -cat output/*
(二)编写程序实现对输入文件的排序
现在有多个输入文件,每个文件中的每行内容均为一个整数。要求读取所有文件中的整数,进行升序排序后,输出到一个新的文件中,输出的数据格式为每行两个整数,第一个数字为第二个整数的排序位次,第二个整数为原待排列的整数。
1.创建1.txt ,2.txt ,3.txt,输入上述内容
vim 1.txt
vim 2.txt
vim 3.txt
2.将1.txt ,2.txt ,3.txt上传到HDFS中
./bin/hdfs dfs -put ./1.txt /input/test2/
./bin/hdfs dfs -put ./2.txt /input/test2/
./bin/hdfs dfs -put ./3.txt /input/test2/
3.编写实验代码并编译运行
package com.xusheng.mapreduce.shiyan;
import java.io.IOException;
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.Partitioner;
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.util.GenericOptionsParser;
public class MergeSort {
/**
* @param xusheng
* 输入多个文件,每个文件中的每行内容均为一个整数
* 输出到一个新的文件中,输出的数据格式为每行两个整数,第一个数字为第二个整数的排序位次,第二个整数为原待排列的整数
*/
//map函数读取输入中的value,将其转化成IntWritable类型,最后作为输出key
public static class Map extends Mapper<Object, Text, IntWritable, IntWritable>{
private static IntWritable data = new IntWritable();
public void map(Object key, Text value, Context context) throws IOException,InterruptedException{
String text = value.toString();
data.set(Integer.parseInt(text));
context.write(data, new IntWritable(1));
}
}
//reduce函数将map输入的key复制到输出的value上,然后根据输入的value-list中元素的个数决定key的输出次数,定义一个全局变量line_num来代表key的位次
public static class Reduce extends Reducer<IntWritable, IntWritable, IntWritable, IntWritable>{
private static IntWritable line_num = new IntWritable(1);
public void reduce(IntWritable key, Iterable<IntWritable> values, Context context) throws IOException,InterruptedException{
for(IntWritable val : values){
context.write(line_num, key);
line_num = new IntWritable(line_num.get() + 1);
}
}
}
//自定义Partition函数,此函数根据输入数据的最大值和MapReduce框架中Partition的数量获取将输入数据按照大小分块的边界,然后根据输入数值和边界的关系返回对应的Partiton ID
public static class Partition extends Partitioner<IntWritable, IntWritable>{
public int getPartition(IntWritable key, IntWritable value, int num_Partition){
int Maxnumber = 65223;//int型的最大数值
int bound = Maxnumber/num_Partition+1;
int keynumber = key.get();
for (int i = 0; i<num_Partition; i++){
if(keynumber<bound * (i+1) && keynumber>=bound * i){
return i;
}
}
return -1;
}
}
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
Configuration conf = new Configuration();
//conf.set("fs.default.name","hdfs://localhost:9000");
conf.set("fs.defaultFS","hdfs://hadoop102:8020");
String[] otherArgs = new String[]{"/input/test2","/output/test2"}; /* 直接设置输入参数 */
if (otherArgs.length != 2) {
System.err.println("Usage: wordcount <in><out>");
System.exit(2);
}
Job job = Job.getInstance(conf,"Merge and sort");//实例化Merge类
job.setJarByClass(MergeSort.class);//设置主类名
job.setMapperClass(Map.class);//指定使用上述代码自定义的Map类
job.setReducerClass(Reduce.class);//指定使用上述代码自定义的Reduce类
job.setPartitionerClass(Partition.class);
job.setOutputKeyClass(IntWritable.class);
job.setOutputValueClass(IntWritable.class);//设定Reduce类输出的<K,V>,V类型
FileInputFormat.addInputPath(job, new Path(otherArgs[0]));//添加输入文件位置
FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));//设置输出结果文件位置
System.exit(job.waitForCompletion(true) ? 0 : 1);//提交任务并监控任务状态
}
}
4 .实验结果
(三)对给定的表格进行信息挖掘
下面给出一个child-parent的表格,要求挖掘其中的父子辈关系,给出祖孙辈关系的表格。
1.创建child.txt,输入上述内容
vim child.txt
- 将child.txt上传到HDFS中
./bin/hdfs dfs -put ./ child.txt /input/test3/
3.编写实验代码并编译运行
package com.xusheng.mapreduce.shiyan;
import java.io.IOException;
import java.util.*;
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.util.GenericOptionsParser;
public class simple_data_mining {
public static int time = 0;
/**
* @param xusheng
* 输入一个child-parent的表格
* 输出一个体现grandchild-grandparent关系的表格
*/
//Map将输入文件按照空格分割成child和parent,然后正序输出一次作为右表,反序输出一次作为左表,需要注意的是在输出的value中必须加上左右表区别标志
public static class Map extends Mapper<Object, Text, Text, Text>{
public void map(Object key, Text value, Context context) throws IOException,InterruptedException{
String child_name = new String();
String parent_name = new String();
String relation_type = new String();
String line = value.toString();
int i = 0;
while(line.charAt(i) != ' '){
i++;
}
String[] values = {line.substring(0,i),line.substring(i+1)};
if(values[0].compareTo("child") != 0){
child_name = values[0];
parent_name = values[1];
relation_type = "1";//左右表区分标志
context.write(new Text(values[1]), new Text(relation_type+"+"+child_name+"+"+parent_name));
//左表
relation_type = "2";
context.write(new Text(values[0]), new Text(relation_type+"+"+child_name+"+"+parent_name));
//右表
}
}
}
public static class Reduce extends Reducer<Text, Text, Text, Text>{
public void reduce(Text key, Iterable<Text> values,Context context) throws IOException,InterruptedException{
if(time == 0){ //输出表头
context.write(new Text("grand_child"), new Text("grand_parent"));
time++;
}
int grand_child_num = 0;
String grand_child[] = new String[10];
int grand_parent_num = 0;
String grand_parent[]= new String[10];
Iterator ite = values.iterator();
while(ite.hasNext()){
String record = ite.next().toString();
int len = record.length();
int i = 2;
if(len == 0) continue;
char relation_type = record.charAt(0);
String child_name = new String();
String parent_name = new String();
//获取value-list中value的child
while(record.charAt(i) != '+'){
child_name = child_name + record.charAt(i);
i++;
}
i=i+1;
//获取value-list中value的parent
while(i<len){
parent_name = parent_name+record.charAt(i);
i++;
}
//左表,取出child放入grand_child
if(relation_type == '1'){
grand_child[grand_child_num] = child_name;
grand_child_num++;
}
else{//右表,取出parent放入grand_parent
grand_parent[grand_parent_num] = parent_name;
grand_parent_num++;
}
}
if(grand_parent_num != 0 && grand_child_num != 0 ){
for(int m = 0;m<grand_child_num;m++){
for(int n=0;n<grand_parent_num;n++){
context.write(new Text(grand_child[m]), new Text(grand_parent[n]));
//输出结果
}
}
}
}
}
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
Configuration conf = new Configuration();
//conf.set("fs.default.name","hdfs://localhost:9000");
conf.set("fs.default.name","hdfs://hadoop102:8020");
String[] otherArgs = new String[]{"/input/test3","/output/test3"}; /* 直接设置输入参数 */
if (otherArgs.length != 2) {
System.err.println("Usage: wordcount <in><out>");
System.exit(2);
}
Job job = Job.getInstance(conf,"Single table join");
job.setJarByClass(simple_data_mining.class);
job.setMapperClass(Map.class);
job.setReducerClass(Reduce.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
4实验结果
出现的问题:
问题:文件上传到HDFS时路径错误
描述:在将本地文件上传到HDFS时,路径不正确导致文件上传失败。
解决方案(列出遇到的问题和解决办法,列出没有解决的问题):
问题:输出文件中有重复记录
解决办法:修正了Reducer中的去重逻辑,确保重复数据只输出一次。
问题:文件上传到HDFS时路径错误
解决办法:确认上传路径正确,重新上传文件。
问题:程序在排序时未正确处理排序位次
解决办法:修正了Reducer中排序位次的生成逻辑,确保输出结果正确。
实验6
熟悉Hive的基本操作
1.实验目的
(1)理解Hive作为数据仓库在Hadoop体系结构中的角色。
(2)熟练使用常用的HiveQL。
2.实验平台
操作系统:Ubuntu18.04(或Ubuntu16.04)。
Hadoop版本:3.1.3。
Hive版本:3.1.2。
JDK版本:1.8。
3.数据集
由《Hive编程指南》(O'Reilly系列,人民邮电出版社)提供,下载地址:
https://raw.githubusercontent.com/oreillymedia/programming_hive/master/prog-hive-1st-ed-data.zip
备用下载地址:
https://www.cocobolo.top/FileServer/prog-hive-1st-ed-data.zip
解压后可以得到本实验所需的stocks.csv和dividends.csv两个文件。
4.实验步骤
(1)创建一个内部表stocks,字段分隔符为英文逗号,表结构如表14-11所示。
表14-11 stocks表结构
col_name data_type
exchange string
symbol string
ymd string
price_open float
price_high float
price_low float
price_close float
volume int
price_adj_close float
(2)创建一个外部分区表dividends(分区字段为exchange和symbol),字段分隔符为英文逗号,表结构如表14-12所示。
表14-12 dividends表结构
col_name data_type
ymd string
dividend float
exchange string
symbol string
(3)从stocks.csv文件向stocks表中导入数据。
(4) 创建一个未分区的外部表dividends_unpartitioned,并从dividends.csv向其中导入数据,表结构如表14-13所示。
表14-13 dividends_unpartitioned表结构
col_name data_type
ymd string
dividend float
exchange string
symbol string
(5)通过对dividends_unpartitioned的查询语句,利用Hive自动分区特性向分区表dividends各个分区中插入对应数据。
(6)查询IBM公司(symbol=IBM)从2000年起所有支付股息的交易日(dividends表中有对应记录)的收盘价(price_close)。
(7)查询苹果公司(symbol=AAPL)2008年10月每个交易日的涨跌情况,涨显示rise,跌显示fall,不变显示unchange。
(8)查询stocks表中收盘价(price_close)比开盘价(price_open)高得最多的那条记录的交易所(exchange)、股票代码(symbol)、日期(ymd)、收盘价、开盘价及二者差价。
(9)从stocks表中查询苹果公司(symbol=AAPL)年平均调整后收盘价(price_adj_close) 大于50美元的年份及年平均调整后收盘价。
(10)查询每年年平均调整后收盘价(price_adj_close)前三名的公司的股票代码及年平均调整后收盘价。
5.实验报告
题目: 熟悉Hive的基本操作 姓名 闫浩 日期:
2024/12/8
实验环境:
操作系统:Ubuntu22.04
Hadoop版本:3.3.6。
Hive版本:3.1.2。
JDK版本:1.8。
实验内容与完成情况:
(1)创建一个内部表stocks,字段分隔符为英文逗号,表结构如表14-11所示。
(2)创建一个外部分区表dividends(分区字段为exchange和symbol),字段分隔符为英文逗号,表结构如表14-12所示。
(3)从stocks.csv文件向stocks表中导入数据。
(4) 创建一个未分区的外部表dividends_unpartitioned,并从dividends.csv向其中导入数据。
(5)通过对dividends_unpartitioned的查询语句,利用Hive自动分区特性向分区表dividends各个分区中插入对应数据。
(6)查询IBM公司(symbol=IBM)从2000年起所有支付股息的交易日(dividends表中有对应记录)的收盘价(price_close)。
(7)查询苹果公司(symbol=AAPL)2008年10月每个交易日的涨跌情况,涨显示rise,跌显示fall,不变显示unchange。
(8)查询stocks表中收盘价(price_close)比开盘价(price_open)高得最多的那条记录的交易所(exchange)、股票代码(symbol)、日期(ymd)、收盘价、开盘价及二者差价。
(9)从stocks表中查询苹果公司(symbol=AAPL)年平均调整后收盘价(price_adj_close) 大于50美元的年份及年平均调整后收盘价。
(10)查询每年年平均调整后收盘价(price_adj_close)前三名的公司的股票代码及年平均调整后收盘价。
出现的问题:
问题:Hive无法连接到HDFS
问题:查询时数据格式不符合要求
描述:查询过程中,日期格式或者字段类型不符合预期,导致查询结果错误。
解决方案(列出遇到的问题和解决办法,列出没有解决的问题):
问题:分区表插入数据时,分区字段没有正确填充
解决办法:使用 INSERT OVERWRITE 和 PARTITION 子句正确填充分区字段。
问题:加载数据时路径不正确
解决办法:确认文件路径,确保上传到正确的 HDFS 目录。
问题:查询时数据格式不符合要求
解决办法:修正表结构中的字段类型,确保数据格式一致。
实验7
Spark初级编程实践
1.实验目的
(1)掌握使用Spark访问本地文件和HDFS文件的方法
(2)掌握Spark应用程序的编写、编译和运行方法
2.实验平台
(1)操作系统:Ubuntu18.04(或Ubuntu16.04);
(2)Spark版本:2.4.0;
(3)Hadoop版本:3.1.3。
3.实验步骤
(1)Spark读取文件系统的数据
(1)在spark-shell中读取Linux系统本地文件“/home/hadoop/test.txt”,然后统计出文件的行数;
(2)在spark-shell中读取HDFS系统文件“/user/hadoop/test.txt”(如果该文件不存在,请先创建),然后,统计出文件的行数;
(3)编写独立应用程序(推荐使用Scala语言),读取HDFS系统文件“/user/hadoop/test.txt”(如果该文件不存在,请先创建),然后,统计出文件的行数;通过sbt工具将整个应用程序编译打包成 JAR包,并将生成的JAR包通过 spark-submit 提交到 Spark 中运行命令。
(2)编写独立应用程序实现数据去重
对于两个输入文件A和B,编写Spark独立应用程序(推荐使用Scala语言),对两个文件进行合并,并剔除其中重复的内容,得到一个新文件C。下面是输入文件和输出文件的一个样例,供参考。
输入文件A的样例如下:
20170101 x
20170102 y
20170103 x
20170104 y
20170105 z
20170106 z
输入文件B的样例如下:
20170101 y
20170102 y
20170103 x
20170104 z
20170105 y
根据输入的文件A和B合并得到的输出文件C的样例如下:
20170101 x
20170101 y
20170102 y
20170103 x
20170104 y
20170104 z
20170105 y
20170105 z
20170106 z
(3)编写独立应用程序实现求平均值问题
每个输入文件表示班级学生某个学科的成绩,每行内容由两个字段组成,第一个是学生名字,第二个是学生的成绩;编写Spark独立应用程序求出所有学生的平均成绩,并输出到一个新文件中。下面是输入文件和输出文件的一个样例,供参考。
Algorithm成绩:
小明 92
小红 87
小新 82
小丽 90
Database成绩:
小明 95
小红 81
小新 89
小丽 85
Python成绩:
小明 82
小红 83
小新 94
小丽 91
平均成绩如下:
(小红,83.67)
(小新,88.33)
(小明,89.67)
(小丽,88.67)
4.实验报告
题目: Spark初级编程实践 姓名 闫浩 日期
2024/12/10
实验环境:
操作系统:Ubuntu 24.04
Spark版本:3.2.0
Hadoop版本:3.3.6
Scala版本:2.12.11
SBT版本:使用SBT工具进行打包和构建
实验内容与完成情况:
(1)Spark读取文件系统的数据
(1)在spark-shell中读取Linux系统本地文件“/home/hadoop/test.txt”,然后统计出文件的行数;
(2)在spark-shell中读取HDFS系统文件“/user/hadoop/test.txt”(如果该文件不存在,请先创建),然后,统计出文件的行数;
(3)编写独立应用程序(推荐使用Scala语言),读取HDFS系统文件“/user/hadoop/test.txt”(如果该文件不存在,请先创建),然后,统计出文件的行数;通过sbt工具将整个应用程序编译打包成 JAR包,并将生成的JAR包通过 spark-submit 提交到 Spark 中运行命令。
进入 spark 安装目录:
cd /usr/local/spark
mkdir mycode && cd mycode
创建 HDFStest 目录并编写 Scala 文件:
mkdir -p HDFStest/src/main/scala
vim ./HDFStest/src/main/scala/HDFStest.scala
进入 HDFStest 目录,创建 simple.sbt:
cd HDFStest
vim simple.sbt
内容如下:
name := "A Simple HDFS Test"
version := "1.0"
scalaVersion := "2.11.8"
libraryDependencies += "org.apache.spark" %% "spark-core" % "2.1.0"
接下来,可以通过如下代码将整个应用程序打包成 JAR:
/usr/local/sbt/sbt package
运行如下代码使用生成的 jar 包:
实验代码:
/* HDFStest.scala */
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf
object HDFStest {
def main(args: Array[String]) {
val logFile = "hdfs://localhost:9000/user/hadoop/test.txt"
val conf = new SparkConf().setAppName("Simple Application")
val sc = new SparkContext(conf)
val logData = sc.textFile(logFile, 2)
val num = logData.count()
printf("The num of this file is %d\n", num)
}
}
(2)编写独立应用程序实现数据去重
对于两个输入文件A和B,编写Spark独立应用程序(推荐使用Scala语言),对两个文件进行合并,并剔除其中重复的内容,得到一个新文件C。
进入到 mycode 目录,新建 RemDup 目录:
cd /usr/local/spark/mycode
mkdir -p RemDup/src/main/scala
cd RemDup
新建 datas 目录,写入文件 A 和文件 B:
mkdir datas
vim ./datas/A
vim ./datas/B
编写 Scala 文件:
vim ./src/main/scala/RemDup.scala
编写 simple.sbt 文件:
vim simple.sbt
内容如下:
name := "Remove Duplication"
version := "1.0"
scalaVersion := "2.11.8"
libraryDependencies += "org.apache.spark" %% "spark-core" % "2.1.0"
使用如下命令打包:
/usr/local/sbt/sbt package
使用生成的 jar 包:
/usr/local/spark/bin/spark-submit –class "RemDup" /usr/local/spark/mycode/RemDup/target/scala-2.12/remove.jar
使用如下命令查看输出:cat result/*
输出如下:
实验代码
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf
import org.apache.spark.HashPartitioner
object RemDup {
def main(args: Array[String]) {
val conf = new SparkConf().setAppName("RemDup")
val sc = new SparkContext(conf)
val dataFile = "file:///usr/local/spark/mycode/RemDup/datas"
val data = sc.textFile(dataFile,2)
val res = data.filter(_.trim().length>0).map(line=>(line.trim,"")).partitionBy(new HashPartitioner(1)).groupByKey().sortByKey().keys
res.saveAsTextFile("file:///usr/local/spark/mycode/RemDup/result")
}
}
(3)编写独立应用程序实现求平均值问题
每个输入文件表示班级学生某个学科的成绩,每行内容由两个字段组成,第一个是学生名字,第二个是学生的成绩;编写Spark独立应用程序求出所有学生的平均成绩,并输出到一个新文件中。
进入到 mycode 目录,新建 AvgScore 目录:
cd /usr/local/spark/mycode
mkdir -p AvgScore/src/main/scala
cd AvgScore
新建 datas 目录,写入文件 algorithm、database、python:
mkdir datas
vim ./datas/algorithm
vim ./datas/database
vim ./datas/python
编写 Scala 文件:
vim ./src/main/scala/AvgScore.scala
编写 simple.sbt 文件:
使用如下命令打包:
/usr/local/sbt/sbt package
使用生成的 jar 包:
使用如下命令查看输出:
cat result/*
出现的问题:
问题:Spark程序提交时出现JAR包错误
描述:在提交独立应用程序时,有时会遇到JAR包没有正确打包或依赖项缺失的问题。
问题:数据文件路径错误
描述:在读取HDFS文件时,若文件路径不正确会导致文件读取失败。
解决方案(列出遇到的问题和解决办法,列出没有解决的问题):
问题:JAR包提交时缺少依赖项
解决办法:检查 simple.sbt 配置,确保 spark-core 依赖项正确,并重新构建JAR包。
问题:HDFS路径错误导致文件读取失败
解决办法:使用 hdfs dfs -ls 确认文件路径,确保HDFS中存在相应文件,并使用正确的路径进行读取。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现