Flink面试突击

大数据方面的面试总结汇总,本篇为Flink的面试总结。

一、简单介绍一下 Flink

Flink 是一个框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算。并且 Flink 提供了数据分布容错机制以及资源管理等核心功能。

Flink提供了诸多高抽象层的API以便用户编写分布式任务:

  • DataSet API,对静态数据进行批处理操作,将静态数据抽象成分布式的数据集,用户可以方便地使用Flink提供的各种操作符对分布式数据集进行处理,支持Java、Scala和Python。
  • DataStream API,对数据流进行流处理操作,将流式的数据抽象成分布式的数据流,用户可以方便地对分布式数据流进行各种操作,支持Java和Scala。
  • Table API,对结构化数据进行查询操作,将结构化数据抽象成关系表,并通过类SQL的DSL对关系表进行各种查询操作,支持Java和Scala。

此外,Flink 还针对特定的应用领域提供了领域库,例如: Flink ML,Flink 的机器学习库,提供了机器学习Pipelines API并实现了多种机器学习算法。 Gelly,Flink 的图计算库,提供了图计算的相关API及多种图计算算法实现。

Flink的特性包括:

  • 支持高吞吐、低延迟、高性能的流处理
  • 支持带有事件时间的窗口 (Window) 操作
  • 支持有状态计算的 Exactly-once 语义
  • 支持基于 time、count、session 以及 data-driven 的窗口操作
  • 支持具有 Backpressure 功能的持续流模型
  • 支持基于轻量级分布式快照(Snapshot)实现的容错
  • 同时支持 Batch on Streaming 处理和 Streaming 处理
  • Flink 在 JVM 内部实现了自己的内存管理支持迭代计算支持程序自动优化:避免特定情况下 Shuffle、排序等昂贵操作,中间结果有必要进行缓存

二、Flink 相比传统的 Spark Streaming 区别?

这个问题是一个非常宏观的问题,因为两个框架的不同点非常之多。但是在面试时有非常重要的一点一定要回答出来:Flink 是标准的实时处理引擎,基于事件驱动。而 Spark Streaming 是微批(Micro-Batch)的模型

下面我们就分几个方面介绍两个框架的主要区别:

  1. 架构模型

    Spark Streaming 在运行时的主要角色包括:Master、Worker、Driver、Executor

    Flink 在运行时主要包含:Jobmanager、Taskmanager和Slot

  2. 任务调度

    Spark Streaming 连续不断的生成微小的数据批次,构建有向无环图DAG,Spark Streaming 会依次创建 DStreamGraph、JobGenerator、JobScheduler。

    Flink 根据用户提交的代码生成 StreamGraph,经过优化生成 JobGraph,然后提交给 JobManager进行处理,JobManager 会根据 JobGraph 生成 ExecutionGraph,ExecutionGraph 是 Flink 调度最核心的数据结构,JobManager 根据 ExecutionGraph 对 Job 进行调度。

  3. 时间机制

    Spark Streaming 支持的时间机制有限,只支持处理时间

    Flink 支持了流处理程序在时间上的三个定义:处理时间、事件时间、注入时间。同时也支持 watermark 机制来处理滞后数据。

  4. 容错机制

    对于 Spark Streaming 任务,我们可以设置 checkpoint,然后假如发生故障并重启,我们可以从上次 checkpoint 之处恢复,但是这个行为只能使得数据不丢失,可能会重复处理,不能做到恰好一次处理语义。

    Flink 则使用两阶段提交协议来解决这个问题。

三、为什么说 Flink 统一了流和批处理?

因为flink无论是批处理还是流处理,底层都是有状态的流处理,flink执行批处理实际上是流处理的一种特例,只不过此时的流式有界的,而流处理的流式无界的,应用于流处理上的transformation完全可以应用在batch上并且table API和sql都可以用在批处理和流处理上,只不过区别在于:

  1. 容错并不是采用的流式处理的checkpoint,而是直接重新计算
  2. dataset api 处理的数据是很简单的数据结构,而stream处理的是key/value
  3. 流处理在应用 transformation 和 table api 和 sql 的时候不支持topN、limit、sort普通字段等操作

另外从计算模型上来说:批处理每个stage只有完全处理完才会把缓存中(缓存+磁盘)序列化的数据发往下一个stage,而流处理是一条一条,批处理吞吐量大,流处理时效性强,而flink则是采用了折中的方式,在内存中划分缓冲小块,当小块满了就发往下一个stage。如果缓存块无限大,那么就是批处理了

四、Flink是如何支持批流一体的?

在这里插入图片描述

本道面试题考察的其实就是一句话:Flink的开发者认为批处理是流处理的一种特殊情况。批处理是有限的流处理。Flink 使用一个引擎支持了DataSet API 和 DataStream API

五、Flink 的运行必须依赖 Hadoop组件吗?

Flink可以完全独立于Hadoop,在不依赖Hadoop组件下运行。但是做为大数据的基础设施,Hadoop体系是任何大数据框架都绕不过去的。

Flink可以集成众多Hadooop 组件,例如Yarn、Hbase、HDFS等等。例如,Flink可以和Yarn集成做资源调度,也可以读写HDFS,或者利用HDFS做检查点。

六、Flink的并行度了解吗?Flink的并行度设置是怎样的?

Flink中的任务被分为多个并行任务来执行,其中每个并行的实例处理一部分数据。这些并行实例的数量被称为并行度。我们在实际生产环境中可以从四个不同层面设置并行度:

  • 操作算子层面 (Operator Level)
  • 执行环境层面 (Execution Environment Level)
  • 客户端层面 (Client Level)
  • 系统层面 (System Level)

需要注意的优先级:算子层面>环境层面>客户端层面>系统层面

七、Flink的基础编程模型了解吗?

在这里插入图片描述
上图是来自Flink官网的运行流程图。通过上图我们可以得知:

Flink 程序的基本构建是数据输入来自一个 Source,Source 代表数据的输入端,经过 Transformation 进行转换,然后在一个或者多个Sink接收器中结束

数据流(stream)就是一组永远不会停止的数据记录流,而转换(transformation)是将一个或多个流作为输入,并生成一个或多个输出流的操作。执行时,Flink程序映射到 streaming dataflows,由流(streams)和转换操作(transformation operators)组成。

八、Flink集群有哪些角色?各自有什么作用?

在这里插入图片描述
Flink 程序在运行时主要有 TaskManager,JobManager,Client三种角色。

  • JobManager扮演着集群中的管理者Master的角色,它是整个集群的协调者,负责接收Flink Job,协调检查点,Failover故障恢复等,同时管理Flink集群中从节点TaskManager。

    1). JobManager 接收待执行的 application。application 包含一个 JobGraph 和 JAR (包含所有需要的classes,libraries 和其他资源)。

    2). JobManager 将 JobGraph 转成 ExecutionGraph,ExecutionGraph中包含可以并发执行的 tasks。

    3). JobManager 向 ResourceManager 申请需要的资源(TaskManager slots),一旦分配到足够的slots,则分发 tasks 到 TaskManager 执行。

    4). 执行期间,JobManager 负责中央协调,如协调checkpoint等

  • TaskManager是实际负责执行计算的Worker,在其上执行Flink Job的一组Task,每个TaskManager负责管理其所在节点上的资源信息,如内存、磁盘、网络,在启动的时候将资源的状态向JobManager汇报

    1). 启动之后,TaskManager 向 ResourceManager 注册 slots 数,当接收到 ResourceManager 的分配通知后,会向 JobManager 提供一个或多个slots。

    2). 紧接着 JobManager 将 tasks 分配到 slots 执行。

    3). 执行期间,不同的 TaskManager 之间会进行数据交换

  • Client是Flink程序提交的客户端,当用户提交一个Flink程序时,会首先创建一个Client,该Client首先会对用户提交的Flink程序进行预处理,并提交到Flink集群中处理,所以Client需要从用户提交的Flink程序配置中获取JobManager的地址,并建立到JobManager的连接,将Flink Job提交给JobManager。

九、Flink的架构?

主从结构Jobmanager,taskmanager两个进程(可以把client也加进去)。
集群模式standalone,on yarn(在yarn上运行一个flink集群/提交到yarn上运行flink job)

(1)Jobmanager:

  • registerTaskManager:在flink集群启动时,taskmanager会向jobmanager注册
  • submitjob:flink程序内部通过client向jobmanager提交job,job是以jobgraph形式提交
  • canceljob:请求取消一个flinkjob
  • updateTaskExcutionStage:更新taskmanager中excution的状态信息
  • requestnextinputsplit:运行在taskmanager上的task请求获取下一个要处理的split
  • jobstatuschanged:executionGraph向jobmanager发送该消息,用来表示job的状态变化

(2)Taskmanager:

  • 注册:向jobmnager注册自己
  • 可操作阶段:该阶段taskmanager可以接受并处理与task有关的消息

(3)client:

client对用户提交的代码进行预处理,client将程序组装成一个 jobgraph,它是由多个jobvertex组成的DAG。

十、Flink 的组件栈有哪些?

根据 Flink 官网描述,Flink 是一个分层架构的系统,每一层所包含的组件都提供了特定的抽象,用来服务于上层组件。

在这里插入图片描述
自下而上,每一层分别代表:

  • Deploy 层:该层主要涉及了Flink的部署模式,在上图中我们可以看出,Flink 支持包括local、Standalone、Cluster、Cloud等多种部署模式
  • Runtime 层:Runtime层提供了支持 Flink 计算的核心实现,比如:支持分布式 Stream 处理、JobGraph到ExecutionGraph的映射、调度等等,为上层API层提供基础服务。
  • API层:API 层主要实现了面向流(Stream)处理和批(Batch)处理API,其中面向流处理对应DataStream API,面向批处理对应DataSet API,后续版本,Flink有计划将DataStream和DataSet API进行统一。
  • Libraries层:该层称为Flink应用框架层,根据API层的划分,在API层之上构建的满足特定应用的实现计算框架,也分别对应于面向流处理和面向批处理两类。面向流处理支持:CEP(复杂事件处理)、基于SQL-like的操作(基于Table的关系操作);面向批处理支持:FlinkML(机器学习库)、Gelly(图处理)。

十一、Flink的 Checkpoint 机制详细讲一下?注意与spark的区别?

Flink 实现容错主要靠强大的 CheckPoint 机制和 State 机制。Checkpoint 负责定时制作分布式快照、对程序中的状态进行备份;State 用来存储计算过程中的中间状态

Flink是通过checkpoint机制实现容错,它的原理是不断的生成分布式streaming数据流snapshot快照。在流处理失败时通过这些snapshot可以恢复数据流处理。而flink的快照有两个核心:

  • barrier 机制:barrier是实现checkpoint的机制。
  • state 状态保存:state保存则是通过barrier这种机制进行 分布式快照 的实现。

1. barrier

barrier是checkpoint的核心,他会当做记录打入数据流,从而将数据流分组,并沿着数据流方向向前推进,每个barrier会携带一个snapshotID,属于该snapshot的记录会被推向该barrier的前方。所以barrier之后的属于下一个ckeckpoint期间(snapshot中)的数据。

然后当中间的operation接收到barrier后,会发送barrier到属于该barrier的snapshot的数据流中,等到sink operation接收到该barrier后会向checkpoint coordinator确认该snapshot,直到所有的sink operation都确认了该snapshot,才会认为完成了本次checkpoint或者本次snapshot。

理解:可以认为barrier这种机制是flink实现分布式快照的手段。那么在这个过程中要记录state快照信息,到底有哪些信息需要序列话呢?

在说state保存之前我们要知道flink的三种方式,

  • jobmanager内存,不建议;
  • hdfs(可以使用,同步 进行分布式快照);
  • rocksDB(异步 进行分布式快照)。

除了第3种其他两种都是同步快照。也就是说用hdfs这种方式快照是会阻塞数据处理的,只有当两个barrier之间数据处理完成并完成快照之后才向下一个task发送数据并打入barrier n。我们不管异步快照,我们现在只说同步快照。

2. state状态保存

state状态保存分为两种:

  • 一种是用户自定义状态:也就是我们为了实现需求敲的代码(算子),他们来创建和修改的state;
  • 一种是系统状态:此状态可以认为数据缓冲区,比如window窗口函数,我们要知道数据处理的情况。

生成的快照现在包含:

  • 对于每个并行流数据源,创建快照时流中的偏移/位置
  • 对于每个运算符,存储在快照中的状态指针

3. stream aligning (barrier k对齐)

这个情况出现的很少,用于解决同一个Operation处理多个输入流的情况(不是同一个数据源),这种情况下operation将先收到barrier k的数据缓存起来不进行处理,只有当另一个流的barrier k到达之后再进行处理,同时opeartion会向checkpoint coordinator上报snapshot。这就是barrier k对齐

SparkCheckpoint

spark的checkpoint的方式没有这么复杂,直接通过记录metadata和data的方式来进行checkpoint。从checkpoint中恢复时是决不允许修改代码的,而sss是有些情况可以接受修改代码的。

  1. metadata checkpoint ,将定义流式计算的信息保存到hdfs:配置、dstream操作、尚未完成的批次
  2. data checkpoint , 这就比较直接了,直接持久化RDD到hdfs,因为我们知道spark的容错就是基于rdd的血缘关系的,而为了避免依赖关系链太长,spark会定期从最新的rdd中持久化数据到hdfs。

注意:如果spark程序中没有updateStateByKeyreduceByKeyAndWindow这种带有状态持续改变的算子操作的时候完全可以不用对rdd进行持久化,只需要利用metadata来恢复程序即可,因为数据的丢失时可以接受的,但是如果存在状态转换的算法就不行了。

十二、Flink 分布式快照 Checkpoint 的原理是什么?

Flink的分布式快照是根据Chandy-Lamport算法量身定做的。简单来说就是持续创建分布式数据流及其状态的一致快照
在这里插入图片描述
核心思想:是在 input source 端插入 barrier,控制 barrier 的同步来实现 snapshot 的备份exactly-once 语义

十三、Flink Checkpoint 常见失败原因分析?

Flink Checkpoint 失败有很多种原因,常见的失败原因如下:

  • 用户代码逻辑没有对于异常处理,让其直接在运行中抛出。比如解析 Json 异常,没有捕获,导致 Checkpoint失败,或者调用Dubbo 超时异常等等。
  • 依赖外部存储系统,在进行数据交互时,出错,异常没有处理。比如输出数据到 Kafka、Redis、HBase等,客户端抛出了超时异常,没有进行捕获,Flink 任务容错机制会再次重启。
  • 内存不足,频繁GC,超出了 GC 负载的限制。比如 OOM 异常
  • 网络问题、机器不可用问题等等。

从目前的具体实践情况来看,Flink Checkpoint 异常绝大多数还是用户代码逻辑的问题,对于程序异常没有正确的处理导致。所以在编写 Flink 实时任务时,一定要注意处理程序可能出现的各种异常。这样,也会让实时任务的逻辑更加的健壮。

十四、Flink 是如何保证Exactly-once语义的?

Flink通过实现 两阶段提交状态保存 来实现 端到端 的一致性语义。 分为以下几个步骤:

  • 开始事务(beginTransaction)创建一个临时文件夹,来写把数据写入到这个文件夹里面
  • 预提交(preCommit)将内存中缓存的数据写入文件并关闭
  • 正式提交(commit)将之前写完的临时文件放入目标目录下。这代表着最终的数据会有一些延迟
  • 丢弃(abort)丢弃临时文件

十五、如果下级存储不支持事务,Flink 怎么保证 exactly-once?

端到端的 exactly-once 对 sink 要求比较高,具体实现主要有幂等写入事务性写入两种方式。

  • 幂等写入的场景依赖于业务逻辑,更常见的是用事务性写入。而事务性写入又有预写日志(WAL)和两阶段提交(2PC)两种方式
  • 如果外部系统不支持事务,那么可以用预写日志的方式,把结果数据先当成状态保存,然后在收到 checkpoint 完成的通知时,一次性写入 sink 系统。

十六、Flink 中的 Time 有哪几种?

Flink中的时间有三种类型,如下图所示:
在这里插入图片描述

  • Event Time:是事件创建的时间。它通常由事件中的时间戳描述,例如采集的日志数据中,每一条日志都会记录自己的生成时间,Flink通过时间戳分配器访问事件时间戳。
  • Ingestion Time:是数据进入Flink的时间
  • Processing Time:是每一个执行基于时间操作的算子的本地系统时间,与机器相关,默认的时间属性就是Processing Time。

例如,一条日志进入Flink的时间为2021-01-22 10:00:00.123,到达Window的系统时间为2021-01-22 10:00:01.234,日志的内容如下:

2021-01-06 18:37:15.624 INFO Fail over to rm2

对于业务来说,要统计1min内的故障日志个数,哪个时间是最有意义的?—— eventTime,因为我们要根据日志的生成时间进行统计。

十七、Flink对于迟到数据是怎么处理的?

Flink中 WaterMarkWindow 机制解决了流式数据的乱序问题,对于因为延迟而顺序有误的数据,可以根据eventTime进行业务处理,对于延迟的数据Flink也有自己的解决办法,主要的办法是给定一个允许延迟的时间,在该时间范围内仍可以接受处理延迟数据

  • 设置允许延迟的时间是通过allowedLateness(lateness: Time)设置
  • 保存延迟数据则是通过sideOutputLateData(outputTag: OutputTag[T])保存
  • 获取延迟数据是通过DataStream.getSideOutput(tag: OutputTag[X])获取

十八、Flink 资源管理中 Task Slot 的概念

在Flink中每个TaskManager是一个JVM的进程, 可以在不同的线程中执行一个或多个子任务。 为了控制一个worker能接收多少个task。worker通过task slot(任务槽)来进行控制(一个worker至少有一个task slot)。

简单的说,TaskManager 会将自己节点上管理的资源分为不同的 Slot:固定大小的资源子集。这样就避免了不同 Job 的 Task 互相竞争内存资源,但是需要主要的是,Slot 只会做内存的隔离。没有做 CPU 的隔离

TaskSlot 与 Parallelism的关系?

Slot 是指 TaskManager 最大能并发执行 的能力。
parallelism 是指 TaskManager 实际使用的并发能力。也是 Flink-Job 的实际并发能力。

十九、Flink的重启策略了解吗

Flink支持不同的重启策略,这些重启策略控制着job失败后如何重启:

  1. 固定延迟重启策略

    固定延迟重启策略会尝试一个给定的次数来重启Job,如果超过了最大的重启次数,Job最终将失败。在连续的两次重启尝试之间,重启策略会等待一个固定的时间。

  2. 失败率重启策略

    失败率重启策略在Job失败后会重启,但是超过失败率后,Job会最终被认定失败。在两个连续的重启尝试之间,重启策略会等待一个固定的时间。

  3. 无重启策略

    Job直接失败,不会尝试进行重启。

二十、Flink是如何处理反压的?

Flink 内部是基于 producer-consumer 模型来进行消息传递的,Flink的反压设计也是基于这个模型。Flink 使用了高效有界的分布式阻塞队列,就像 Java 通用的阻塞队列(BlockingQueue)一样。下游消费者消费变慢,上游就会受到阻塞。

二十一、Flink中的状态存储?

Flink在做计算的过程中经常需要存储中间状态,来避免数据丢失和状态恢复。选择的状态存储策略不同,会影响状态持久化如何和 checkpoint 交互。Flink提供了三种状态存储方式:MemoryStateBackendFsStateBackendRocksDBStateBackend

二十二、Flink的内存管理是如何做的?

Flink 并不是将大量对象存在堆上,而是将对象都序列化到一个预分配的内存块上。此外,Flink大量的使用了堆外内存。如果需要处理的数据超出了内存限制,则会将部分数据存储到硬盘上。Flink 为了直接操作二进制数据实现了自己的序列化框架。

理论上 Flink 的内存管理分为三部分:

  • Network Buffers:这个是在 TaskManager 启动的时候分配的,这是一组用于缓存网络数据的内存,每个块是32K,默认分配 2048 个,可以通过“taskmanager.network.numberOfBuffers”修改
  • Memory Manage pool:大量的 Memory Segment 块,用于运行时的算法(Sort/Join/Shuffle等),这部分启动的时候就会分配。下面这段代码,根据配置文件中的各种参数来计算内存的分配方法。(heap or off-heap,这个放到下节谈),内存的分配支持预分配和 lazy load,默认懒加载的方式。
  • User Code,这部分是除了 Memory Manager 之外的内存用于 User code 和 TaskManager
    本身的数据结构。

二十三、Flink CEP 编程中当状态没有到达的时候会将数据保存在哪里

在流式处理中,CEP 当然是要支持 EventTime 的,那么相对应的也要支持数据的迟到现象,也就是watermark的处理逻辑。CEP对未匹配成功的事件序列的处理,和迟到数据是类似的。在 Flink CEP的处理逻辑中,状态没有满足的和迟到的数据,都会存储在一个Map数据结构中,也就是说,如果我们限定判断事件序列的时长为5分钟,那么内存中就会存储5分钟的数据,这在我看来,也是对内存的极大损伤之一。

二十四、Flink中 window 出现数据倾斜怎么解决?

window 产生数据倾斜指的是数据在不同的窗口内堆积的数据量相差过多。本质上产生这种情况的原因是数据源头发送的数据量速度不同导致的。出现这种情况一般通过两种方式来解决:

  • 在数据进入窗口前做预聚合
  • 重新设计窗口聚合的 key

二十五、Flink 任务延时高,如何入手?

在 Flink 的后台任务管理中,我们可以看到 Flink 的哪个算子和 task 出现了反压。

最主要的手段是资源调优算子调优

  • 资源调优即是对作业中的 Operator的并发数(parallelism)、CPU(core)、堆内存(heap_memory)等参数进行调优。
  • 作业参数调优包括:并行度的设置,State 的设置,checkpoint 的设置。

二十六、Flink 计算资源调度如何实现?

TaskManager 中最细粒度的资源是 Task slot,代表了一个固定大小的资源子集,每个 TaskManager 会将其所占有的资源平分给它的 slot。

通过调整 task slot 的数量,用户可以定义 task 之间是如何相互隔离的。每个 TaskManager 有一个 slot,也就意味着每个 task 运行在独立的 JVM 中。每个 TaskManager 有多个 slot 的话,也就是说多个 task 运行在同一个 JVM 中。

而在同一个 JVM 进程中的 task,可以共享 TCP 连接(基于多路复用)和心跳消息,可以减少数据的网络传输,也能共享一些数据结构,一定程度上减少了每个 task 的消耗。 每个 slot 可以接受单个 task,也可以接受多个连续 task 组成的 pipeline,如下图所示,FlatMap 函数占用一个 taskslot,而 key Agg 函数和 sink 函数共用一个 taskslot。

二十七、简单说说FlinkSQL的是如何实现的?

Flink 将 SQL 校验、SQL 解析以及 SQL 优化交给了Apache Calcite。Calcite 在其他很多开源项目里也都应用到了,譬如 Apache Hive, Apache Drill, Apache Kylin, Cascading。Calcite 在新的架构中处于核心的地位,如下图所示。
在这里插入图片描述
构建抽象语法树的事情交给了 Calcite 去做。SQL query 会经过 Calcite 解析器转变成 SQL 节点树,通过验证后构建成 Calcite 的抽象语法树(也就是图中的 Logical Plan)。另一边,Table API 上的调用会构建成 Table API 的抽象语法树,并通过 Calcite 提供的 RelBuilder 转变成 Calcite 的抽象语法树。然后依次被转换成逻辑执行计划和物理执行计划。在提交任务后会分发到各个 TaskManager 中运行,在运行时会使用 Janino 编译器编译代码后运行。

二十八、你们的Flink集群规模多大?

大家注意,这个问题看起来是问你实际应用中的Flink集群规模,其实还隐藏着另一个问题:Flink可以支持多少节点的集群规模?

在回答这个问题时候,可以将自己生产环节中的集群规模、节点、内存情况说明,同时说明部署模式(一般是Flink on Yarn),除此之外,用户也可以同时在小集群(少于5个节点)和拥有 TB 级别状态的上千个节点上运行 Flink 任务。

参考:

flink常见面试题

Flink进入大厂面试准备,收藏这一篇就够了

Flink 面试题

Flink面试题

Flink吐血总结,学习与面试收藏这一篇就够了!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值