大数据系统基本实验(持续更新中)

大数据管理与分析实验报告

第一章 大数据系统基本实验

第二章 文档倒排索引算法实现

实验三 PageRank 算法实现


目录

实验平台

  • 操作系统:Ubuntu Kylin
  • Hadoop 版本:2.10.1
  • JDK 版本:1.8
  • Java IDE:Eclipse 3.8
  • Hbase版本:1.2.6

第零部分 Hadoop系统安装运行与基本程序开发

系统安装:略

参考一些安装手册或查博客

运行Hadoop自带的WordCount程序

在这里插入图片描述
找一下jar包路径用hadoop命令运行即可

使用Java API编写WordCount并运行

在运行 MapReduce 程序前, 还需要执行一项重要操作: 将/usr/local/hadoop/etc/hadoop 中将有修改过的配置文件(如伪分布式需要 coresite.xml 和 hdfs-site.xml),以及 log4j.properties 复制到 WordCount 项目下的 src 文件夹中

package org.apache.hadoop.examples;

import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.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 WordCount {
	
	public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable> {
		
		private final static IntWritable one = new IntWritable(1);
		
		private Text word = new Text();
		
		public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
			StringTokenizer itr = new StringTokenizer(value.toString());
			while (itr.hasMoreTokens()) {
				word.set(itr.nextToken());
				context.write(word, one);
			}
		}
	}
	
	public static class IntSumReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
		
		private IntWritable result = new IntWritable();
		
		public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
			int sum = 0;
			for (IntWritable val : values) {
				sum += val.get();
			}
			result.set(sum);
			context.write(key, result);
		}
	}
	
	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();
		String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();
		if (otherArgs.length != 2) {
			System.err.println("Usage:wordcount <int> <out>");
			System.exit(2);
		}
		Job job = Job.getInstance(conf);
		job.setJarByClass(WordCount.class);
		job.setMapperClass(TokenizerMapper.class);
		job.setCombinerClass(IntSumReducer.class);
		job.setReducerClass(IntSumReducer.class);
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(IntWritable.class);
		FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
		FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
		System.exit(job.waitForCompletion(true) ? 0 : 1);
	}

}

Exception in thread “main” java.lang.ClassNotFoundException: wordcount
参考博客:Exception in thread “main” java.lang.ClassNotFoundException: wordcount问题的解决
在这里插入图片描述
解决方法:不需要将jar包再上传hdfs,只需将WordCount改为org.apache.hadoop.examples.WordCount
在这里插入图片描述

第一部分 熟悉常用的Linux 操作和Hadoop 操作

Hadoop 运行在Linux 系统上,因此需要学习实践一些常用的Linux 命令。本实验旨在熟悉常用的Linux 操作和Hadoop 操作,为顺利开展后续其它实验奠定基础。

cd 命令:切换目录

  • 切换到目录/usr/local
    在这里插入图片描述
  • 切换到当前目录的上一级目录
    在这里插入图片描述
  • 切换到当前登录Linux 系统的用户自己的主文件夹
    在这里插入图片描述

ls 命令:查看文件与目录

查看目录/usr 下的所有文件和目录
在这里插入图片描述

mkdir 命令:新建目录

  • 进入/tmp 目录,创建一个名为a 的目录,并查看/tmp 目录下已经存在哪些目录。
    在这里插入图片描述
  • 进入/tmp 目录,创建目录a1/a2/a3/a4。
    在这里插入图片描述
    -p 确保目录名称存在,不存在的就建一个。

rmdir 命令:删除空的目录

  • 将上面创建的目录a(在/tmp 目录下面)删除。
    在这里插入图片描述
  • 删除上面创建的目录a1/a2/a3/a4(在/tmp 目录下面),然后查看/tmp 目录下面存在哪些目录。
    在这里插入图片描述
    -p 递归删除目录dirname,当子目录删除后其父目录为空时,也一同被删除。

cp 命令:复制文件或目录

  • 将当前用户的主文件夹下的文件.bashrc 复制到目录“/usr”下,并重命名为bashrc1
    在这里插入图片描述
  • 在目录“/tmp”下新建目录test,再把这个目录复制到“/usr”目录下
    在这里插入图片描述

mv 命令:移动文件与目录,或更名

  • 将“/usr”目录下的文件bashrc1 移动到“/usr/test”目录下
    在这里插入图片描述
  • 将“/usr”目录下的test 目录重命名为test2
    在这里插入图片描述

rm 命令:移除文件或目录

  • 将“/usr/test2”目录下的bashrc1 文件删除
    在这里插入图片描述
  • 将“/usr”目录下的test2 目录删除
    在这里插入图片描述

cat 命令:查看文件内容

查看当前用户主文件夹下的.bashrc 文件内容
在这里插入图片描述

tac 命令:反向查看文件内容

反向查看当前用户主文件夹下的.bashrc 文件的内容
在这里插入图片描述

more 命令:一页一页翻动查看

翻页查看当前用户主文件夹下的.bashrc 文件的内容
在这里插入图片描述在这里插入图片描述

head 命令:取出前面几行

  • 查看当前用户主文件夹下.bashrc 文件内容前20 行
    在这里插入图片描述
    或者
    在这里插入图片描述
  • 查看当前用户主文件夹下.bashrc 文件内容,后面50 行不显示,只显示前面几行
    在这里插入图片描述

tail 命令:取出后面几行

  • 查看当前用户主文件夹下.bashrc 文件内容最后20行
    在这里插入图片描述
  • 查看当前用户主文件夹下.bashrc 文件内容,并且只列出50 行以后的数据
    在这里插入图片描述
    或者
    在这里插入图片描述

touch 命令:修改文件时间或创建新文件

  • 在“/tmp”目录下创建一个空文件hello,并查看文件时间
    在这里插入图片描述
  • 修改hello 文件,将文件时间整为5天前
    在这里插入图片描述
    或者
    在这里插入图片描述

chown 命令:修改文件所有者权限

将hello 文件所有者改为root 帐号,并查看属性
在这里插入图片描述

find 命令:文件查找

找出主文件夹下文件名为.bashrc 的文件
在这里插入图片描述

tar 命令:压缩命令

  • 在根目录“/”下新建文件夹test,然后在根目录“/”下打包成test.tar.gz
    在这里插入图片描述
  • 把上面的test.tar.gz 压缩包,解压缩到“/tmp”目录
    在这里插入图片描述

grep 命令:查找字符串

从“~/.bashrc”文件中查找字符串’examples’
在这里插入图片描述

使用hadoop 用户登录Linux 系统, 启动Hadoop ( Hadoop 的安装目录为“/usr/local/hadoop”),为hadoop 用户在HDFS 中创建用户目录“/user/hadoop”

在这里插入图片描述

接着在HDFS 的目录“/user/hadoop”下,创建test 文件夹,并查看文件列表

在这里插入图片描述

将Linux 系统本地的“~/.bashrc”文件上传到HDFS 的test 文件夹中,并查看test

在这里插入图片描述

将HDFS 文件夹test 复制到Linux 系统本地文件系统的“/usr/local/hadoop”目录下

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

第二部分 熟悉常用的HDFS 操作

  • 理解HDFS 在Hadoop 体系结构中的角色。
  • 熟练使用HDFS 操作常用的shell 命令。
  • 熟悉HDFS 操作常用的Java API。

编程实现以下功能,并利用Hadoop 提供的Shell 命令完成相同任务:

向HDFS 中上传任意文本文件,如果指定的文件在HDFS 中已经存在,则由用户来指定是追加到原有文件末尾还是覆盖原有的文件;

  • shell:
hadoop fs -put text1.txt ./test
hadoop fs -appendToFile text2.txt ./test/text1.txt
hadoop fs -copyFromLocal -f text2.txt ./test/text1.txt

上传文本文件
在这里插入图片描述
追加到原有文件末尾
在这里插入图片描述
覆盖原有的文件,-f 表示强制
在这里插入图片描述

  • java:
public static void test1(FileSystem hdfs, Path srcPath, Path desPath){
    try {
        if (hdfs.exists(desPath)){
        	System.out.println(desPath + " exists.");
            System.out.println("Do you want to overwrite(y) or append(n) the existed file? (y/n)");
            if (new Scanner(System.in).next().equals("y")){
            	hdfs.copyFromLocalFile(false, true, srcPath, desPath);
            } else {
                FileInputStream inputStream = new FileInputStream(srcPath.toString());
                FSDataOutputStream out  = hdfs.append(desPath);
                byte[] bytes = new byte[1024];
                int read = -1;
                while ((read = inputStream.read(bytes)) > 0){
                	out.write(bytes,0,read);
                }
                inputStream.close();
                out.close();
            }
        } else {
        	hdfs.copyFromLocalFile(srcPath, desPath);
        	System.out.println("Copy successfully.");
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

org.apache.hadoop.fs.ChecksumException: Checksum error解决方法:删除本地存在的crc文件
在这里插入图片描述

上传任意文本文件(WARN暂时忽略)
在这里插入图片描述
追加到文件末尾
在这里插入图片描述
覆盖原有文件
在这里插入图片描述

从HDFS 中下载指定文件,如果本地文件与要下载的文件名称相同,则自动对下载的文件重命名;

  • shell:
hadoop fs -copyToLocal /test/text1.txt text4.txt

在这里插入图片描述

  • java:
public static void test2(FileSystem hdfs, Path srcPath, FileSystem local, Path desPath) {
	try {
		if (hdfs.exists(srcPath) && !local.exists(desPath)) {
			hdfs.copyToLocalFile(srcPath, desPath);
		} else if (hdfs.exists(srcPath)) {
			System.out.println(desPath.toString() + " exists.");
			String[] file = desPath.toString().split("\\.");
			System.out.println("copy to " + file[0] + "_copy.txt");
			hdfs.copyToLocalFile(srcPath, new Path(file[0] + "_copy.txt"));
		} else {
			System.out.println(srcPath + " not exists.");
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
}

在这里插入图片描述

将HDFS 中指定文件的内容输出到终端中;

  • shell:
hadoop fs -cat /test/text1.txt

在这里插入图片描述

  • java:
public static void test3(FileSystem hdfs, Path srcPath) {
	try {
		if (!hdfs.exists(srcPath)) {
			System.out.println(srcPath + " not exists.");
		} else {
			FSDataInputStream in = hdfs.open(srcPath);
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in));
			String line;
			while ((line = bufferedReader.readLine()) != null) {
				System.out.println(line);
			}
		}
	} catch (IOException e) {
		e.printStackTrace();
	}
}

在这里插入图片描述

显示HDFS 中指定的文件的读写权限、大小、创建时间、路径等信息;

  • shell:
hadoop fs -ls /test/text1.txt
hadoop fs -ls -h /test/text1.txt

在这里插入图片描述

  • java:
public static void test4(FileSystem hdfs, Path srcPath) {
	try {
		FileStatus[] fileStatus = hdfs.listStatus(srcPath);
		for (FileStatus status : fileStatus) {
			System.out.println("permission: " + status.getPermission().toString());
			System.out.println("      size: " + status.getLen());
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			System.out.println("      time: " + format.format(status.getModificationTime()));
			System.out.println("      path: " + status.getPath());
		}
	} catch (FileNotFoundException e) {
		System.out.println(srcPath + " not exists.");
	} catch (IOException e) {
		e.printStackTrace();
	}
}

在这里插入图片描述

给定HDFS 中某一个目录,递归输出该目录下的所有文件的读写权限、大小、创建时间、路径等信息;

  • shell:
hadoop fs -ls -R /

在这里插入图片描述

  • java:
public static void test5(FileSystem hdfs, Path srcPath) {
	RemoteIterator<LocatedFileStatus> iterator;
	try {
		iterator = hdfs.listFiles(srcPath, true);
		while (iterator.hasNext()) {
			FileStatus status = iterator.next();
			System.out.println("permission: " + status.getPermission().toString());
			System.out.println("      size: " + status.getLen());
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			System.out.println("      time: " + format.format(status.getModificationTime()));
			System.out.println("      path: " + status.getPath());
		}
	} catch (FileNotFoundException e) {
		System.out.println(srcPath + " not exists.");
	} catch (IOException e) {
		e.printStackTrace();
	}
}

在这里插入图片描述

提供一个HDFS 内的文件的路径,对该文件进行创建和删除操作。如果文件所在目录不存在,则自动创建目录;

  • shell:
hadoop fs -touchz /test/text2/text2.txt
hadoop fs -mkdir /test/text2
hadoop fs -rm /test/text2/text2.txt

在这里插入图片描述

  • java:
public static void test6(FileSystem hdfs, Path dirPath, Path filePath) {
	filePath = new Path(dirPath.toString() + Path.SEPARATOR + filePath.toString());
	try {
		System.out.println("Do you want to create(y) or delete(n) the file? (y/n)");
		if (new Scanner(System.in).next().equals("y")){
			if (!hdfs.exists(dirPath)) {
				System.out.println(dirPath + " not exists.");
				hdfs.mkdirs(dirPath);
				System.out.println("mkdir " + dirPath + ", now it exists.");
			}
	        hdfs.create(filePath);
	        System.out.println("created " + filePath);
		} else {
			if (!hdfs.exists(filePath)) {
				System.out.println(filePath + " not exists.");
			} else {
				hdfs.delete(filePath, true);
			}
	    }
	} catch (IOException e) {
		e.printStackTrace();
	}
}

在这里插入图片描述

提供一个HDFS 的目录的路径,对该目录进行创建和删除操作。创建目录时,如果目录文件所在目录不存在,则自动创建相应目录;删除目录时,当该目录为空时删除,当该目录不为空时不删除该目录;

  • shell:
hadoop fs -mkdir /test/text2
hadoop fs -rmdir /test/text2

在这里插入图片描述

  • java:
public static void test7(FileSystem hdfs, Path dirPath) {
	try {
		System.out.println("Do you want to create(y) or delete(n) the directory? (y/n)");
		if (new Scanner(System.in).next().equals("y")){
			if (hdfs.exists(dirPath)) {
				System.out.println(dirPath + " has existed.");
			} else {
				System.out.println(dirPath + " not exists. now create it.");
				hdfs.mkdirs(dirPath);
				System.out.println("created " + dirPath);
			}
		} else {
			if (!hdfs.exists(dirPath)) {
				System.out.println(dirPath + " not exists.");
			} else {
				FileStatus[] fileStatus = hdfs.listStatus(dirPath);
				if (fileStatus.length == 0) {
					hdfs.delete(dirPath, true);
					System.out.println(dirPath + " is empty and deleted");
				} else {
					System.out.println(dirPath + " is not empty and not deleted");
					for (FileStatus status : fileStatus) {
						System.out.println(status.getPath());
					}
				}
			}
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}

在这里插入图片描述

向HDFS 中指定的文件追加内容,由用户指定内容追加到原有文件的开头或结尾;

  • shell:
    追加到结尾
hadoop fs -appendToFile text1.txt /test/text1.txt

在这里插入图片描述
追加到开头(用tmp.txt做跳板,这种思路仅供参考)

hadoop fs -get /test/text1.txt tmp.txt

在这里插入图片描述

  • java:
public static void test8(FileSystem hdfs, Path srcPath, FileSystem local, Path desPath, Path tmpPath) {
	try {
		if (!hdfs.exists(desPath) || !local.exists(srcPath)) {
			System.out.println(srcPath + " or " + desPath + " not exists.");
		} else {
			System.out.println("Do you want to add content at the start(y) or end(n) of the file? (y/n)");
			if (new Scanner(System.in).next().equals("y")){
				hdfs.moveToLocalFile(desPath, tmpPath);
				FSDataOutputStream out = hdfs.create(desPath);
	            byte[] bytes = new byte[1024];
	            int read = -1;
	            FileInputStream srcin = new FileInputStream(srcPath.toString());
	            while ((read = srcin.read(bytes)) > 0){
                    out.write(bytes,0,read);
	            }
	            srcin.close();
	            FileInputStream tmpin = new FileInputStream(tmpPath.toString());
	            while ((read = tmpin.read(bytes)) > 0){
                    out.write(bytes,0,read);
	            }
	            tmpin.close();
	            local.delete(tmpPath, true);
	            out.close();
			} else {
				FileInputStream in = new FileInputStream(srcPath.toString());
	            FSDataOutputStream out  = hdfs.append(desPath);
	            byte[] bytes = new byte[1024];
	            int read = -1;
	            while ((read = in.read(bytes)) > 0){
                    out.write(bytes,0,read);
	            }
	            in.close();
	            out.close();
			}
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}

在这里插入图片描述

删除HDFS 中指定的文件;

  • shell:
hadoop fs -rm /test/text1.txt
  • java:
public static void test9(FileSystem hdfs, Path filePath) {
	try {
		if (!hdfs.exists(filePath)) {
			System.out.println(filePath + " not exists.");
		} else {
			hdfs.delete(filePath, true);
			System.out.println("deleted " + filePath);
		}
	} catch (Exception e) {
		System.out.println("failed to delete " + filePath);
	}
}

在这里插入图片描述

在HDFS 中,将文件从源路径移动到目的路径。

  • shell:
hadoop fs -mv /test/text1.txt /test/text2.txt

在这里插入图片描述

  • java:
public static void test10(FileSystem hdfs, Path srcPath, Path desPath) {
	try {
		if (!hdfs.exists(srcPath)) {
			System.out.println(srcPath + " not exists.");
		} else {
			hdfs.rename(srcPath, desPath);
			System.out.println("moved " + srcPath + " to " + desPath);
		}
	} catch (Exception e) {
		System.out.println("failed to move.");
	}
}

在这里插入图片描述

第三部分 熟悉常用的HBase 操作

  • 理解HBase 在Hadoop 体系结构中的角色。
  • 熟练使用HBase 操作常用的shell 命令。
  • 熟悉HBase 操作常用的Java API。

编程实现以下指定功能,并用Hadoop 提供的HBase Shell 命令完成相同任务:

列出HBase所有的表的相关信息,例如表名

  • shell
list
  • java

在终端打印出指定的表的所有记录数据

  • shell
scan 'Student'
  • java

向已经创建好的表添加和删除指定的列族或列;

  • shell
put 'Student','s001','S_No','2015001'
delete 'Student','s001','S_No'
  • java

清空指定的表的所有记录数据;

  • shell
truncate 'Student'
  • java

统计表的行数。

  • shell
count 'Student'
  • java

HBase 数据库操作

现有以下关系型数据库中的表和数据,要求将其转换为适合于HBase 存储的表并插入数据:
在这里插入图片描述

  • Student表

创建表

create 'Studet','S_No','S_Name','S_Sex','S_Age'

插入数据

# 第1行数据
put 'Student','s001','S_No','2015001'
put 'Student','s001','S_Name','Zhangsan'
put 'Student','s001','S_Sex','male'
put 'Student','s001','S_Age','23'
# 第2行数据
put 'Student','s002','S_No','2015002'
put 'Student','s002','S_Name','Mary'
put 'Student','s002','S_Sex','female'
put 'Student','s002','S_Age','22'
# 第3行数据
put 'Student','s003','S_No','2015003'
put 'Student','s003','S_Name','Lisi'
put 'Student','s003','S_Sex','male'
put 'Student','s003','S_Age','24'
  • Course表

创建表

create 'Course','C_No','C_Name','C_Credit'

插入数据

put 'Course','c001','C_No','123001'
put 'Course','c001','C_Name','Math'
put 'Course','c001','C_Credit','2.0'
put 'Course','c002','C_No','123002'
put 'Course','c002','C_Name','Computer'
put 'Course','c002','C_Credit','5.0'
put 'Course','c003','C_No','123003'
put 'Course','c003','C_Name','English'
put 'Course','c003','C_Credit','3.0'
  • SC表

创建表

create 'SC','SC_Sno','SC_Cno','SC_Score'

插入数据

put 'SC','sc001','SC_Sno','2015001'
put 'SC','sc001','SC_Cno','123001'
put 'SC','sc001','SC_Score','86'
put 'SC','sc002','SC_Sno','2015001'
put 'SC','sc002','SC_Cno','123003'
put 'SC','sc002','SC_Score','69'
put 'SC','sc003','SC_Sno','2015002'
put 'SC','sc003','SC_Cno','123002'
put 'SC','sc003','SC_Score','77'
put 'SC','sc004','SC_Sno','2015002'
put 'SC','sc004','SC_Cno','123003'
put 'SC','sc004','SC_Score','99'
put 'SC','sc005','SC_Sno','2015003'
put 'SC','sc005','SC_Cno','123001'
put 'SC','sc005','SC_Score','98'
put 'SC','sc006','SC_Sno','2015003'
put 'SC','sc006','SC_Cno','123002'
put 'SC','sc006','SC_Score','95'

请编程实现以下功能:

createTable(String tableName, String[] fields)

创建表,参数tableName 为表的名称,字符串数组fields 为存储记录各个字段名称的数组。要求当HBase 已经存在名为tableName 的表的时候,先删除原有的表,然后再创建新的表。

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 存储这三门课的成绩。

scanColumn(String tableName, String column)

浏览表tableName 某一列的数据,如果某一行记录中该列数据不存在,则返回null。要求当参数column 为某一列族名称时,如果底下有若干个列限定符,则要列出每个列限定符代表的列的数据;当参数column 为某一列具体名称(例如“Score:Math”)时,只需要列出该列的数据。

modifyData(String tableName, String row, String column)

修改表tableName,行row(可以用学生姓名S_Name 表示),列column 指定的单元格的数据。

deleteRow(String tableName, String row)

删除表tableName 中row 指定的行的记录。

第四部分 NoSQL 和关系数据库的比较

  • 理解4 种数据库(MySQL、HBase、Redis 和MongDB)的概念及不同点。
  • 熟练使用4 种数据库操作常用的shell 命令。
  • 熟悉4 种数据库操作常用的Java API。

第五部分 MapReduce 初级编程

  • 通过实验掌握基本的MapReduce 编程方法。
  • 掌握用MapReduce 解决一些常见的数据处理问题,包括数据去重、数据排序和数据挖掘等。
  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

容嬷嬷当年一枝花

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值