24面试复习

24面试复习
java
冒泡、选择排序、 二分查找

排序算法:

  1. 冒泡排序(Bubble Sort):比较相邻的元素,如果顺序错误就交换它们,直到整个序列排序完毕。
  2. 选择排序(Selection Sort):每次从未排序部分选择最小(或最大)的元素,放到已排序部分的末尾,直到整个序列排序完毕。

二分查找算法:
二分查找(Binary Search)是一种高效的查找算法,要求被查找的序列必须是有序的。它的基本思想是:

  1. 将序列的中间元素与目标元素进行比较。
  2. 如果中间元素等于目标元素,则查找成功。
  3. 如果中间元素大于目标元素,则在左半部分继续查找。
  4. 如果中间元素小于目标元素,则在右半部分继续查找。
  5. 重复上述步骤,直到找到目标元素或查找范围缩小为空,表示查找失败。
hashmap 和 hashtable的区别?hashmap底层实现原理
  1. 线程安全性:Hashtable是线程安全的,而HashMap不是

  2. Null键和值:HashMap允许使用null作为键和值,而Hashtable则不允许

  3. 性能:由于Hashtable是线程安全的,它在多线程环境中的性能通常比HashMap低。HashMap在单线程环境下的性能更好。

关于HashMap的底层实现原理:
HashMap的底层实现是基于哈希表(Hash Table),具体是使用数组和链表(或红黑树)的组合来实现的。

  1. 数组:HashMap内部维护了一个存储元素的数组,通过使用哈希函数将键映射到数组的索引位置上。

  2. 链表和红黑树:当多个键映射到同一个索引位置上时,HashMap会使用链表(JDK7及之前版本)或红黑树(JDK8及之后版本)来解决哈希冲突问题。这样可以在同一个索引位置上存储多个键值对。

  3. 哈希函数:哈希函数用于将键映射到数组的索引位置。HashMap使用键的hashCode()方法来计算哈希值,然后通过对数组长度取模来确定存储位置。

  4. 扩容和重新哈希:当HashMap中的元素数量超过数组容量的阈值时,会触发扩容操作。扩容会创建一个更大的数组,并将原数组中的元素重新计算哈希值,并存储到新数组的对应位置上。

通过使用数组和链表(或红黑树)的组合,HashMap能够在常数时间复杂度下实现快速的插入、删除和查找操作,并且具有较高的效率。

stirngbuilder和stringbuffer的区别?

StringBuilder和StringBuffer都是用于处理可变字符串的类,它们之间的主要区别如下:

  1. 线程安全性:StringBuffer是线程安全的,而StringBuilder不是。

  2. 性能:由于StringBuffer是线程安全的,它在多线程环境中的性能通常比StringBuilder要低。StringBuffer的每个方法都使用了synchronized关键字进行同步,因此在并发访问时,需要进行线程同步,对性能有一定的影响。而StringBuilder没有进行同步处理,不需要额外的线程同步操作,因此在单线程环境下的性能更好。

  3. 可变性:StringBuilder和StringBuffer都提供了可变的字符串操作,可以进行字符串的拼接、插入、删除等操作。但是StringBuffer的API与StringBuilder完全一致,可以作为其线程安全版本的替代

修饰符的场景?

修饰符是用于修改类、方法、变量和接口的行为和特性的关键字。不同的修饰符可以在不同的场景中使用,具体的应用场景如下:

  1. 访问控制:修饰符可以控制成员(变量和方法)的可访问性,限定其他类或代码块对成员的访问权限。例如,public修饰符表示公共访问,可以从任何地方访问,而private修饰符表示私有访问,只能在当前类内部访问。

  2. 继承:修饰符可以影响类和成员的继承关系。例如,final修饰符可以阻止类被继承,protected修饰符可以允许子类访问父类的成员。

  3. 多态性:修饰符可以在多态性的概念中发挥作用。例如,abstract修饰符用于声明抽象类和抽象方法,这些抽象成员可以在子类中被实现和重写。

  4. 并发和线程安全:修饰符可以用于实现线程安全和并发控制。例如,synchronized修饰符用于实现同步访问,保证多个线程对共享资源的安全访问。

  5. 性能和内存管理:修饰符可以影响对象的性能和内存管理。例如,static修饰符用于声明静态成员,这些成员在内存中只有一份拷贝,可以通过类名直接访问,而不需要创建对象。

  6. 接口实现:修饰符可以用于类实现接口的过程中。例如,implements关键字用于表示一个类实现了一个或多个接口。

STRING的 特殊性

在许多编程语言中,包括Java,字符串(String)具有一些特殊性质,如下所示:

  1. 不可变性:字符串是不可变的,即一旦创建就不能被修改。当对字符串进行操作时,实际上是创建了一个新的字符串对象。

  2. 字符串池(String Pool):为了提高字符串的重用性和效率,Java使用了字符串池的概念。字符串池是一个位于堆内存的特殊区域,用于存储字符串常量。当创建字符串时,如果字符串池中已经存在相同内容的字符串,就会直接返回池中的引用,而不会重新创建对象。

  3. 字符串拼接:在许多编程语言中,包括Java,字符串拼接可以使用"+"操作符来实现。然而,由于字符串的不可变性,每次拼接操作都会创建一个新的字符串对象,这样在大量拼接操作时会导致性能下降。为了提高性能,可以使用StringBuilder或StringBuffer类进行字符串拼接操作。

  4. 字符串常量和字符串对象:**在Java中,字符串可以通过字符串常量或字符串对象的形式表示。**字符串常量是直接以双引号括起来的字符串,如"Hello"。字符串对象是通过new关键字显式创建的对象,如new String(“Hello”)。

内部类、外部类区别
  1. 定义方式:外部类是独立的类,而内部类是定义在另一个类内部的类。

  2. 访问权限:外部类可以被其他类直接访问,而内部类的访问权限取决于它所属的外部类。

  3. 关系与作用:内部类与外部类之间具有更紧密的关系,内部类可以访问外部类的成员,而外部类不能直接访问内部类的成员。

  4. 实例化方式:外部类可以直接实例化,而内部类必须通过外部类的实例进行实例化。

  5. 生命周期:外部类的对象可以独立存在,而内部类的生命周期依赖于外部类的对象。

  6. 静态特性:外部类中可以包含静态成员,而内部类可以是静态的或非静态的。

jvm了解吗?工作原理?一个对象的生命周期?垃圾回收(GC)机制?

我了解JVM(Java虚拟机)以及其工作原理,对象的生命周期以及垃圾回收机制的基本概念如下:

JVM(Java虚拟机):
Java虚拟机(JVM)是Java程序运行的环境,它负责解释和执行Java字节码。JVM是Java平台的核心组件,提供了内存管理、垃圾回收、线程管理等功能,使得Java程序可以在各种操作系统上运行。

JVM的工作原理:
JVM的工作原理可以简要概括为以下几个步骤:

  1. 代码编译:Java源代码通过编译器(例如javac)编译成字节码文件(.class文件)。
  2. 类加载:JVM的类加载器将字节码文件加载到内存中,并进行验证、准备和解析等操作。
  3. 内存分配:JVM将程序需要的内存划分为不同的区域,如堆、栈、方法区等。
  4. 字节码解释执行:JVM解释执行字节码指令,将其翻译成机器码并执行。
  5. 垃圾回收:JVM的垃圾收集器(GC)负责回收不再使用的对象,释放内存空间。
  6. 即时编译:JVM在运行过程中会对热点代码进行即时编译,以提高执行效率。

对象的生命周期:
在Java中,对象的生命周期包括以下几个阶段:

  1. 创建(Allocation):通过关键字new或其他方式创建对象。
  2. 使用(Usage):对象可以被程序使用,调用其方法或访问其属性。
  3. 不可达(Unreachable):当对象不再被任何引用所引用时,即没有可达的引用指向该对象。
  4. 垃圾收集(Garbage Collection):当对象变为不可达状态时,JVM的垃圾收集器会在适当的时机回收该对象的内存空间。
  5. 销毁(Finalization):在垃圾收集过程中,如果对象覆盖了finalize()方法,JVM会调用该方法执行一些清理操作。
  6. 回收(Deallocation):垃圾收集器回收对象的内存空间,使其可供后续的内存分配使用。

垃圾回收(GC)机制:
垃圾回收是JVM的一项重要功能,用于自动回收不再使用的内存空间。JVM中的垃圾收集器会定期或在特定条件下执行垃圾回收操作。垃圾回收机制的基本原理如下:

  1. 标记-清除算法:垃圾收集器首先标记所有被引用的对象,然后清除未标记的对象,释放其占用的内存空间。
  2. 垃圾收集器分类:JVM中有多种垃圾收集器,如Serial、Parallel、CMS、G1等。不同的收集器采用不同的算法和策略,以适应不同的应用场景和需求。
  3. 垃圾回收的触发条件:JVM根据不同的垃圾收集器和配置参数,根据内存使用情况、对象引用关系等条件来触发垃圾回收操作。
  4. 垃圾回收的停顿:垃圾回收操作会导致程序的停顿,即在垃圾回收过程中,程序的执行会暂时停止。这种停顿时间的长短与垃圾回收算法、堆大小和应用程序的特性有关。

垃圾回收机制的具体实现和优化是一个复杂的领域,涉及到对象的分代、引用追踪、内存压缩等技术。不同的垃圾收集器有不同的特点和适用场景,开发者可以根据具体需求和性能要求选择适合的垃圾收集器和调优参数。

MYSQL
mysql的事务?

事务是一组数据库操作的逻辑单元,要么全部执行成功,要么全部回滚(撤销)到事务开始之前的状态,确保数据的一致性和完整性。

事务具有以下四个特性(常称为ACID特性):

  1. 原子性(Atomicity):事务是一个原子操作单元,要么全部执行成功,要么全部失败回滚。如果在事务执行过程中发生了错误,所有已执行的操作都会被撤销,数据库回滚到事务开始之前的状态。

  2. 一致性(Consistency):事务的执行使数据库从一个一致性状态转移到另一个一致性状态。在事务开始和结束时,数据库的完整性约束必须保持一致,即数据库中的数据必须满足预先定义的规则。

  3. 隔离性(Isolation):事务的隔离性指的是并发执行的事务之间应该相互隔离,每个事务的操作对其他事务是不可见的,各个事务之间不会互相干扰。这样可以避免并发执行时产生的问题,如脏读(Dirty Read)、不可重复读(Non-repeatable Read)和幻读(Phantom Read)。

  4. 持久性(Durability):事务一旦提交(Commit),其对数据库的修改就是永久性的,即使在系统故障或重启之后,修改的数据也能够保留。

在MySQL中,使用以下语句来定义和控制事务:

  1. 开启事务:START TRANSACTIONBEGIN语句。
  2. 提交事务:COMMIT语句。将事务中的操作永久保存到数据库中。
  3. 回滚事务:ROLLBACK语句。撤销事务中的操作,回到事务开始之前的状态。
  4. 设置事务隔离级别:使用SET TRANSACTION ISOLATION LEVEL语句来指定事务的隔离级别,例如READ COMMITTEDREPEATABLE READ等。
什么是索引?如何创建索引

在数据库中,索引(Index)是一种数据结构,用于提高查询的速度和效率。它类似于书籍的目录,可以快速定位到存储在数据库表中的数据行。

索引通过创建并维护一种特定的数据结构,通常是B树(B-tree)或哈希表(Hash table),来存储列或列组合的值和对应的物理存储位置。通过使用索引,数据库可以避免全表扫描,而是直接定位到包含所需数据的位置,从而加快查询的速度。

创建索引时,需要选择要创建索引的列或列组合。一般来说,选择经常被查询和用于连接表的列作为索引列是有效的。索引可以应用于单个列(单列索引)或多个列的组合(复合索引)。

在MySQL中,可以使用如下的语法来创建索引:

  1. 创建单列索引:
CREATE INDEX index_name ON table_name (column_name);
  1. 创建复合索引:
CREATE INDEX index_name ON table_name (column1_name, column2_name, ...);

在创建索引时,需要注意以下几点:

  • 索引会占用一定的存储空间,因此不宜滥用。过多或不必要的索引可能会导致性能下降和额外的存储开销。
  • 索引适用于频繁被查询的列,但对于很少被查询的列,创建索引可能没有明显的性能提升,并且会增加插入、更新和删除操作的开销。
  • 当表中的数据发生变化(插入、更新、删除)时,索引需要进行维护,因此可能会对写操作的性能产生一定的影响。
  • 在选择索引列时,要考虑到查询的频率和选择性,选择具有高选择性的列作为索引列可以提高索引的效果。
  • 可以使用EXPLAIN语句来查看查询执行计划,以评估索引的使用情况和性能。

需要根据具体的数据库和应用场景来选择创建索引的列和类型,以及权衡索引的好处和开销。合理的索引设计可以显著提高数据库查询的性能和响应时间。

mysql有哪些优化?

MySQL有多种优化技术和策略,可以提高数据库性能和响应时间。以下是一些常见的MySQL优化方法:

  1. 设计良好的数据模型:合理设计数据库的表结构、字段和关系,使用适当的数据类型和约束,避免冗余和重复数据,以提高查询和操作的效率。

  2. 创建适当的索引:通过创建索引来加快查询速度。根据查询的频率和选择性选择合适的列作为索引列,避免创建过多或不必要的索引。

  3. 优化查询语句:编写高效的查询语句,避免全表扫描和不必要的数据操作。使用合适的查询条件、连接方式和排序规则,避免使用过多的子查询和复杂的表达式。

  4. 避免使用SELECT *:只选择需要的列,避免查询不必要的数据,减少网络传输和内存消耗。

  5. 优化表结构和数据类型:选择合适的数据类型,避免使用过大或过小的字段类型。对于大字段(如文本或二进制数据),可以使用延迟加载或分离存储的方式优化性能。

  6. 调整服务器参数:根据服务器硬件配置和应用需求,调整MySQL的配置参数,如缓冲区大小、并发连接数、线程池大小等,以提高性能和资源利用率。

  7. 使用合适的存储引擎:MySQL支持多种存储引擎,如InnoDB、MyISAM等。根据应用的需求选择合适的存储引擎,考虑事务支持、并发性能、数据完整性等因素。

  8. 缓存和缓冲:使用合适的缓存技术,如MySQL自带的查询缓存、应用层缓存(如Memcached、Redis)等,减少对数据库的频繁访问。同时,合理配置数据库的缓冲区(如查询缓存、键值缓存等),提高数据的访问速度。

  9. 分区和分表:对于大型数据表,可以考虑使用分区或分表技术,将数据划分成较小的逻辑单元,以提高查询和维护的效率。

  10. 定期优化和维护:定期进行数据库的优化和维护工作,如碎片整理、统计信息更新、索引重建等,以保持数据库的性能和稳定性。

以上是一些常见的MySQL优化方法,具体的优化策略需要根据应用的具体情况和需求进行调整和实施。同时,使用性能分析工具和监控工具可以帮助识别和解决性能瓶颈,进一步提高MySQL的性能。

Hadoop
hadoop是什么?请你说下你对hadoop理解?

hadoop是一个适合海量数据的分布式存储和分布式计算的平台

Hadoop的核心组件包括:

  1. Hadoop分布式文件系统(Hadoop Distributed File System,HDFS):HDFS是Hadoop的存储层,用于存储大规模数据集。它将数据分散存储在多个节点上,提供高容错性和高可靠性,支持数据的并行访问和处理。

  2. Hadoop YARN(Yet Another Resource Negotiator):YARN是Hadoop的资源管理系统,负责集群资源的调度和管理。它将集群资源划分为容器(Container),并为应用程序分配资源。

  3. Hadoop MapReduce:MapReduce是Hadoop的计算模型和编程框架,用于在分布式环境中处理大规模数据集。MapReduce通过将计算任务分解为Map和Reduce阶段,并在多个节点上并行执行,实现扩展性和高性能的数据处理。

hdfs由什么组成

Hadoop分布式文件系统(Hadoop Distributed File System,HDFS)由以下几个组件组成:

  1. NameNode(名称节点):NameNode是HDFS的主节点,负责管理文件系统的命名空间和元数据信息。它记录了文件的层次结构、文件的块信息、文件与块的映射关系等。NameNode还负责处理客户端的读写请求,并协调数据块的存储和复制。

  2. DataNode(数据节点):DataNode是HDFS的工作节点,负责存储实际的数据块。每个数据节点负责管理本地存储上的数据块,并定期向NameNode报告自身的存储状态和可用性。DataNode也负责处理客户端的读写请求,根据指令进行数据块的传输和复制。

  3. Secondary NameNode(辅助名称节点):Secondary NameNode并不是NameNode的备份,而是用于辅助NameNode的工具节点。Secondary NameNode定期从NameNode获取文件系统的快照,并帮助合并编辑日志文件,以减少NameNode的恢复时间和元数据的风险。

  4. 客户端:HDFS的客户端是与HDFS交互的应用程序或工具。客户端可以通过HDFS提供的API或命令行工具与文件系统进行交互,例如读取文件、写入文件、创建目录等。客户端与NameNode和DataNode进行通信,根据需要传输数据块和请求元数据信息。

这些组件共同工作,构成了HDFS的分布式存储和管理系统。NameNode负责管理元数据,而DataNode负责存储和管理实际的数据块。客户端通过与NameNode和DataNode的交互,实现对文件系统的访问和操作。

HDFS的设计目标是支持大规模数据的存储和处理,具有高容错性、可靠性和可扩展性。通过将数据块分散存储在多个节点上,并在各个节点上并行处理数据,HDFS实现了高性能和高可用性的分布式文件系统。

yarn由什么组成

YARN(Yet Another Resource Negotiator)是Hadoop的资源管理系统,它由以下几个组件组成:

  1. ResourceManager(资源管理器):ResourceManager是YARN的主节点,负责整个集群的资源管理和调度。它接收来自客户端和应用程序的资源请求,并根据可用资源情况进行资源分配和调度。ResourceManager还负责监控集群中的节点和应用程序的状态,并协调节点之间的通信。

  2. NodeManager(节点管理器):NodeManager是YARN的工作节点,负责管理单个节点上的资源和任务执行。每个节点上都运行一个NodeManager进程,它负责与ResourceManager通信,报告节点的可用资源和运行任务的状态。NodeManager还负责启动和停止容器(Container),容器是YARN分配给应用程序的资源单位,用于在节点上执行任务。

  3. ApplicationMaster(应用程序管理器):ApplicationMaster是每个应用程序在YARN上的管理实体。每个应用程序都会有一个独立的ApplicationMaster进程,它负责与ResourceManager协商资源、分配任务、监控任务执行和处理失败情况。ApplicationMaster运行在容器中,可以与NodeManager交互,以执行任务和获取节点资源。

  4. 客户端:YARN的客户端是与YARN交互的应用程序或工具。客户端可以通过YARN提供的API或命令行工具与ResourceManager进行通信,提交应用程序、查询应用程序状态、获取应用程序的日志等。

这些组件共同工作,构成了YARN的资源管理和调度系统。ResourceManager负责全局的资源管理和调度决策,NodeManager负责本地节点上的资源管理和任务执行,而ApplicationMaster负责单个应用程序的资源协调和任务管理。

YARN的设计目标是为大规模数据处理提供统一的资源管理框架,使多个应用程序可以共享集群资源,实现高效的资源利用和任务调度。通过将资源管理和任务调度分离,YARN提供了更大的灵活性和扩展性,可以支持不同类型的应用程序和计算框架(如MapReduce、Spark等)在同一个集群上运行。

相同的key的数据进入到同一个reduce,这个reduce是指reduce函数还是任务

在Hadoop的MapReduce编程模型中,“reduce"通常指的是"reduce函数”,而不是"任务"。

在MapReduce中,Map阶段将输入数据集划分为若干个独立的数据块,并由多个Mapper并行处理这些数据块。每个Mapper将输入数据的不同部分映射为键值对(key-value pairs),其中键(key)是用于标识数据的关键信息,值(value)是与键相关联的数据。

接着,在Reduce阶段,具有相同键的键值对会被分配到同一个Reduce函数中进行处理。Reduce函数是用户自定义的函数,用于对具有相同键的数据进行聚合、计算或其他操作。Reduce函数的输入是一个键和一个值的迭代器,它可以对这些值执行自定义的逻辑,生成最终的输出。

因此,"reduce"一般指的是在Reduce阶段执行的用户定义的Reduce函数,用于处理具有相同键的数据。任务(task)则是指整个MapReduce作业的执行单元,包括Map任务和Reduce任务。每个Reduce任务会处理一部分具有相同键的数据,而Reduce函数则是在Reduce任务中对这些数据进行处理的逻辑。

hdfs读写流程

读流程

在这里插入图片描述

写流程

在这里插入图片描述

HDFS(Hadoop Distributed File System)的读写流程如下:

写入数据的流程:

  1. 客户端向NameNode发送写入请求,并提供要写入的文件的名称和数据块大小等信息。
  2. NameNode接收到写入请求后,检查文件是否已经存在,如果不存在,则创建新的文件条目,并返回可用的DataNode列表。
  3. 客户端根据返回的DataNode列表选择其中一个作为目标节点,并与该DataNode建立连接。
  4. 客户端将数据分成固定大小的数据块,并逐个发送给目标DataNode。
  5. 目标DataNode接收到数据块后,进行数据块的确认写入,即将数据块存储到本地磁盘。
  6. 目标DataNode将写入结果返回给客户端。
  7. 客户端继续将剩余的数据块发送给其他的DataNode,直到所有的数据块都写入完毕。
  8. 客户端向NameNode发送完成写入的请求,NameNode更新文件的元数据信息。

读取数据的流程:

  1. 客户端向NameNode发送读取请求,并提供要读取的文件的名称。
  2. NameNode接收到读取请求后,检查文件是否存在,并返回包含文件块信息的DataNode列表。
  3. 客户端根据返回的DataNode列表选择其中一个作为源节点,并与该DataNode建立连接。
  4. 客户端根据文件块信息,向源DataNode发送读取请求。
  5. 源DataNode读取请求的数据块,并将数据块的内容返回给客户端。
  6. 客户端继续向其他DataNode发送读取请求,直到获取完整的文件内容。
  7. 客户端完成数据的读取,并进行后续的处理。

在HDFS中,数据存储在多个DataNode上,通过数据块的复制和分散存储,提高了数据的可靠性和容错性。NameNode负责管理文件的元数据信息,包括文件的名称、大小、数据块的位置等。客户端与NameNode和DataNode进行交互,实现对文件的读写操作。同时,HDFS还利用了数据本地性原则,尽可能将计算任务分配到与数据所在位置相邻的节点上,以减少数据传输的开销。

MapReduce流程

在这里插入图片描述

任务在yarn上的执行流程

在YARN(Yet Another Resource Negotiator)上执行任务的流程如下:

  1. 提交应用程序:客户端向ResourceManager提交应用程序,并指定所需的资源、应用程序的执行命令和其他相关配置信息。

  2. 资源分配和调度:ResourceManager接收到应用程序的提交请求后,根据集群中可用的资源情况进行资源的分配和调度。它将根据应用程序的需求决定在哪些节点上启动容器(Container)来执行任务。

  3. 启动ApplicationMaster:ResourceManager为应用程序分配一个容器,并在该容器中启动ApplicationMaster。ApplicationMaster是应用程序在YARN上的管理实体,负责与ResourceManager进行通信、协调资源、分配任务、监控任务执行等。

  4. 任务分配和执行:ApplicationMaster向ResourceManager请求所需的资源,并根据资源的分配情况决定在哪些节点上启动任务。ResourceManager将为任务分配相应的容器,并在相应的节点上启动任务。

  5. 任务执行:任务在容器中启动后,执行相关的计算逻辑。任务可以是用户自定义的Map任务或Reduce任务,也可以是其他类型的任务。

  6. 监控和容错:ApplicationMaster负责监控任务的执行状态和进度,以及处理任务执行过程中可能出现的错误或故障。它会与NodeManager通信,获取任务的状态信息,并将更新的状态报告给ResourceManager。

  7. 任务完成和资源释放:任务在完成后,ApplicationMaster会向ResourceManager报告任务的完成状态。ResourceManager收到报告后,会释放任务所占用的资源,并更新应用程序的状态。

  8. 应用程序完成:当应用程序的所有任务完成后,ApplicationMaster会向ResourceManager发送应用程序完成的报告。ResourceManager收到报告后,会将应用程序的状态更新为完成状态。

整个过程中,ResourceManager负责全局的资源管理和调度决策,而ApplicationMaster负责在应用程序级别上管理和协调任务的执行。任务的启动、执行和监控是通过与NodeManager进行通信来实现的。

YARN的设计目标是实现多租户的资源管理和调度,使不同类型的应用程序可以共享集群资源并以高效的方式运行。它提供了灵活的资源管理框架,可支持多种计算框架和应用程序在同一个集群上运行,并实现了高可用性和容错性。

yarn的调度策略?三种,默认是哪一种

YARN(Yet Another Resource Negotiator)提供了多种调度策略,其中三种常见的调度策略是:

  1. 公平调度(Fair Scheduler):公平调度器通过将资源公平地分配给各个应用程序,尽量保证每个应用程序能够获得相等的资源份额。它使用基于权重的机制来为应用程序分配资源,较为适用于多用户、多租户环境。

  2. 容量调度(Capacity Scheduler):容量调度器将集群资源划分为多个预定义的队列,每个队列都有自己的资源容量。管理员可以为每个队列设置资源配额,并根据应用程序的优先级和需求将资源分配给相应的队列。容量调度器适用于多部门或多项目共享同一集群的场景。

  3. 先入先出调度(FIFO Scheduler):先入先出调度器按照应用程序的提交顺序,依次为应用程序分配资源。这意味着先提交的应用程序会优先获得资源,直到它们完成或释放资源。先入先出调度器简单且易于实现,适用于单用户或单应用程序的场景。

默认情况下,YARN的调度策略是容量调度(Capacity Scheduler)。这是因为容量调度器提供了更灵活的资源管理和调度能力,可以根据不同的队列需求进行资源划分,适应多种场景和应用程序的需求。管理员可以根据实际情况进行配置和调整,以满足集群的资源管理和调度需求。

yarn队列设置?

在YARN(Yet Another Resource Negotiator)中,队列的设置是使用容量调度器(Capacity Scheduler)来进行资源管理和调度的一项重要配置。通过队列设置,管理员可以为不同的应用程序或用户定义不同的队列,并为每个队列分配资源配额。

以下是设置YARN队列的一般步骤:

  1. 配置容量调度器:在YARN的配置文件(如yarn-site.xml)中,找到容量调度器相关的配置项,通常是yarn.scheduler.capacity.*。确保容量调度器已启用,并配置正确的调度器属性。

  2. 创建队列:在容量调度器的配置中,可以定义多个队列,每个队列代表一个资源分配的单元。可以为不同的应用程序、用户或部门创建不同的队列。在配置文件中,配置项通常是类似于yarn.scheduler.capacity.<queue-name>.*的形式。

  3. 配置队列属性:为每个队列设置相应的属性,包括资源配额、优先级、最大资源限制和可选的队列属性。例如,capacity属性定义队列的资源配额百分比,maxCapacity属性定义队列的最大资源配额百分比。

  4. 配置队列层次结构:容量调度器支持队列的层次结构,可以创建多级队列结构以实现更灵活的资源管理。可以为父队列设置资源配额,然后在父队列下创建子队列并为其分配资源配额。

  5. 重启YARN服务:在完成队列的配置后,需要重启YARN服务以使新的配置生效。

通过队列设置,管理员可以根据不同的需求和优先级为应用程序分配资源,并实现多租户的资源隔离和管理。队列的设置可以在YARN的配置文件中完成,具体的配置项和属性根据不同的YARN版本和发行版可能会有所不同,请参考相应的文档和配置指南进行具体设置。

Hadoop优化

hdfs中合并小文件
合理设置map数量(切片的大小控制)
当集群资源充足的情况下:map数量可以适当调多,那就是意味着切片小些。
当集群资源不充足的情况下:map数量可以适当调少,那就是意味着切片大些。

hive
hive的架构?

hive是hadoop的客户端工具。
存储依赖于hdfs
计算默认依赖于mr
元数据默认使用derby,经常会改成mysql
解析引擎,驱动:mysql的驱动。

什么是分区

从物理层面看:hive的一个表就是hdfs中的一个目录,分区就是子目录。
作用:为了避免全表扫描,提高查询效率。
使用场景:日期最多,省份。。dt,pt

外部表和内部表

外部表(External Table)和内部表(Internal Table)是Hive中用于管理数据的两种不同方式。

外部表在Hive中仅维护对数据的元数据信息,数据本身存储在外部的文件系统中。外部表的数据可以是已经存在的文件,也可以是由外部过程或其他工具生成的文件。外部表的数据可以被多个系统共享和访问,删除外部表不会删除关联的数据文件,只会删除元数据信息

内部表在Hive中维护对数据的元数据信息,并将数据存储在Hive管理的默认位置。内部表的数据由Hive负责管理,包括数据的加载、存储和清理。内部表的数据只能由Hive访问,删除内部表将同时删除关联的数据文件和元数据信息。

hive和mysql的区别?

区别包括:

  • 处理方式:Hive适合大规模数据处理,MySQL适合实时查询。
  • 数据规模和性能:Hive适合PB级数据,性能较慢;MySQL适合中小规模数据,性能较快。
  • 存储和查询语言:Hive存储在分布式文件系统中,使用HiveQL;MySQL存储在关系型数据库中,使用SQL。
  • 数据模型和灵活性:Hive支持半结构化数据,MySQL使用关系型数据模型。
  • 数据安全性:Hive提供细粒度权限控制,MySQL也提供权限管理功能。

选择取决于数据规模、查询需求、性能要求和数据模型的复杂性。

hive如何优化

以下是优化Hive性能的几个关键点:

  1. 数据分区和分桶:使用分区和分桶技术减少数据扫描范围,提高查询性能。

  2. 数据压缩:采用合适的压缩格式减少存储空间和I/O开销。

  3. 数据存储格式:选择适合场景的列式存储格式,如Parquet或ORC。

  4. 统计信息:确保分区和表的统计信息准确,帮助优化查询计划。

  5. 调整查询参数:根据需求调整Hive的查询参数,如mapreduce.job.reduces和内存分配。

  6. 处理数据倾斜:针对数据倾斜采取相应策略,如随机前缀、动态分区等。

  7. 合理设计表结构:考虑查询需求和模式,选择合适的字段类型、分区策略和索引。

  8. 硬件和资源优化:确保适当配置的硬件和资源支持并发查询和大规模数据处理。

这些策略可以帮助提升Hive的性能,但具体的优化方法需根据实际场景和需求进行调整和实验。

数据仓库
什么是数仓

数仓是一个集成和存储来自不同数据源的数据的中心化系统,旨在支持企业的决策制定和分析需求。它提供高质量、一致性和历史性的数据,按照业务主题组织,支持数据分析和查询。数仓技术包括ETL、数据建模、数据存储和查询等。它在业务分析、决策支持和业务智能方面发挥关键作用。

如何分层, 每一层的作用。每一层依赖的工具。

数仓分为以下几个层级:

  1. 原始数据层:存储原始数据,保留数据完整性。

    • 工具:数据抽取工具、分布式文件系统。
  2. 数据清洗和转换层:清洗、转换和集成数据,消除冗余和不一致性。

    • 工具:ETL工具。
  3. 维度建模层:按照维度建模方法组织数据,提供面向主题的数据视图。

    • 工具:数据建模工具。
  4. 数据存储层:存储清洗、转换和建模后的数据,提供高性能的访问和查询。

    • 工具:列式存储格式、关系型数据库。
  5. 数据应用层:提供给用户进行数据分析、查询和报表的接口。

    • 工具:商业智能工具、数据分析工具。

通过这些层级,数仓能够提供高质量、易于分析的数据,满足企业的决策和分析需求。

星型和雪花,区别在哪里?

星型模型(Star Schema)和雪花模型(Snowflake Schema)是两种常见的数据仓库维度建模方式。

星型模型:

  • 结构简单,由一个中心的事实表和多个维度表组成。
  • 维度表直接与事实表关联,无进一步规范化。
  • 易于理解和查询,适用于简单的分析需求和较小规模的数据集。

雪花模型:

  • 在星型模型基础上进行规范化,将维度表进一步细分成多个规范化的子维度表。
  • 子维度表通过外键与父维度表关联,形成多层级的关联结构。
  • 存储效率高,但查询复杂度增加,适用于复杂的分析需求和大规模的数据集。

根据具体需求和数据规模,选择适合的模型。

什么是事实表、维度表?宽表、拉链表?

在数据仓库和维度建模中,以下是关于事实表、维度表、宽表和拉链表的简要解释:

事实表(Fact Table):

  • 事实表是数据仓库中的核心表,用于存储事实数据和度量指标。
  • 它包含与业务过程相关的数值型数据,例如销售金额、订单数量、客户数量等。
  • 事实表通常包含外键,与维度表进行关联。

维度表(Dimension Table):

  • 维度表用于提供事实表上的数据上下文信息,描述事实数据的特征。
  • 它包含业务中用于分析和查询的描述性属性,例如时间、地理位置、产品、客户等。
  • 维度表的每个记录通常具有唯一标识符和相关属性列。

宽表(Wide Table):

  • 宽表是指具有大量列的表,其中包含了多个维度表的属性列和事实表的度量列。
  • 宽表的设计目的是为了简化查询操作,将多个维度的属性和度量指标集中在一张表中,提高查询性能。

拉链表(Slowly Changing Dimensions,SCD):

  • 拉链表是一种用于跟踪维度数据变化历史的技术。
  • 当维度数据发生变化时,拉链表会保留历史版本,并在新版本中添加有效期的时间范围。
  • 拉链表常用于记录维度属性的变化,例如产品价格、客户地址等,以便分析历史趋势和追溯数据变化。

维度表和事实表是在维度建模中常用的两个关键概念,用于构建数据仓库的结构。宽表是简化查询操作的一种设计方式,而拉链表是用于跟踪维度数据变化历史的技术手段。

数据集成
datax和sqoop的使用区别?

DataX和Sqoop是用于数据传输的工具,但有以下主要区别:

  • 数据源和目标支持:Sqoop主要用于关系型数据库,DataX支持更广泛的数据源和目标。
  • 传输方式:Sqoop使用基于MapReduce的方式,DataX支持多种传输方式。
  • 数据转换和处理:DataX具有更灵活的数据转换和处理功能。
  • 社区和生态系统:Sqoop拥有较大的社区和成熟的生态系统,DataX的社区规模较小。

根据需求和数据源目标的不同,选择适合的工具。

flume有哪些source?

Flume的常见Source组件包括:

  • Avro Source:从Avro客户端接收数据。
  • Netcat Source:通过TCP/IP接收网络数据。
  • Exec Source:通过执行外部命令获取数据。
  • Spooling Directory Source:监视目录中的文件,并将其内容发送为事件。
  • Syslog Source:通过UDP或TCP接收Syslog日志消息。
  • HTTP Source:通过HTTP接收数据。
  • JMS Source:从JMS队列或主题接收消息。
  • Kafka Source:从Kafka消息队列接收消息。
  • Twitter Source:从Twitter Streaming API接收推文数据。
  • Taildir Source:监视目录中的文件,并将新行发送为事件。
    根据数据源类型和需求选择适当的Source组件。
同步策略?

MySQL到Hive的同步策略包括:

  1. 批量导入:定期将MySQL数据批量导入到Hive。
  2. 增量导入:将MySQL中新增或更新的数据同步到Hive。
  3. 实时同步:实时将MySQL数据同步到Hive。(flume-kafka或者cannal-kafka)
  4. 自定义ETL:自定义抽取、转换、加载过程将MySQL数据加载到Hive。

根据需求选择适合的同步策略。

HBASE
如何设计rowkey

设计RowKey的主要考虑因素包括:

  • 唯一性:确保RowKey的唯一性。
  • 散列化:通过散列函数将数据均匀分布在不同的Region中,避免热点问题。
  • 可排序性:考虑RowKey的排序性,使得相关数据能按顺序存储,便于范围查询。
  • 数据局部性:将相关数据存储在相邻的行中,提高数据的局部性,减少随机读取开销。
  • 访问模式:根据数据的访问模式和查询需求设计RowKey。
  • 大小控制:合理控制RowKey的大小,避免过长或过短。

综合考虑这些因素,设计一个合适的RowKey,满足数据存储和访问的需求。

HBASE中如何处理热点问题

处理HBase热点问题的方法包括:

  • 预分区:使用预分区技术将数据均匀分布到不同的Region中。
  • 散列前缀:在RowKey中添加散列前缀,将数据均匀分散在不同的Region中。
  • 随机化RowKey:将RowKey设计为随机值,减少写入热点。
  • 前缀字节随机化:在具有共同前缀的RowKey中引入随机值,均匀分布数据。
  • 区域拆分:手动触发区域拆分操作,实现负载均衡。
  • 增加Region服务器:增加Region服务器节点,分担负载。
  • 数据缓存调优:合理配置数据缓存大小,提高读取性能。

这些方法可帮助处理HBase中的热点问题,提高性能和负载均衡。根据具体情况选择适合的方法。

HBASE读写流程

HBase的读写流程如下:

写入流程:

  1. 客户端向HBase的主节点(HMaster)发送写入请求。
  2. HMaster根据表的元数据信息确定数据所在的RegionServer。
  3. HMaster将写入请求转发给对应的RegionServer。
  4. RegionServer接收到写入请求后,将数据暂存在内存中的MemStore中。
  5. 当MemStore中的数据达到一定大小(或其他触发条件)时,会将数据写入磁盘上的HFile。
  6. 同时,RegionServer会将写入操作记录到WAL(Write-Ahead Log)中,以保证数据的持久性。
  7. 写入完成后,RegionServer向客户端发送写入成功的响应。

读取流程:

  1. 客户端向HBase的主节点发送读取请求。
  2. HMaster根据表的元数据信息确定数据所在的RegionServer。
  3. HMaster将读取请求转发给对应的RegionServer。
  4. RegionServer首先检查内存中的MemStore,如果数据在MemStore中存在,直接返回给客户端。
  5. 如果数据不在MemStore中,RegionServer会从磁盘上的HFile中读取数据。
  6. 如果HFile中的数据不足以满足读取请求,RegionServer会查找并合并其他存储在磁盘上的HFile。
  7. 读取完成后,RegionServer将数据返回给客户端。

同时,HBase还利用了缓存机制和块索引(Block Index)来提高读取性能。读取过程中,HBase会利用块索引快速定位数据的位置,并通过缓存(包括内存缓存和文件系统缓存)减少磁盘访问次数,加快读取速度。

总体而言,HBase的写入流程包括主节点路由、数据写入内存和磁盘持久化,而读取流程则涉及主节点路由、数据检索与合并,以及数据返回给客户端。

HBASE有哪些组件

HBase是一个分布式、可扩展和可靠的列式数据库,它包含以下核心组件:

  1. HMaster:HMaster是HBase的主节点,负责管理整个集群的元数据(包括表的结构信息)和协调管理RegionServer。它处理表的创建、删除、分割和合并等操作。

  2. RegionServer:RegionServer是HBase的工作节点,负责实际的数据存储和处理。每个RegionServer负责管理多个Region,其中每个Region存储一部分表的数据。RegionServer处理数据的读写请求,并负责数据的持久化和复制等操作。

  3. ZooKeeper:ZooKeeper是HBase的协调服务,用于管理和协调集群中各个组件的状态和配置信息。它负责选举HMaster,存储元数据和集群配置,以及监控和通知各个组件的变化。

  4. HDFS:HDFS(Hadoop分布式文件系统)是HBase的底层存储系统,用于存储HBase的数据。HBase利用HDFS的分布式特性和高容错性来存储数据,并通过HDFS的数据复制机制实现数据的冗余备份。

  5. HFile:HFile是HBase中的数据存储格式,它是一种基于块的文件格式,用于存储和组织数据。HFile采用压缩和索引等技术,提供高效的数据读写和范围查询能力。

  6. WAL(Write-Ahead Log):WAL是HBase的写入日志,用于确保数据的持久性和可恢复性。在数据写入期间,HBase将写入操作先记录到WAL中,然后再将数据写入内存和磁盘。在发生故障时,可以使用WAL中的日志重放来恢复数据。

除了这些核心组件外,HBase还依赖于其他工具和技术,如Hadoop、HBase客户端API和管理工具(如HBase Shell和HBase Web UI)等,以提供完整的功能和管理能力。

hive和hbase的区别

Hive和HBase是两个在Hadoop生态系统中广泛使用的数据存储和处理工具,它们有以下主要区别:

  1. 数据模型:

    • Hive:Hive是一个基于SQL的数据仓库工具,它使用类似于传统关系型数据库的表结构和SQL查询语言来处理数据。Hive将数据存储在HDFS中,并使用Hive的元数据来管理表结构和数据的存储位置。
    • HBase:HBase是一个面向列的分布式数据库,它提供了实时随机读写的能力。HBase使用键值对存储数据,其中行键(RowKey)用于唯一标识数据,并且数据按列族(Column Family)进行组织。
  2. 数据访问方式:

    • Hive:Hive使用HiveQL,一种类似于SQL的查询语言,使用户可以使用SQL语法进行数据查询和分析。Hive查询通常会被转换为MapReduce或Tez等作业进行执行。
    • HBase:HBase提供了编程接口(如Java API)来进行数据的读写操作。用户可以使用API直接访问HBase中的数据,实现高度灵活的数据操作。
  3. 数据处理能力:

    • Hive:Hive适用于批量处理大规模的结构化数据,特别擅长于数据分析和批量数据转换。它使用MapReduce或Tez等框架来执行作业,处理速度相对较慢。
    • HBase:HBase适用于需要实时随机读写的场景,特别擅长于处理海量的非结构化或半结构化数据。HBase通过数据的分布和索引来实现快速的数据访问。
  4. 数据一致性:

    • Hive:Hive的数据一致性通常是“最终一致性”,即在数据写入完成后,需要一定的时间才能在查询结果中看到更新后的数据。
    • HBase:HBase提供强一致性,即在数据写入完成后,可以立即读取到最新的数据。

总体而言,Hive适用于批量处理和数据分析,使用类SQL语言进行查询,而HBase适用于实时随机读写和非结构化数据存储,使用键值对进行数据访问。选择使用哪个工具取决于具体的数据处理需求和性能要求。在某些情况下,Hive和HBase可以结合使用,以满足不同类型的数据处理需求。

kafka
为什么这么快?吞吐大原因?

Kafka之所以快速且具有高吞吐量的原因主要有以下几点:

  1. 分布式架构:采用分布式架构,实现数据的并行处理和负载均衡。

  2. 高效存储:采用顺序写和零拷贝技术,提高存储和读取效率。

  3. 批量处理和压缩:支持批量发送和消费消息,并可进行消息压缩,减少网络传输开销。

  4. 零拷贝技术:避免数据在内核空间和用户空间之间的多次拷贝,降低CPU和内存开销。

  5. 优化的网络处理:采用非阻塞I/O和多路复用技术,高效处理网络请求。

综上所述,这些因素共同作用使得Kafka具有高速和高吞吐量的特点。

挂了怎么办?是kafka的服务挂了,还是kafka的集群物理机挂了?

kafka是分布式的,有备份,有选举。
下游的消费者一般会有记录的offset,
上游写入一般可以重新写入,如果数据有重复,可以代码去重。

spark
RDD五大特性

RDD由很多partition构成,有多少partition就对应有多少task

算子实际上是作用在每一个分区上,

RDD之间有依赖关系,宽依赖和窄依赖,用于切分Stage

Spark默认是hash分区,ByKey类的算子只能作用在kv格式的rdd上

Spark为task的计算提供了最佳的计算位置,移动计算而不是移动数据

常用的一些高性能算子

以下是Spark中常用的高性能算子:

  1. map()和flatMap():逐个处理数据集中的元素。

  2. filter():根据条件过滤数据集。

  3. reduce()和fold():聚合数据集的元素。

  4. groupByKey()和reduceByKey():按键对数据集进行分组和聚合。

  5. join()和cogroup():合并多个数据集。

  6. sortBy():对数据集进行排序。

  7. distinct():去重操作。

  8. count()和countByKey():计算元素总数和相同键的元素个数。

  9. foreach():对每个元素应用函数,常用于执行副作用操作。

这些算子可帮助高效处理大规模数据集。

reducebykey和 groupbykey区别?

reduceByKey会在map端做预聚合,可以减少shufflfflffle过程中传输的数据量,提高执行效率,

groupByKey不能做预聚合

在某些业务场景reduceByKey没办法实现,需要使用groupByKey

尽量使用reduceByKey代替groupByKey

介绍下spark的部署方式?

Spark的部署方式包括:

  1. Local模式:在本地机器上以单机模式运行Spark应用程序。

  2. Standalone模式:搭建独立的Spark集群,包括主节点和工作节点。

  3. Apache Mesos:将Spark作为Mesos的一个框架运行。

  4. Hadoop YARN:将Spark作为YARN的一个应用程序运行。

  5. Kubernetes:在Kubernetes集群上部署和运行Spark。

选择部署方式取决于需求和环境配置。

spark提交任务的参数?

Spark提交任务时常用的参数包括:

  1. –class:指定应用程序的主类。
  2. –master:指定Spark集群的URL。
  3. –deploy-mode:指定应用程序的部署模式。
  4. –executor-memory:指定每个Executor的内存大小。
  5. –num-executors:指定Executor的数量。
  6. –executor-cores:指定每个Executor的CPU核心数。
  7. –total-executor-cores:指定所有Executor的总CPU核心数。
  8. –conf:设置其他Spark配置属性的参数。
  9. 应用程序的jar包或Python文件路径。

这些参数可根据需求进行配置,用于优化Spark应用程序的性能和资源利用。

血统,宽依赖,窄依赖

在Spark中,血统(Lineage)是指RDD之间的依赖关系记录,用于容错和故障恢复。宽依赖(Wide Dependency)涉及数据洗牌和重新分区,而窄依赖(Narrow Dependency)不需要洗牌,性能更高。血统和依赖关系记录对于Spark的数据恢复和容错非常重要。

spark优化

Spark应用程序的性能和效率可以通过以下几种方式进行优化:

  1. 数据压缩与序列化:使用压缩算法和序列化机制可以减少数据在网络传输和存储中的大小,提高数据的传输效率和存储效率。

  2. 广播变量(Broadcast Variables):对于较小的数据集,可以将其广播到所有的工作节点上,避免重复传输,提高性能。

  3. 避免数据倾斜(Data Skew):监控和处理数据倾斜问题,例如通过使用随机前缀或重新分区等方式来均衡数据分布,避免某个任务或分区处理过多数据。

  4. 分区和并行度设置:根据数据量和集群资源情况,合理设置数据的分区数和并行度,以充分利用集群资源并提高任务执行效率。

  5. 使用合适的缓存策略:通过使用缓存(Caching)机制,将中间结果或常用数据缓存到内存中,减少计算和读取的开销。

  6. 使用合适的算子和转换操作:选择合适的算子和转换操作,尽可能使用窄依赖的操作来减少洗牌(Shuffle)操作,减少数据的传输和重组。

  7. 数据分区优化:根据数据的访问模式和计算需求,进行数据的合理分区,以提高数据的局部性和减少洗牌操作。

  8. 内存管理和调优:合理配置Spark的内存分配和使用,包括调整驱动程序和Executor的内存大小、使用合适的内存管理模式和GC策略等,以提高内存利用率和减少GC开销。

  9. 并行操作与异步调用:在适当的情况下,使用并行操作和异步调用来提高任务的并发度和响应性。

  10. 监控和调优:使用Spark的监控工具和日志信息来监控应用程序的性能和资源使用情况,进行调优和优化。

牌,性能更高。血统和依赖关系记录对于Spark的数据恢复和容错非常重要。

spark优化

Spark应用程序的性能和效率可以通过以下几种方式进行优化:

  1. 数据压缩与序列化:使用压缩算法和序列化机制可以减少数据在网络传输和存储中的大小,提高数据的传输效率和存储效率。

  2. 广播变量(Broadcast Variables):对于较小的数据集,可以将其广播到所有的工作节点上,避免重复传输,提高性能。

  3. 避免数据倾斜(Data Skew):监控和处理数据倾斜问题,例如通过使用随机前缀或重新分区等方式来均衡数据分布,避免某个任务或分区处理过多数据。

  4. 分区和并行度设置:根据数据量和集群资源情况,合理设置数据的分区数和并行度,以充分利用集群资源并提高任务执行效率。

  5. 使用合适的缓存策略:通过使用缓存(Caching)机制,将中间结果或常用数据缓存到内存中,减少计算和读取的开销。

  6. 使用合适的算子和转换操作:选择合适的算子和转换操作,尽可能使用窄依赖的操作来减少洗牌(Shuffle)操作,减少数据的传输和重组。

  7. 数据分区优化:根据数据的访问模式和计算需求,进行数据的合理分区,以提高数据的局部性和减少洗牌操作。

  8. 内存管理和调优:合理配置Spark的内存分配和使用,包括调整驱动程序和Executor的内存大小、使用合适的内存管理模式和GC策略等,以提高内存利用率和减少GC开销。

  9. 并行操作与异步调用:在适当的情况下,使用并行操作和异步调用来提高任务的并发度和响应性。

  10. 监控和调优:使用Spark的监控工具和日志信息来监控应用程序的性能和资源使用情况,进行调优和优化。

这些是一些常见的Spark优化技巧和策略,具体的优化方法和策略会根据应用程序的需求和环境的不同而有所差异。通过综合考虑数据处理、资源利用、并发度和网络传输等因素,可以持续优化Spark应用程序的性能和效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

混分吴老师

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

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

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

打赏作者

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

抵扣说明:

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

余额充值