Spark SQL优化方案总结

SparkSQL优化查询性能的方法 需要注意的是,不同的连接方式适用于不同的数据集大小和查询场景,具体的选择需要根据实际情况进行判断。另外,还可以使用SparkSQL中的调优参数和配置,如。SparkSQL中有几种方法,可以用于优化查询性能。等,来优化查询性能。 阅读详情

Spark SQL 的优化是一个多层面的工作,涉及资源管理、查询编写、数据存储、配置调优等多个方面。以下是一些关键的优化方案,涵盖了不同层次:

🧩 一、 资源与配置优化

  1. 合理分配资源:

    • spark.executor.memory: 为 Executor 分配足够的内存,避免频繁的磁盘溢出(Spill)或 OOM。考虑堆外内存 (spark.executor.memoryOverhead)。
    • spark.executor.cores: 根据任务类型(CPU密集型 vs IO密集型)和集群资源设置每个 Executor 的核心数。通常 3-5 个核心是一个较好的平衡点。
    • spark.executor.instances: 确定 Executor 的总数量,充分利用集群资源。
    • spark.driver.memory: 如果 Driver 需要处理大量数据(如 collect()),确保其内存足够。
    • spark.sql.shuffle.partitions: 非常重要! 控制 Shuffle 操作(如 Join, Group By, Order By)后的分区数。默认值(200)通常太低,导致每个分区数据量过大(容易OOM)或 Executor 资源利用不足。应根据数据量和集群规模调大(例如 1000, 2000 甚至更高),目标是使每个分区的数据量在 100MB - 200MB 左右。
    • spark.default.parallelism: 设置 RDD 转换操作的默认并行度,通常设置为集群核心总数的 2-3 倍。对于 Spark SQL,spark.sql.shuffle.partitions 更常用。
  2. 利用动态资源分配:

    • 设置 spark.dynamicAllocation.enabled=true。Spark 可以根据工作负载动态地申请和释放 Executor,提高集群资源利用率,特别是在共享集群上。
  3. 内存管理:

    • 理解 Unified Memory Management:Spark 统一管理执行内存(Execution,用于 Shuffle、Join、Sort)和存储内存(Storage,用于 Cache/Broadcast)。调整 spark.memory.fraction(默认 0.6)和 spark.memory.storageFraction(默认 0.5)可以平衡两者比例。
    • 使用堆外内存 (spark.memory.offHeap.enabled, spark.memory.offHeap.size) 处理非常大的数据集或减少 GC 开销。
  4. 垃圾回收 (GC) 优化:

    • 对于大内存 Executor,使用 G1GC (-XX:+UseG1GC) 通常比默认的 Parallel GC 表现更好,因为它能更好地处理大堆内存和减少暂停时间。
    • 监控 GC 日志 (-XX:+PrintGCDetails -XX:+PrintGCDateStamps),如果 Full GC 频繁或时间长,需要优化内存使用或 GC 配置。

📝 二、 查询编写与逻辑优化

  1. 谓词下推 (Predicate Pushdown):

    • Spark SQL(通过 Catalyst 优化器)会自动尝试将过滤条件(WHERE 子句)下推到数据源层(如 Parquet、ORC、JDBC),尽可能早地过滤掉不需要的数据,减少 I/O 和后续处理的数据量。确保你的查询条件清晰可下推。
  2. 列裁剪 (Column Pruning):

    • Catalyst 优化器会自动分析查询,只读取查询中实际引用的列。对于列式存储(Parquet, ORC)尤其高效。在编写 SQL 时,避免 SELECT *,明确列出需要的列。
  3. 分区裁剪 (Partition Pruning):

    • 如果你的表是按照某个字段(如日期 dt、地区 region)分区的,在 WHERE 子句中使用分区字段进行过滤,Spark 只会读取满足条件的分区目录,极大减少 I/O。确保分区字段的选择合理。
  4. 避免笛卡尔积 (Cartesian Product):

    • 显式的 CROSS JOIN 或没有连接条件的 Join 会产生巨大的中间结果,性能极差。务必确保 Join 有有效的连接条件。
  5. 优化 Join 策略:

    • 广播哈希 Join (Broadcast Hash Join): 当一个小表(默认 < 10MB,可通过 spark.sql.autoBroadcastJoinThreshold 调整)和一个大表 Join 时,Spark 会将小表广播到所有 Executor。这是最高效的 Join 方式。确保小表确实足够小。
    • Sort Merge Join: 默认用于两个大表的等值 Join。要求数据在 Join Key 上已分区且排序才能发挥最佳性能。Catalyst 通常会自动处理排序,但确保 spark.sql.shuffle.partitions 设置合理以避免数据倾斜。
    • 倾斜 Join 优化 (Skew Join Optimization):
      • 如果某个 Join Key 的值异常多(数据倾斜),会导致某些 Task 处理时间远长于其他 Task(长尾效应)。
      • 手动拆分:识别倾斜 Key,将其对应的数据单独处理,再与非倾斜数据合并。
      • 自动优化 (AQE):Spark 3.0+ 的 AQE 可以自动检测运行时的数据倾斜,并将倾斜的分区分割成更小的子分区进行处理,需设置 spark.sql.adaptive.skewJoin.enabled=true。
  6. 优化聚合 (Group By/Aggregation):

    • 类似 Join,spark.sql.shuffle.partitions 影响聚合的并行度。
    • 如果聚合 Key 存在倾斜,AQE (spark.sql.adaptive.skewJoin.enabled=true) 也能帮助优化。
    • 考虑使用近似聚合函数(如 approx_count_distinct)代替精确的 count(distinct) 以换取性能,如果业务能接受轻微误差。
  7. 合理使用 Cache/Persist:

    • 如果一个 DataFrame/RDD 会被多次使用(尤其是在迭代算法或交互式查询中),将其缓存(df.cache() / df.persist())可以避免重复计算。
    • 谨慎使用! 缓存占用宝贵内存。只缓存真正需要复用的中间结果,并在不再需要时使用 df.unpersist() 释放。评估缓存带来的收益是否大于内存成本。
  8. 使用 Broadcast 变量:

    • 对于需要在 Task 中频繁访问的只读小数据集(如 lookup 表、配置字典),将其创建为广播变量 (sparkContext.broadcast()) 比直接封装在闭包中更高效,因为数据只传输一次到每个 Executor。
  9. 避免或优化 distinct 和 order by:

    • distinct 和全局的 order by(没有 limit)通常需要昂贵的 Shuffle 操作。考虑是否真的需要全局去重或全局排序,或者能否用 group by 或窗口函数替代部分逻辑。结合 limit 的 order by 可能更快。

💾 三、 数据存储优化

  1. 选择高效的文件格式:

    • 列式存储: Parquet 和 ORC 是首选。它们支持高效的压缩(节省存储空间和 I/O)、谓词下推、列裁剪,非常适合分析型查询。Spark 读写它们有高度优化。
    • 压缩: 在存储文件时启用合适的压缩算法(如 Snappy, Gzip, Zstd)。Snappy 提供较好的压缩比和速度平衡,Gzip/Zstd 压缩比更高但可能稍慢。设置 spark.sql.parquet.compression.codec 等。
  2. 文件大小与数量:

    • 避免大量小文件(“small file problem”)。这会显著增加 Namenode 压力和任务启动开销。在写入前使用 coalesce() 或 repartition() 控制输出文件的数量和大小(理想大小通常是 HDFS block size 的倍数,如 128MB-1GB)。
    • 避免单个巨大的文件,这不利于并行处理。使用 repartition() 或 distribute by / cluster by (在 Databricks SQL 等中) 在写入时根据业务逻辑重新分区。
  3. 分区与分桶:

    • 分区: 按常用过滤字段(日期、类别等)对表进行分区,利用分区裁剪。
    • 分桶: 根据 Join Key 或 Group By Key 将数据划分为固定数量的桶(文件)。这可以显著加速相同 Key 上的 Join 和聚合,因为相同 Key 的数据已经物理上聚集在相同的桶文件里。设置 spark.sql.sources.bucketing.enabled=true。CLUSTERED BY ... INTO ... BUCKETS。分桶通常需要与高效的文件格式(Parquet/ORC)结合使用。

⚙ 四、 利用高级特性 (Spark 3.x 及以上)

  1. 自适应查询执行 (Adaptive Query Execution - AQE):

    • Spark 3.0 的核心优化! 强烈建议启用 (spark.sql.adaptive.enabled=true)。
    • 动态合并 Shuffle 分区: 根据 Shuffle 后的实际数据量,自动调整下游的分区数,避免过多小分区或过少的大分区。
    • 动态切换 Join 策略: 在运行时,如果发现广播的表实际大小超过了阈值,可以自动将 Broadcast Join 降级为 Sort Merge Join,避免 OOM。
    • 动态优化数据倾斜 Join: 自动检测并处理 Join 中的数据倾斜问题(如前所述)。
  2. 动态分区裁剪 (Dynamic Partition Pruning - DPP):

    • 当事实表(大)与维度表(小,且过滤后更小)Join,且 Join Key 是事实表的分区键时,AQE 可以将维度表过滤的结果(分区列表)动态应用到事实表的扫描上,实现高效的运行时分区裁剪。需要满足特定条件(小表过滤后有结果,Join Key 是大表分区键)。

🛠 五、 监控与分析

  1. 分析执行计划:

    • 使用 df.explain(mode="formatted") 或 df.explain(mode="extended") 查看 Spark SQL 生成的逻辑计划、物理计划以及优化后的计划。关注 FileScan(I/O)、Exchange(Shuffle)、Sort 等操作的成本。
    • 使用 Spark UI(端口 4040):
      • Stages/Tasks: 查看各 Stage 和 Task 的执行时间、Shuffle 读写量、GC 时间、数据倾斜(任务执行时间差异大)情况。
      • SQL: 查看 SQL 查询的 DAG 图、各物理操作的耗时、指标(行数、字节数)。
      • Storage: 查看缓存的数据和内存使用情况。
      • Environment: 确认配置生效情况。
      • Executor: 查看 Executor 的资源使用(CPU、内存)、GC 情况。
  2. 使用 Profiling 工具:

    • Spark 内置的指标系统或外部 APM 工具(如 Prometheus + Grafana)进行更细致的性能监控和分析。

📌 总结

优化 Spark SQL 是一个持续迭代的过程:

  1. 监控先行: 利用 Spark UI 和日志识别性能瓶颈(是 I/O 慢?Shuffle 数据量大?GC 严重?数据倾斜?)。
  2. 基础配置: 确保资源(内存、CPU、并行度 spark.sql.shuffle.partitions)分配合理。
  3. 启用 AQE: 对于 Spark 3.x,这是最重要的开箱即用优化。
  4. 数据存储: 使用列式格式(Parquet/ORC),合理分区/分桶,管理文件大小。
  5. 查询优化: 编写高效 SQL(谓词下推、列裁剪、避免笛卡尔积、慎用全局排序去重),利用广播 Join,缓存策略得当。
  6. 高级特性: 针对数据倾斜等问题应用特定优化。
  7. 再次监控验证: 检查优化后的效果。

没有放之四海而皆准的"最佳配置",最优策略取决于你的具体数据规模、集群环境、查询负载和工作流特点。持续监控、分析和实验是关键!🚀

SparkSQL-性能调优 在这个举国同庆的时刻,我们首先献上对祖国的祝福:第二,祝福我们国家未来的路越走越宽广,科技更发达,人民更幸福第三,我们会紧紧跟随您的脚步,一起为美好的未来奋斗。 阅读详情

相关推荐

Spark SQL 自动缓存重复 Table Scan 实现解析:基于 AQE 扩展的 2 种优化方案

本文深入解析了Spark SQL中基于AQE扩展的两种自动缓存重复Table Scan优化方案,通过逻辑计划优化与物理计划运行时优化,有效解决了大规模数据处理中的性能瓶颈问题。文章详细探讨了缓存识别算法、粒度选择策略及与AQE的协同机制,为开发者提供了可落地的解决方案。

weixin_27244795的博客 255

SparkSQL优化

主要介绍了sparksql执行计划查看,参数优化,SQL优化(HIVE适用)等内容

me_to_007的博客 1万+

Spark SQL调优案例:单字段NOT IN子查询优化

背景 有如下的数据查询场景。 表结构 create table order (order_id long, customer_id long) partitioned by (date string); 要查询当天有订单,但是前一天没有订单的用户在当天的订单记录。 数据分析的同事编写的最初版本的SQL如下 select * from order where date='2021-07-17' and customer_id not in ( select customer_id fr

hammertank的博客 3015

sparkSQL-优化

用于设置Shuffle过程中的分区数量。:用于设置自动广播Join操作的阈值,超过该阈值的大表将不会被广播到所有的Executor节点。:用于设置是否启用排序合并连接算法,默认为true。:用于启用或禁用SparkSQL的优化器,默认为false。:用于设置是否启用堆外内存存储列式数据,默认为false。

互联网知识分享 1367

025、分布式计算实战:Spark Core与Spark SQL

Spark用起来像开车——自动挡简单,但想开得快还得懂手动模式。别迷信DataFrame API就一定比RDD快,复杂的多阶段处理里,RDD的精细控制反而更有效。生产环境永远先跑小样本数据,看看执行计划再全量跑。遇到数据倾斜别怕,sample()取样分析key分布,针对性打散。记住,集群资源是有限的,最优雅的实现往往不是最高效的,在简洁性和性能之间找到平衡点,这才是工程师的价值。下次我们聊聊Spark Streaming的容错机制,那又是另一个深夜故事了。

xiaoyaohou11的博客 998

SparkSQL性能优化终极篇

随着Spark版本的不断迭代,SparkSQL底层使用了各种优化技术,无论方便性、性能都已经超越了RDD。因此SparkSQL已经是最常用的Spark开发方式,因此,这里把常用的SparkSQL性能优化技术进行汇总。

SunnyRivers 2710

spark数据倾斜,spark sql 优化方案

1、SQL 代码层面优化 Step1、阻塞调度的长耗时 SQL 提取 Step2、划分长耗时 SQL Step3、对长耗时 SQL 逐个分析 1.1、数据倾斜优化 1.1.1、sqoop 导入数据时的倾斜: 经过分析发现,数据倾斜是因为sqoop 同步数据时,采用的是sys_time字段进行数据进行split的,客户可能在某一时间对业务数据库进行过导入,从而导致很多数据的sys_time 在同一时间。所以sqoop 导入的数据,即存在了数据倾斜 解决办法: 将sqoop split_by 的..

Lpf的博客 3246

Spark SQL 优化笔记

前言 记录自己在工作开发中遇到的SQL优化问题 1、避免用in 和 not in 解决方案: 用exists 和 not exists代替 用join代替 not exists示例 not in: select stepId,province_code,polyline from route_step where stepId not in (select stepId from stepId...

主要分享大数据相关的知识,如Spark、Hudi 1782

spark3 sql优化:同一个表关联多次,优化方案

合并查询: 用 CASE WHEN 合并多个 JOIN。简化 JOIN 条件: 提前通过 WHERE 子句过滤无效数据。广播连接: 对小表使用 BROADCAST,减少 shuffle 开销。分区和分桶: 对大表进行分区或分桶优化 JOIN 性能。使用 DataFrame API: 在某些复杂查询中,DataFrame API 性能更优。缓存数据: 重复使用的数据可以进行缓存或持久化。避免笛卡尔积: 确保 JOIN 有明确的条件,避免全表扫描。

MyNameIsWangYi的博客 2046

西电计组实验一 存储器实验

计组实验报告 实验一 存储器实验——FPGA中LPM_ROM定制与读出实验 一.实验目的   1.掌握FPGA中lpm_ROM的设置,作为只读存储器ROM的工作特性和配置方法;   2.用文本编辑器编辑mif文件配置ROM,学习将程序代码以mif格式文件加载于lpm_ROM中;   3.在初始化存储器编辑窗口编辑mif文件配置ROM;   4.验证FPGA中mega_lpm_ROM的功能。 二.实验原理   ALTERA的FPGA中有许多可调用的LPM (Library Parameterized Modu

Polaris的博客 1万+

DisplayPort 1.4扰码原理与16位并行LFSR实现

DisplayPort物理层信号完整性依赖于数据扰码技术,其本质是通过线性反馈移位寄存器(LFSR)对传输数据进行伪随机化处理,以改善频谱分布、抑制低频能量堆积、提升时钟恢复能力并降低EMI辐射。该技术广泛应用于高速串行接口如DP、PCIe和USB,核心在于确定性伪随机序列生成与收发端同步。DisplayPort 1.4强制要求符合ANSI X3.230标准的16级LFSR,并在8b/10b编码后执行扰码;而传统串行实现难以满足21.6Gbps速率下的时序约束,必须采用并行展开结构——将16拍移位压缩为单周

weixin_29192211的博客 313

Spark与Hive SQL交互中的优化问题(Spark与Hive执行结果不一致问题)及解决方案

在大数据处理领域,Spark和Hive都是极为重要的工具。Spark凭借其强大的内存计算能力和丰富的编程接口,在数据处理和分析方面表现卓越;而Hive作为数据仓库工具,擅长处理大规模数据的存储和管理,并提供了类似于SQL的查询语言Hive SQL。由于它们的广泛应用,常常需要在同一数据处理流程中协同工作。然而,Spark在对Hive SQL进行优化时,可能会引发一些问题,如类型转换异常和文件格式转换异常等,这些问题在特定场景下会给数据处理工作带来困扰,本文将深入探讨这些问题并提供相应的解决方案。

qq_41631913的博客 1663

手把手教你用Alinx黑金开发板(Spartan-6)驱动ADF4351信号源(附Verilog SPI代码)

本文详细介绍了如何使用Alinx黑金开发板(Spartan-6 FPGA)驱动ADF4351信号源,包括硬件连接、Verilog SPI代码实现及系统调试。通过实战指南,帮助开发者快速掌握FPGA控制ADF4351芯片的技术,适用于射频信号源设计和学习应用。

weixin_30335575的博客 628

Spark SQL

一、SQL on Hadoop SQL是一种传统的用来进行数据分析的标准 Hive:原始的SQL-on-Hadoop解决方案 Impala:和Hive一样,提供了一种可以针对已有Hadoop数据编写SQL查询的方法 Presto:类似于Impala,未被主要供应商支持 Shark:Spark SQL的前身,设计目标是作为Hive的补充 Phoenix:基于HBase的开源SQL查询引擎 二、Spark SQL Spark SQL的前身 Shark是spark sql的前身,它的设计初衷是让Hive运行在

jiandanbuguo的博客 385

Clion+platformIO开发ESP32时找不到lib文件中引入第三方库的头文件(头文件下有波浪线),实际编译是成功的解决方法

Clion+platformIO开发ESP引入第三方库文件编译器无法找到.h文件但编译成功

zs5218的博客 1852

Habitat-baselines v0.1.7防踩坑手把手带你跑通(全网唯一)

Habitat-baselines v0.1.7防踩坑手把手带你跑通(全网唯一)

shannanshouyin的博客 1277

spark sql 中的 笛卡尔积(cross join)优化方法--mapjoin

在编写sql代码时,需要尽量少使用 笛卡尔积,但是有些特殊场景下很难找到代替方案。这里分享了 在进行spark sql 编写任务作业时遇到笛卡尔积导致执行慢的问题,并记录了解决方案

samur2的博客 3638

疲劳容器的定义_FKM规范针对非焊接构件疲劳强度评估实例介绍(下篇)

一、写在前面FKM Inside ANSYS软件,该软件是在FKM 规范的基础上,在ANSYS Workbench内开发的进行非焊接构件及焊缝静强度和疲劳强度评估的工具包。基于ANSYS计算模型的信息,在图形用户界面定义相关设置,利用有限元分析计算结果,自动嵌入FKM规范要求的静强度计算和疲劳强度计算流程和方法,自动将应力结果和相关物理量传递给工具包,无须手动预先选择评估的热点区域,可以...

weixin_35925298的博客 1885

Spark-SQL-on-HBase 项目常见问题解决方案

Spark-SQL-on-HBase 项目常见问题解决方案 项目基础介绍 Spark-SQL-on-HBase 是一个开源项目,旨在通过 Spark SQL 和 DataFrame 接口优化访问 HBase 数据。HBase 是一个分布式 Key-Value 存储系统,运行在 HDFS 上,其数据模型类似于 Google 的 BigTable。该项目的主要目标是提供一种高效、原生的方式来查询 HB...

gitblog_01027的博客 1110

使用python对创芯科技CANalyst-II分析仪进行二次开发(详细教程)

使用python对创芯科技CANalyst-II分析仪进行二次开发

ningqingzy的博客 1万+

【国产MCU系列】-GD32F4开发环境搭建(基于Embedded Builder)

GD32F4系列MCU采用Arm® Cortex®-M4内核,处理器主频高达240MHz,可支持算法复杂度更高的嵌入式应用,具备更快速的实时处理能力,并拥有业界领先的大容量存储优势。

视觉与物联智能 3081

Spark SQL 核心优化实践

我们先简单聊一下Spark SQL 的架构。下面这张图描述了一条 SQL 提交之后需要经历的几个阶段,结合这些阶段就可以看到在哪些环节可以做优化。很多时候,做数据仓库建模的同学更倾向于直接写 SQL 而非使用 Spark 的 DSL。一条 SQL 提交之后会被 Parser 解析并转化为 Unresolved Logical Plan。它的重点是 Logical Plan 也即逻辑计划,它描述了希望做什么样的查询。

AI天才研究院 400

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Spark SQL踩坑经验总结及调优分享

写在之前: 本篇文章写就时间较早,因此本文所讨论的Spark SQL非最新版本,后续更新版本可能有部分修复和更新。 一、Spark内存泄露 1.高并发情况下的内存泄露的具体表现 首先得很遗憾的告诉各位,Spark在设计之初的架构就不是为了高并发请求而生的,我们尝试在网络条件不好的集群下,进行100并发的查询,在压测3天后发现了内存泄露。 在进行大量小SQL的压测过程中发现,有大量的activejob在spark ui上一直处于pending状态,且永远不结束,如下图所示: 并且发现driver内存爆满:

weixin_52672149的博客 750

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Spark-SQL 多维度聚合优化

1.问题描述 问题描述 上线一个SQL任务,发现其本身输入表都不打,基本都在百万级别以内,但是能够耗时几个小时,故进行优化 2.优化思路 2.1 并行度 根据运行指标发现,各个stage运行时间,应该是某个stage下的task发生倾斜 进一步跟踪stage,发现 task读写数据不是很大,只有几百KB,只要shuffle时才比较大 ,因此判断并行度无益于作业优化 stage 362 2.2 shuffle ,聚合 shuflle 分析各个job的数据,发现也不是很大,无需改变shuffle时

dymkkj的专栏 2312

Spark SQL 如何与 Hive 集成?如何在 Spark SQL 中查询 Hive 表?

Spark SQL与Hive集成概述:Spark通过共享Hive元数据存储实现无缝集成,支持直接访问Hive表和分区信息。配置需添加spark-hive依赖,设置Hive Metastore连接参数(如JDBC URL、Thrift服务地址等),并在SparkSession中启用Hive支持。查询方式包括直接引用Hive表(spark.table())或执行SQL语句。集成后,Spark可充分利用Hive现有元数据,同时保持自身高性能计算能力。

weixin_45422672的博客 923

sparksql 合并小文件参数_Spark SQL小文件问题在OPPO的解决方案

Spark SQL小文件是指文件大小显著小于hdfs block块大小的的文件。过于繁多的小文件会给HDFS带来很严重的性能瓶颈,对任务的稳定和集群的维护会带来极大的挑战。一般来说,通过Hive调度的MR任务都可以简单设置如下几个小文件合并的参数来解决任务产生的小文件问题:sethive.merge.mapfiles=true;sethive.merge.mapredfiles=tr...

weixin_28737073的博客 1052
上一篇: Flink窗口触发器类型及实现原理
下一篇: Flink SQL 优化方案总结
走过冬季
博客等级 码龄9年 1609粉丝 341原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

走过冬季

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

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

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

打赏作者

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

抵扣说明:

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

余额充值