Hadoop(05) HBase2

命令如下:

cd /usr/local/
sudo chown -R hadoop ./hbase-2.5.5/

2.3 配置环境变量
将hbase-2.5.5下的bin目录添加到path中,这样,启动hbase就无需到/usr/local/hbase目录下,大大的方便了hbase的使用。教程下面的部分还是切换到了/usr/local/hbase-2.5.5目录操作,有助于初学者理解运行过程,熟练之后可以不必切换。
编辑~/.bashrc文件

cd ~
vim ~/.bashrc

如果没有引入过PATH请在~/.bashrc文件尾行添加如下内容:

export HBASE_HOME=/usr/local/hbase-2.5.5
export PATH=$PATH:$HBASE_HOME/bin

由于在之前的教程中安装过Java和Hadoop,并且配置过PATH,因此只需要在原本的export PATH的基础上添加 $HBASE_HOME/bin:,这里的“:”是分隔符。如下图:

 添加之后的如下图所示:

保存并退出

2.3 执行source命令使上述配置在当前终端立即生效

命令如下:

source ~/.bashrc

2.4 查看Hbase 的版本

命令如下:

hbase version

此处出现一条警告信息(SLF4J: Class path contains multiple SLF4J bindings.),可以点击此处查看解决办法

看到输出版本消息表示HBase已经安装成功,接下来将分别进行HBase单机模式和伪分布式模式的配置。

三、HBase配置

HBase作为一个分布式的开源NoSQL数据库,它有三种运行模式,分别是单机模式(Standalone Mode)、伪分布式模式(Pseudo-Distributed Mode)和完全分布式模式(Fully-Distributed Mode)。

  1. 单机模式(Standalone Mode):
    在单机模式下,HBase运行在单个节点上,所有的HBase组件(包括HMaster和HRegionServer)都在同一个JVM进程中运行。这种模式适用于在开发和测试环境中快速搭建和运行HBase,但不具备分布式的优势和可扩展性。
  2. 伪分布式模式(Pseudo-Distributed Mode):
    在伪分布式模式下,HBase模拟了分布式环境,但实际上仍然在单个节点上运行。每个HBase组件都在独立的JVM进程中运行,包括一个HMaster和一个或多个HRegionServer。这种模式适用于在本地机器上模拟和测试分布式的HBase环境,通常用于开发和学习目的。
  3. 完全分布式模式(Fully-Distributed Mode):
    在完全分布式模式下,HBase运行在一个真正的分布式环境中,通过多台物理或虚拟机器组成。HBase集群包括一个HMaster节点和多个HRegionServer节点,数据被分布存储在不同的RegionServer上。这种模式适用于生产环境,可以提供高可用性、容错性和水平扩展性,并支持大规模数据存储和处理。

选择适当的模式取决于你的使用场景和需求。在开发和测试阶段,可以使用单机模式或伪分布式模式进行快速验证和开发。在实际生产环境中,通常会使用完全分布式模式来构建可靠的、高性能的HBase集群。

作为学习,我们将重点讨论单机模式和伪分布式模式。
以下先决条件很重要,比如没有配置JAVA_HOME环境变量,就会报错。

  • jdk
  • Hadoop( 单机模式不需要,伪分布式模式和分布式模式需要)
  • SSH

以上三者如果没有安装,请回到Hadoop(01) Hadoop3.3.6安装教程,单机/伪分布式配置 Hadoop3.3.6安装教程,单机/伪分布式配置 ")参考如何安装。

3.1 单机模式

3.1.1 配置/usr/local/hbase-2.5.5/conf/hbase-env.sh

配置JAVA环境变量,并添加配置HBASE_MANAGES_ZK为true。如果此前配置过JAVA_HOME可以输入下列命令显示其路径。

echo $JAVA_HOME

配置HBASE_MANAGES_ZK为true,表示由hbase自己管理zookeeper,不需要单独的zookeeper。hbase-env.sh中本来就存在该变量的配置,只需要删除前面的#并修改配置内容即可(#代表注释),用vi命令打开并编辑hbase-env.sh,命令如下:

保存后退出。

3.1.2 配置/usr/local/hbase-2.5.5/conf/hbase-site.xml

打开并编辑hbase-site.xml,命令如下:

vim /usr/local/hbase-2.5.5/conf/hbase-site.xml

在启动HBase前需要设置属性hbase.rootdir,用于指定HBase数据的存储位置,因为如果不设置的话,hbase.rootdir默认为/tmp/hbase-${user.name},这意味着每次重启系统都会丢失数据。此处设置为HBase安装目录下的hbase-tmp文件夹即(/usr/local/hbase-2.5.5/hbase-tmp),添加配置如下:

<configuration>
        <property>
                <name>hbase.rootdir</name>
                <value>file:///usr/local/hbase-2.5.5/hbase-tmp</value>
        </property>
</configuration>

修改前:

修改后:

3.1.3 测试运行

首先切换目录至HBase安装目录/usr/local/hbase;再启动HBase。命令如下:

cd /usr/local/hbase-2.5.5/bin/
./start-hbase.sh  # 启动HBase
./hbase shell   # 打开shell命令行模式,用户可以通过输入shell命令操作HBase数据库

使用exit可以退出shell命令行模式。

停止HBase运行,命令如下:

注意:如果在操作HBase的过程中发生错误,可以通过{HBASE_HOME}目录(/usr/local/hbase-2.5.5)下的logs子目录中的日志文件查看错误原因。

3.2 伪分布式模式

3.2.1 配置/usr/local/hbase-2.5.5/conf/hbase-env.sh

配置JAVA_HOME,HBASE_CLASSPATH,HBASE_MANAGES_ZK。HBASE_CLASSPATH设置为本机HBase安装目录下的conf目录(即/usr/local/hbase-2.5.5/conf)命令如下:

cd /usr/local/hbase-2.5.5/conf/
vim hbase-env.sh 

在hbase-env.sh中添加下列命令:

 export HBASE_MANAGES_ZK=true
 export JAVA_HOME=/usr/local/java/jdk-11.0.20.1+1
 export HBASE_CLASSPATH=/usr/local/hbase-2.5.5/conf

3.2.2 配置/usr/local/hbase-2.5.5/conf/hbase-site.xml

用命令vi打开并编辑hbase-site.xml,命令如下:

/usr/local/hbase-2.5.5/conf/hbase-site.xml

修改hbase.rootdir,指定HBase数据在HDFS上的存储路径;将属性hbase.cluter.distributed设置为true。假设当前Hadoop集群运行在伪分布式模式下,在本机上运行,且NameNode运行在9000端口。

<configuration>
        <property>
                <name>hbase.rootdir</name>
                <!-- value 的 URL 地址请与hadoop配置文件core-site.xml
                 中的 fs.default.name 保持一致,然后再加上 /hbase。
                 此处建议用主机名(不建议直接填写 IP 地址,否则可能出错)-->
                <value>hdfs://hadoop01:9000/hbase</value>
        </property>
        <property>
                <name>hbase.cluster.distributed</name>
                <value>true</value>
        </property>
        <property>
        <name>hbase.unsafe.stream.capability.enforce</name>
        <value>false</value>
    </property>
</configuration>

修改前:

修改后:

hbase.rootdir指定HBase的存储目录;

hbase.cluster.distributed设置集群处于分布式模式.
另外,上面配置文件中,hbase.unsafe.stream.capability.enforce这个属性的设置,是为了避免出现启动错误。也就是说,如果没有设置hbase.unsafe.stream.capability.enforce为false,那么,在启动HBase以后,会出现无法找到HMaster进程的错误,启动后查看系统启动日志(/usr/local/hbase-2.5.5/logs/hbase-hadoop-master-ubuntu.log),会发现如下错误:

2023-10-23 11:05:53,916 ERROR [master/localhost:16000:becomeActiveMaster] master.HMaster: Failed to become active master
java.lang.IllegalStateException: The procedure WAL relies on the ability to hsync for proper operation during component failures, but the underlying filesystem does not support doing so. Please check the config value of ‘hbase.procedure.store.wal.use.hsync’ to set the desired level of robustness and ensure the config value of ‘hbase.wal.dir’ points to a FileSystem mount that can provide it.

3.3.3 测试运行HBase

① 登陆ssh,由于之前设置了无密码登陆,因此这里不需要密码;再切换目录至/usr/local/hadoop-3.3.6 ;再启动hadoop,如果已经启动hadoop请跳过此步骤。命令如下:

一般来说,输入命令jps,如果能看到NameNode,DataNode和SecondaryNameNode都已经成功启动,表示Hadoop启动成功。但是由于我们在此前的 Hadoop(01) Hadoop3.3.6安装教程,单机/伪分布式配置 Hadoop3.3.6安装教程,单机/伪分布式配置") 教程中的 伪分布式配置的小节中 仅将hadoop01作为namenode来进行的配置,所以在hadoop01主机上运行 jps 命令并不会显示datanode,因为datanode是配置在hadoop02 主机上。如果想要查看datanode是否正常启动,可以hadoop02主机上输入 jps,如下所示:

② 切换目录至/usr/local/hbase-2.5.5 ;再启动HBase.命令如下:

cd /usr/local/hbase-2.5.5/bin/
./start-hbase.sh 
jps

输入jps后,看到一下界面表示启动hbase成功。

以上的提示信息表示:

  1. 首先,看到一个关于主机身份验证的提示,要求确认连接。这是SSH连接到本地主机(127.0.0.1)时的一般行为。可以输入yes以继续连接。
  2. 接下来,输出显示成功连接到主机(127.0.0.1),并开始了一系列操作。
running zookeeper启动了ZooKeeper服务,它是HBase的依赖组件之一。ZooKeeper用于协调和管理HBase集群中的各个节点。
running master启动了HBase的Master节点,Master节点负责管理和协调整个HBase集群的操作。
running regionserver启动了HBase的RegionServer节点,RegionServer节点负责存储和处理HBase表中的数据。
  1. 启动过程中的日志输出被重定向到了相应的日志文件中,例如hbase-hadoop-zookeeper-hadoop01.outhbase-hadoop-master-hadoop01.outhbase-hadoop-regionserver-hadoop01.out。你可以查看这些日志文件以获取更多关于启动过程的详细信息。

进入hbase的shell界面,命令如下:

3.3.4 停止HBase运行

启动关闭Hadoop和HBase的顺序一定是:
启动Hadoop—>启动HBase—>关闭HBase—>关闭Hadoop

./stop-hbase.sh 
cd /usr/local/hadoop-3.3.6/sbin/
./stop-dfs.sh 

注意:如果在操作HBase的过程中发生错误,可以通过{HBASE_HOME}目录(/usr/local/hbase)下的logs子目录中的日志文件查看错误原因。

四、编程实践

4.1 Hbase 的shell命令

在使用hbase shell命令之前,要先启动hadoop和hbase,启动命令如上述。

4.1.1 创建表

hbase中用create命令创建表,具体如下:

create 'student','Sname','Ssex','Sage','Sdept','course'

参数说明:创建一个名为student的表,以及定义一些列族(SnameSsexSageSdeptcourse

因为HBase的表中会有一个系统默认的属性作为行键,无需自行创建,默认为put命令操作中表名后第一个数据。创建完“student”表后,可通过describe命令查看“student”表的基本信息。命令执行截图如下:

describe 'student'

4.1.2 hbase数据库的基本操作

本小节主要介绍HBase的增、删、改、查操作。在添加数据时,HBase会自动为添加的数据添加一个时间戳,故在需要修改数据时,只需直接添加数据,HBase即会生成一个新的版本,从而完成“改”操作,旧的版本依旧保留,系统会定时回收垃圾数据,只留下最新的几个版本,保存的版本数可以在创建表的时候指定。

① 添加数据

hbase中用put命令添加数据,注意:一次只能为一个表的一行数据的一个列,也就是一个单元格添加一个数据,所以直接用shell命令插入数据效率很低,在实际应用中,一般都是利用编程操作数据。
当运行命令:put ‘student’,’95001’,’Sname’,’LiYing’时,即为student表添加了学号为95001,名字为LiYing的一行数据,其行键为95001。

put 'student','95001','Sname','LiYing'

参数说明: 在student表的**行键为95001**的行中,插入一个名为Sname的列,并设置其值为LiYing。此处的行键可以代指学生的学号,也可表示为student表添加了学号为95001,名字为LiYing的一行数据,其行键为95001。

补充:在HBase中,行键是用于唯一标识表中每一行数据的标识符。行键是一个字节数组,可以是任意长度的二进制数据。在HBase表中,行键是按照字典顺序进行排序的。

put 'student','95001','course:math','80'

参数说明:在student表的行键为95001的行中,在 course 列族下存储一个名为math的列,并设置其属性值为80

put 'student','95001','course:English','98'

参数说明:在 student 表的行键为95001的行中,在 course 列族下存储一个名为 English 的列,并将其属性值设置为 98

put 'student','95001','Ssex','Male'

参数说明:在student表的行键为95001的行中,插入一个名为Ssex的列,并设置其值为Male

② 查看数据

HBase中有两个用于查看数据的命令:1. get命令,用于查看表的某一行数据;2. scan命令用于查看某个表的全部数据

get命令:

get 'student','95001'

scan命令:

scan 'student'

③ 删除数据

在HBase中用delete以及deleteall命令进行删除数据操作,它们的区别是:1. delete用于删除一个数据,是put的反向操作;2. deleteall操作用于删除一行数据。

delete命令:

delete 'student','95001','course:English'

删除之前:

删除之后:

deleteall命令:

deleteall 'student','95001'

删除之前:

删除之后:

④ 删除表

删除表有两步,第一步先让该表不可用,第二步删除表。

disable 'student'  
drop 'student'

⑤ 查询表的历史数据

在 Hbase 中,表的历史数据通常是通过版本控制来实现的。每次对表执行写操作时,HBase 会为每个单元格(cell)维护多个版本的数据。可以通过设置适当的版本数来控制保存的历史数据量。

注意:保存的历史数据数量可能会影响存储空间的使用和查询性能。因此,应根据实际需求和系统资源进行适当的配置。

step1

在创建表的时候,指定保存的版本数(假设指定为5)

create 'teacher',{NAME=>'username',VERSIONS=>5}

step2

插入数据然后更新数据,使其产生历史版本数据,注意:这里插入数据和更新数据都是用put命令

put 'teacher','91001','username','Mary'
put 'teacher','91001','username','Mary1'
put 'teacher','91001','username','Mary2'
put 'teacher','91001','username','Mary3'
put 'teacher','91001','username','Mary4'  
put 'teacher','91001','username','Mary5'
step3

查询时,指定查询的历史版本数。默认会查询出最新的数据。(有效取值为1到5)

要查询表的历史数据,可以使用 get 命令并指定列族和列限定符,并在命令中添加 VERSIONS 关键字,后接要返回的版本数。例如:(有效取值为1到5)

get 'teacher','91001',{COLUMN=>'username',VERSIONS=>5}

参数说明:返回 teacher 表中 91001 行的 username 列的最近 5 个版本的数据。

也可以使用 scan 命令来扫描整个表或特定的行范围,并通过设置 VERSIONS 选项来获取多个版本的数据。例如:

scan 'student', {VERSIONS => 3}

参数说明: 扫描整个 student 表,并返回每个单元格的最近 3 个版本的数据。

⑥ 退出Hbase数据库表操作

最后退出数据库操作,输入exit命令即可退出,注意:这里退出HBase数据库是退出对数据库表的操作,而不是停止启动HBase数据库后台运行。

exit

4.2 Hbase Java API编程实践

4.2.1 在Linux中安装IDEA开发工具

本实例采用IDEA开发工具,虚拟机中如何安装IDEA 开发工具,参考教程 Linux(04) Debian11中安装IDEA教程(无GNOME) Debian11中安装IDEA教程(无GNOME)")。

通过将IDEA安装到 /opt/idea-IC-23.2/bin 路径之后,可以通过以下命令启动IDEA

cd /opt/idea-IC-23.2/bin
./idea.sh

 启动之后,新建一个名为HBase_Example的java工程。

4.2.2 为项目添加需要用到的JAR包

为了编写一个能够与HBase交互的Java应用程序,需要在这个界面中加载该Java工程所需要用到的JAR包,这些JAR包中包含了可以访问HBase的Java API。这些JAR包都位于Linux系统的HBase安装目录的lib目录下,也就是位于“/usr/local/hbase-2.5.5/lib”目录下。

在 HBase 安装目录下的 /usr/local/hbase-2.5.5/lib 文件夹通常包含 HBase 的相关库文件和依赖项。这些文件是 HBase 运行所需的核心组件和第三方库。除了上述提到的主要内容外,还有另外六个目录:

  1. client-facing-thirdparty:这个目录通常包含 HBase 与客户端交互时所需的第三方库文件。这些库文件用于支持与 HBase 进行交互的客户端应用程序,例如 Java 应用程序或其他编程语言的客户端。
  2. jdk11:这个目录可能包含适用于 JDK 11 的特定版本的 HBase 库文件。由于不同的 JDK 版本可能存在兼容性差异,HBase 可能提供特定于某个 JDK 版本的库文件。
  3. ruby:这个目录可能包含与 Ruby 编程语言相关的库文件。HBase 提供了一些用于与 Ruby 客户端进行交互的接口和工具。
  4. shaded-clients:这个目录通常包含 HBase 为不同的客户端环境提供的 shaded(阴影)客户端库文件。这些库文件将 HBase 的依赖项打包在一起,以便客户端应用程序可以更轻松地使用 HBase。
  5. trace:这个目录可能包含与 HBase 分布式跟踪功能相关的库文件。HBase 的分布式跟踪功能用于跟踪和分析请求在集群中的执行情况。
  6. zkcli:这个目录可能包含与 HBase ZooKeeper 客户端相关的库文件和工具。ZooKeeper 用于 HBase 的协调和配置管理,zkcli 目录可能包含与 ZooKeeper 客户端交互的命令行工具和库文件。

本次的编程实践中我们仅引入 核心库 和 client-facing-thirdparty ,其余几个包均不涉及,如有需要可以按照下面的方法建立依赖。

① 创建libs文件夹

在该java工程中创建libs文件,如果已经存在libs文件夹,则可以直接进行下一步。

② 复制JAR文件

将需要导入的jar**(在 hbase的安装目录下lib文件中,包括核心库的jar包和** **client-facing-thirdparty****)**复制粘贴到libs文件夹中。

复制到HBase_Example项目中的libs文件夹中

查看一下刚复制到libs文件夹中的状态,暂时不要点开

③ 建立该java工程对libs文件夹的依赖

在IDEA中右键单击项目,选择’Open Module Settings”(或者"Project Structure"”),在弹出的窗口中选择’Modules”,然后选择“Dependencies”选项卡。
点击“+"按钮,选择"JARs or directories”,然后在弹出的窗口中选择刚才放置jar包的libs文件夹,然后点击OK(我在此处直接选择的是文件夹)。

如果是这个状态表示依赖建立成功:

这样,导入jar包的操作就完成了。现在你可以直接编写或运行代码了。如果需要进一步优化导包设置,可以根据IDEA的环境配置和插件进行相应的调整(如Maven)。

4.2.3 新建java代码程序

ExampleForHBase.java代码内容具体如下:

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 ExampleForHBase {
    public static Configuration configuration;
    public static Connection connection;
    public static Admin admin;
    public static void main(String[] args)throws IOException{
        init();
        createTable("student",new String[]{"score"});
        insertData("student","zhangsan","score","English","69");
        insertData("student","zhangsan","score","Math","86");
        insertData("student","zhangsan","score","Computer","77");
        getData("student", "zhangsan", "score","English");
        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 (IOException e){
            e.printStackTrace();
        }
    }
 
    public static void close(){
        try{
            if(admin != null){
                admin.close();
            }
            if(null != connection){
                connection.close();
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
 
    public static void createTable(String myTableName,String[] colFamily) throws IOException {
        TableName tableName = TableName.valueOf(myTableName);
        if(admin.tableExists(tableName)){
            System.out.println("talbe is exists!");
        }else {
            TableDescriptorBuilder tableDescriptor = TableDescriptorBuilder.newBuilder(tableName);
            for(String str:colFamily){
                ColumnFamilyDescriptor family = 
ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(str)).build();
                tableDescriptor.setColumnFamily(family);
            }
            admin.createTable(tableDescriptor.build());
        } 
    }
 
    public static void insertData(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 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);
        System.out.println(new String(result.getValue(colFamily.getBytes(),col==null?null:col.getBytes())));
        table.close(); 
    }
}

注意:在开始运行程序之前,需要启动HDFS和HBase。

然后开始编译运行

程序运行成功以后,如上图所示,会在运行结果中出现“69”。

在上面的console控制台中的警告信息为:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.apache.hadoop.security.authentication.util.KerberosUtil (file:/home/hadoop/workspace/HBase_Example/libs/hadoop-auth-2.10.2.jar) to method sun.security.krb5.Config.getInstance()
WARNING: Please consider reporting this to the maintainers of org.apache.hadoop.security.authentication.util.KerberosUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

这些警告信息是关于非法反射访问操作的提示。在 Java 9 及更高版本中,Java 引入了模块化系统,限制了对内部 API 的访问。当使用反射访问内部 API 时,可能会触发这些警告。 解决方案可以参考 启动hadoop报出一串警告 ,因为对程序没有太大的影响,此处并未测试该方案是否可行。

4.2.4 查看运行的结果

在HBase Shell交互式环境中,使用如下命令查看student表是否创建成功:

hbase> list

在HBase Shell交互式环境中,使用如下命令查看student表中的数据:



### 最后

面试题千万不要死记,一定要自己理解,用自己的方式表达出来,在这里预祝各位成功拿下自己心仪的offer。


![大厂面试题](https://img-blog.csdnimg.cn/img_convert/9adc7c241b8967a5c5e699ab44c216b9.webp?x-oss-process=image/format,png)

![面试题目录](https://img-blog.csdnimg.cn/img_convert/6438771155d645f3398d763286c0a8d5.webp?x-oss-process=image/format,png)

![](https://img-blog.csdnimg.cn/img_convert/807cd7d0b9ccd4e735e25c78e56ae400.webp?x-oss-process=image/format,png)

![](https://img-blog.csdnimg.cn/img_convert/53ce4e803da99b9a18d60b8703e65515.webp?x-oss-process=image/format,png)



ecurity.authentication.util.KerberosUtil  
>  WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations  
>  WARNING: All illegal access operations will be denied in a future release
> 
> 
> 


这些警告信息是关于非法反射访问操作的提示。在 Java 9 及更高版本中,Java 引入了模块化系统,限制了对内部 API 的访问。当使用反射访问内部 API 时,可能会触发这些警告。 解决方案可以参考 [启动hadoop报出一串警告]( ) ,因为对程序没有太大的影响,此处并未测试该方案是否可行。


#### 4.2.4 查看运行的结果


在HBase Shell交互式环境中,使用如下命令查看student表是否创建成功:



hbase> list


![](https://img-blog.csdnimg.cn/17675d0bd6354a5e846131c9926a4e7a.png)


在HBase Shell交互式环境中,使用如下命令查看student表中的数据: 



最后

面试题千万不要死记,一定要自己理解,用自己的方式表达出来,在这里预祝各位成功拿下自己心仪的offer。

[外链图片转存中…(img-OZ7KEk02-1718554934404)]

[外链图片转存中…(img-kz9q2zq6-1718554934405)]

[外链图片转存中…(img-CtRyDCTe-1718554934405)]

[外链图片转存中…(img-jZDeQeMx-1718554934405)]

  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值