oracle执行计划(Explain Plan for)

本文详细介绍了如何通过查看SQL执行计划来定位并优化SQL性能问题,包括使用autotrace、SQL命令、Toad和PL/SQLDeveloper工具的方法,以及理解Cardinality/rows、执行计划字段解释、谓词说明和统计信息的重要性的指南。

from : http://blog.youkuaiyun.com/tianlesoftware/article/details/5827245

如果要分析某条SQL的性能问题,通常我们要先看SQL的执行计划,看看SQL的每一步执行是否存在问题。 如果一条SQL平时执行的好好的,却有一天突然性能很差,如果排除了系统资源和阻塞的原因,那么基本可以断定是执行计划出了问题。

      

       看懂执行计划也就成了SQL优化的先决条件。 这里的SQL优化指的是SQL性能问题的定位,定位后就可以解决问题。

 

 

一.         查看执行计划的三种方法

1.1 设置autotrace

序号

命令

解释

1

SET AUTOTRACE OFF

此为默认值,即关闭Autotrace 

2

SET AUTOTRACE ON EXPLAIN

只显示执行计划

3

SET AUTOTRACE ON STATISTICS

 只显示执行的统计信息

4

SET AUTOTRACE ON

 包含2,3两项内容

5

SET AUTOTRACE TRACEONLY

 ON相似,但不显示语句的执行结果

 

SQL> set autotrace on

SQL> select * from dave;

        ID NAME

---------- ----------

         8 安庆

         1 dave

         2 bl

         1 bl

         2 dave

         3 dba

         4 sf-express

         5 dmm

 

已选择8行。

 

执行计划

----------------------------------------------------------

Plan hash value: 3458767806

--------------------------------------------------------------------------

| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |

--------------------------------------------------------------------------

|   0 | SELECT STATEMENT  |      |     8 |    64 |     2   (0)| 00:00:01 |

|   1 |  TABLE ACCESS FULL| DAVE |     8 |    64 |     2   (0)| 00:00:01 |

--------------------------------------------------------------------------

 

统计信息

----------------------------------------------------------

          0  recursive calls

          0  db block gets

          4  consistent gets

          0  physical reads

          0  redo size

        609  bytes sent via SQL*Net to client

        416  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

          8  rows processed

 

SQL>

 

1.2 使用SQL

SQL>EXPLAIN PLAN FOR sql语句;

SQL>SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE'));

 

示例:

SQL> EXPLAIN PLAN FOR SELECT * FROM DAVE;

已解释。

SQL> SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE'));

或者:

SQL>  select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT

--------------------------------------------------------------------------------

Plan hash value: 3458767806

 

--------------------------------------------------------------------------

| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |

--------------------------------------------------------------------------

|   0 | SELECT STATEMENT  |      |     8 |    64 |     2   (0)| 00:00:01 |

|   1 |  TABLE ACCESS FULL| DAVE |     8 |    64 |     2   (0)| 00:00:01 |

--------------------------------------------------------------------------

已选择8行。

执行计划

----------------------------------------------------------

Plan hash value: 2137789089

--------------------------------------------------------------------------------

| Id  | Operation                         | Name    | Rows  | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT                  |         |  8168 | 16336 |    29   (0)| 00:00:01 |

|   1 |  COLLECTION ITERATOR PICKLER FETCH| DISPLAY |  8168 | 16336 |    29   (0)| 00:00:01 |

---------------------------------------------------------------------------------------------

 

统计信息

----------------------------------------------------------

         25  recursive calls

         12  db block gets

        168  consistent gets

          0  physical reads

          0  redo size

        974  bytes sent via SQL*Net to client

        416  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          1  sorts (memory)

          0  sorts (disk)

          8  rows processed

SQL>

 

1.3 使用Toad,PL/SQL Developer工具

 

 

二.         Cardinality(基数)/ rows

Cardinality值表示CBO预期从一个行源(row source)返回的记录数,这个行源可能是一个表,一个索引,也可能是一个子查询。   Oracle 9i中的执行计划中,Cardinality缩写成Card。 在10g中,Card值被rows替换。

 

这是9i的一个执行计划,我们可以看到关键字Card

       执行计划

----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=402)

   1    0   TABLE ACCESS (FULL) OF 'TBILLLOG8' (Cost=2 Card=1 Bytes=402)

 

Oracle 10g的执行计划,关键字换成了rows

执行计划

----------------------------------------------------------

Plan hash value: 2137789089

--------------------------------------------------------------------------------

| Id  | Operation                         | Name    | Rows  | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT                  |         |  8168 | 16336 |    29   (0)| 00:00:01 |

|   1 |  COLLECTION ITERATOR PICKLER FETCH| DISPLAY |  8168 | 16336 |    29   (0)| 00:00:01 |

---------------------------------------------------------------------------------------------

 

Cardinality的值对于CBO做出正确的执行计划来说至关重要。 如果CBO获得的Cardinality值不够准确(通常是没有做分析或者分析数据过旧造成),在执行计划成本计算上就会出现偏差,从而导致CBO错误的制定出执行计划。

 

       在多表关联查询或者SQL中有子查询时,每个关联表或子查询的Cardinality的值对主查询的影响都非常大,甚至可以说,CBO就是依赖于各个关联表或者子查询Cardinality值计算出最后的执行计划。

 

       对于多表查询,CBO使用每个关联表返回的行数(Cardinality)决定用什么样的访问方式来做表关联(如Nested loops Join  hash Join)。

          多表连接的三种方式详解 HASH JOIN MERGE JOIN NESTED LOOP

              http://blog.youkuaiyun.com/tianlesoftware/archive/2010/08/20/5826546.aspx

 

对于子查询,它的Cardinality将决定子查询是使用索引还是使用全表扫描的方式访问数据。

 

 

 

三. SQL 的执行计划

       生成SQL的执行计划是Oracle在对SQL做硬解析时的一个非常重要的步骤,它制定出一个方案告诉Oracle在执行这条SQL时以什么样的方式访问数据:索引还是全表扫描,是Hash Join还是Nested loops Join等。 比如说某条SQL通过使用索引的方式访问数据是最节省资源的,结果CBO作出的执行计划是全表扫描,那么这条SQL的性能必然是比较差的。

       Oracle SQL的硬解析和软解析

       http://blog.youkuaiyun.com/tianlesoftware/archive/2010/04/08/5458896.aspx

 

示例:

SQL> SET AUTOTRACE TRACEONLY;  -- 只显示执行计划,不显示结果集

SQL> select * from scott.emp a,scott.emp b where a.empno=b.mgr;

已选择13行。

 

执行计划

----------------------------------------------------------

Plan hash value: 992080948

---------------------------------------------------------------------------------------

| Id  | Operation                    | Name   | Rows  | Bytes | Cost (%CPU)| Time     |

---------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT             |        |    13 |   988 |     6  (17)| 00:00:01 |

|   1 |  MERGE JOIN                  |        |    13 |   988 |     6  (17)| 00:00:01 |

|   2 |   TABLE ACCESS BY INDEX ROWID| EMP    |    14 |   532 |     2   (0)| 00:00:01 |

|   3 |    INDEX FULL SCAN           | PK_EMP |    14 |       |     1   (0)| 00:00:01 |

|*  4 |   SORT JOIN                  |        |    13 |   494 |     4  (25)| 00:00:01 |

|*  5 |    TABLE ACCESS FULL         | EMP    |    13 |   494 |     3   (0)| 00:00:01 |

---------------------------------------------------------------------------------------

 

Predicate Information (identified by operation id):

---------------------------------------------------

   4 - access("A"."EMPNO"="B"."MGR")

       filter("A"."EMPNO"="B"."MGR")

   5 - filter("B"."MGR" IS NOT NULL)

 

统计信息

----------------------------------------------------------

          0  recursive calls

          0  db block gets

         11  consistent gets

          0  physical reads

          0  redo size

       2091  bytes sent via SQL*Net to client

        416  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          1  sorts (memory)

          0  sorts (disk)

         13  rows processed

SQL>

 

 

 

 

图片是Toad工具查看的执行计划。 在Toad 里面,很清楚的显示了执行的顺序。 但是如果在SQLPLUS里面就不是那么直接。 但我们也可以判断:一般按缩进长度来判断,缩进最大的最先执行,如果有2行缩进一样,那么就先执行上面的。

 

 

3.1 执行计划中字段解释:

       ID: 一个序号,但不是执行的先后顺序。执行的先后根据缩进来判断。

       Operation: 当前操作的内容。

       Rows: 当前操作的CardinalityOracle估计当前操作的返回结果集。

       CostCPU):Oracle 计算出来的一个数值(代价),用于说明SQL执行的代价。

       TimeOracle 估计当前操作的时间。

 

3.2 谓词说明:

Predicate Information (identified by operation id):

---------------------------------------------------

   4 - access("A"."EMPNO"="B"."MGR")

       filter("A"."EMPNO"="B"."MGR")

   5 - filter("B"."MGR" IS NOT NULL)

 

       Access: 表示这个谓词条件的值将会影响数据的访问路劲(表还是索引)。

       Filter:表示谓词条件的值不会影响数据的访问路劲,只起过滤的作用。

 

在谓词中主要注意access,要考虑谓词的条件,使用的访问路径是否正确。

 

 

3.3 统计信息说明:

 

 

 

db block gets : 从buffer cache中读取的block的数量    

consistent gets buffer cache中读取的undo数据的block的数量    

physical reads 从磁盘读取的block的数量    

redo size DML生成的redo的大小    

sorts (memory) 在内存执行的排序量    

sorts (disk) 在磁盘上执行的排序量    

 

       Physical Reads通常是我们最关心的,如果这个值很高,说明要从磁盘请求大量的数据到Buffer Cache里,通常意味着系统里存在大量全表扫描的SQL语句,这会影响到数据库的性能,因此尽量避免语句做全表扫描,对于全表扫描的SQL语句,建议增 加相关的索引,优化SQL语句来解决。

 

关于physical reads db block gets consistent gets这三个参数之间有一个换算公式:

       数据缓冲区的使用命中率=1 - ( physical reads / (db block gets + consistent gets) )

 

用以下语句可以查看数据缓冲区的命中率:

       SQL>SELECT name, value FROM v$sysstat WHERE name IN ('db block gets', 'consistent gets','physical reads');

       查询出来的结果Buffer Cache的命中率应该在90%以上,否则需要增加数据缓冲区的大小。

 

Recursive Calls  Number of recursive calls generated at both the user and system level.    

Oracle Database maintains tables used for internal processing. When it needs to change these tables, Oracle Database generates an internal SQL statement, which in turn generates a recursive call. In short, recursive calls are basically SQL performed on behalf of your SQL. So, if you had to parse the query, for example, you might have had to run some other queries to get data dictionary information. These would be recursive calls. Space management, security checks, calling PL/SQL from SQL—all incur recursive SQL calls

 

DB Block Gets Number of times a CURRENT block was requested.

Current mode blocks are retrieved as they exist right now, not in a consistent read fashion. Normally, blocks retrieved for a query are retrieved as they existed when the query began. Current mode blocks are retrieved as they exist right now, not from a previous point in time. During a SELECT, you might see current mode retrievals due to reading the data dictionary to find the extent information for a table to do a full scan (because you need the "right now" information, not the consistent read). During a modification, you will access the blocks in current mode in order to write to them. (DB Block Gets:请求的数据块在buffer能满足的个数)
       
当前模式块意思就是在操作中正好提取的块数目,而不是在一致性读的情况下而产生的块数。正常的情况下,一个查询提取的块是在查询开始的那个时间点上存在的数据块,当前块是在这个时刻存在的数据块,而不是在这个时间点之前或者之后的数据块数目。

 

Consistent Gets Number of times a consistent read was requested for a block.

This is how many blocks you processed in "consistent read" mode. This will include counts of blocks read from the rollback segment in order to roll back a block. This is the mode you read blocks in with a SELECT, for example. Also, when you do a searched UPDATE/DELETE, you read the blocks in consistent read mode and then get the block in current mode to actually do the modification. (Consistent Gets: 数据请求总数在回滚段Buffer中的数据一致性读所需要的数据块)
       
这里的概念是在处理你这个操作的时候需要在一致性读状态上处理多少个块,这些块产生的主要原因是因为由于在你查询的过程中,由于其他会话对数据块进行操 作,而对所要查询的块有了修改,但是由于我们的查询是在这些修改之前调用的,所以需要对回滚段中的数据块的前映像进行查询,以保证数据的一致性。这样就产 生了一致性读。

 

Physical Reads Total number of data blocks read from disk. This number equals the value of "physical reads direct" plus all reads into buffer cache. (Physical Reads:实例启动后,从磁盘读到Buffer Cache数据块数量)

就是从磁盘上读取数据块的数量,其产生的主要原因是:
       
1) 在数据库高速缓存中不存在这些块
       
2) 全表扫描
       
3) 磁盘排序
它们三者之间的关系大致可概括为:
       
逻辑读指的是Oracle从内存读到的数据块数量。一般来说是'consistent gets' + 'db block gets'。当在内存中找不到所需的数据块的话就需要从磁盘中获取,于是就产生了'physical reads'

 

Sorts(disk):

    Number of sort operations that required at least one disk write. Sorts that require I/O to disk are quite resource intensive. Try increasing the size of the initialization parameter SORT_AREA_SIZE.

 

bytes sent via SQL*Net to client:
    Total number of bytes sent to the client from the foreground processes.

 

bytes received via SQL*Net from client:
    Total number of bytes received from the client over Oracle Net.

 

SQL*Net roundtrips to/from client:
    Total number of Oracle Net messages sent to and received from the client.

 

 

 

 

更多内容参考Oracle联机文档:

       Statistics Descriptions

       http://download.oracle.com/docs/cd/E11882_01/server.112/e10820/stats002.htm#i375475

 

 

3.4 动态分析

       如果在执行计划中有如下提示:

              Note

              ------------

                     -dynamic sampling used for the statement

      

       这提示用户CBO当前使用的技术,需要用户在分析计划时考虑到这些因素。 当出现这个提示,说明当前表使用了动态采样。 我们从而推断这个表可能没有做过分析。

 

 

这里会出现两种情况:

(1)       如果表没有做过分析,那么CBO可以通过动态采样的方式来获取分析数据,也可以或者正确的执行计划。

(2)       如果表分析过,但是分析信息过旧,这时CBO就不会在使用动态采样,而是使用这些旧的分析数据,从而可能导致错误的执行计划。

 

 

 

总结:

       在看执行计划的时候,除了看执行计划本身,还需要看谓词和提示信息。 通过整体信息来判断SQL 效率。

 

<think>首先,用户的问题是关于在Oracle数据库中使用和查看EXPLAINPLANFOR来分析查询执行计划和性能问题。用户提到了引用[1]、[2]、[3],这些引用提供了背景信息:EXPLAINPLAN是用于静态分析SQL执行路径的工具,它生成执行计划而不实际执行SQL,帮助预测性能。我的响应需要遵循系统级指令:-所有行内数学表达式使用$...$格式。-独立公式使用$$...$$格式并单独成段。-LaTeX语法正确。-使用中文回答。-生成相关问题。-回答中引用的段落末尾自然地添加引用标识,例如[^1]。-回答结构清晰,帮助用户逐步解决问题,保证真实可靠。用户的问题具体是:如何在Oracle数据库中使用和查看EXPLAINPLANFOR分析查询执行计划及性能问题。从引用中:-引用[1]解释了EXPLAINPLAN的原理:静态分析,无副作用,依赖优化器统计信息。-引用[2]强调通过执行计划优化SQL性能。-引用[3]提到查看EXPLAINPLANFOR的结果。我的回答应该覆盖:1.如何使用EXPLAINPLANFOR命令。2.如何查看生成的执行计划。3.如何分析执行计划以诊断性能问题。4.最佳实践或常见问题。结构化回答:-**引言**:简要介绍EXPLAINPLAN的作用。-**步骤1:生成执行计划**:如何使用EXPLAINPLANFOR命令。-**步骤2:查看执行计划**:如何查询PLAN_TABLE或使用工具。-**步骤3:分析执行计划**:解释关键元素如操作、成本、行数等。-**性能分析**:如何基于执行计划诊断问题,如全表扫描、索引缺失。-**示例**:提供简单SQL示例来演示。-**总结**:强调重要性。-**相关问题**:生成相关问题。在回答中,自然地添加引用标识:-当引用或基于引用内容时,在末尾加[^1]、[^2]等。-例如,提到原理时引用[^1]。使用中文回答。生成相关问题:-在结尾添加相关问题部分,如示例所示。数学表达式:-用户问题中没有明显的数学,但如果有成本计算等,使用$...$格式。-例如,讨论成本时用$cost$。确保LaTeX语法正确。最终响应结构:1.回答用户问题。2.添加相关问题。现在,草拟响应:-**标题**:Oracle数据库EXPLAINPLANFOR的使用与分析-**内容**:-介绍:EXPLAINPLANOracle用于分析SQL执行计划的工具,帮助优化查询性能[^1]。-使用步骤:-生成执行计划:使用`EXPLAINPLANFOR`命令。-查看执行计划:查询`PLAN_TABLE`或使用`DBMS_XPLAN.DISPLAY`。-分析执行计划:解读操作符、成本、行数。-示例:展示一个简单查询的示例。-性能问题诊断:如何识别瓶颈。-总结。-**相关问题**:列出几个相关问题。</think>### Oracle 数据库中如何使用和查看 EXPLAIN PLAN FOR 分析查询执行计划及性能问题 在 Oracle 数据库中,`EXPLAIN PLAN FOR` 是一个关键工具,用于静态分析 SQL 查询的执行计划,而无需实际执行语句,从而帮助预测和优化性能问题[^1]。执行计划显示了优化器如何访问表、使用索引、执行连接等操作,这对于诊断查询瓶颈(如全表扫描或索引缺失)至关重要[^2]。下面,我将一步步解释如何使用和查看 `EXPLAIN PLAN FOR`,并结合示例演示如何分析性能问题。回答基于 Oracle 官方文档和最佳实践,确保真实可靠。 #### 1. **生成执行计划** 要生成执行计划,使用 `EXPLAIN PLAN FOR` 命令。它会将优化器生成的执行计划写入系统临时表 `PLAN_TABLE`(默认自动创建)[^1]。语法如下: ```sql EXPLAIN PLAN FOR SELECT * FROM your_table WHERE your_condition; ``` - **关键点**: - 替换 `your_table` 和 `your_condition` 为实际查询。 - 此命令不会执行 SQL,因此无副作用(例如,不修改数据)[^1]。 - 执行计划依赖于优化器统计信息(如表行数、索引分布),确保统计信息最新以提高准确性[^1]。 #### 2. **查看执行计划** 生成后,通过查询 `PLAN_TABLE` 或使用内置包查看执行计划。推荐使用 `DBMS_XPLAN.DISPLAY` 函数,因为它提供格式化输出,易于阅读。 - **方法1:查询 PLAN_TABLE** ```sql SELECT * FROM PLAN_TABLE; ``` 输出包含列如 `ID`, `OPERATION`, `OPTIONS`, `OBJECT_NAME`, `COST`, `CARDINALITY`(预估行数),但结果较原始,需手动解析[^3]。 - **方法2:使用 DBMS_XPLAN.DISPLAY(推荐)** ```sql SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY()); ``` 此命令输出树状结构,包括: - `OPERATION`:执行步骤(如 `TABLE ACCESS`,`INDEX SCAN`)。 - `COST`:优化器预估的相对成本(值越小越好)。 - `CARDINALITY`:预估返回行数。 - `TIME`:预估执行时间。 例如,输出可能显示: ``` PLAN_TABLE_OUTPUT ------------------------------------------------------------------- | Id | Operation | Name | Cost | Cardinality | | 0 | SELECT STATEMENT | | 10 | 100 | | 1 | TABLE ACCESS FULL | EMPLOYEES| 10 | 100 | ------------------------------------------------------------------- ``` 这表示查询进行了全表扫描(`TABLE ACCESS FULL`),成本为 $10$[^1]。 #### 3. **分析执行计划以诊断性能问题** 执行计划的分析重点是识别高成本操作和潜在瓶颈。以下步骤帮助诊断性能问题: - **步骤1:检查操作类型** - 优先优化高成本操作:如 `TABLE ACCESS FULL`(全表扫描)通常成本高,应通过添加索引优化;`INDEX SCAN` 更高效[^2]。 - 观察连接方式:`NESTED LOOPS` 适合小数据集,`HASH JOIN` 或 `MERGE JOIN` 适合大数据集。 - **步骤2:评估成本和行数** - 成本(`COST`):优化器计算的相对值,公式基于 $cost = f(\text{IO}, \text{CPU})$。成本过高(如 > 100)表示需要优化[^1]。 - 预估行数(`CARDINALITY`):与实际行数差异大时,统计信息可能过时,需运行 `DBMS_STATS.GATHER_TABLE_STATS` 更新[^1]。 - **步骤3:识别常见问题** - **全表扫描**:缺少索引或索引未使用。解决方案:创建索引,例如: ```sql CREATE INDEX idx_name ON your_table(your_column); ``` - **高成本连接**:连接顺序不当或过滤条件缺失。优化 WHERE 子句或使用提示(如 `/*+ INDEX(table index) */`)强制索引。 - **统计信息不准确**:导致优化器错误预估。定期收集统计信息[^1]。 - **示例分析** 假设分析查询: ```sql EXPLAIN PLAN FOR SELECT * FROM employees WHERE department_id = 10; ``` 查看计划: ```sql SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY()); ``` 输出可能显示: ``` | Id | Operation | Name | Cost | Cardinality | | 0 | SELECT STATEMENT | | 5 | 50 | | 1 | TABLE ACCESS FULL| EMPLOYEES | 5 | 50 | ``` **诊断**: - 操作是 `TABLE ACCESS FULL`,成本 $5$,表示全表扫描。 - 问题:如果 `department_id` 有索引但未使用,优化器可能因统计信息过时而选择全扫描。 - **优化建议**:创建索引并重试: ```sql CREATE INDEX idx_dept ON employees(department_id); EXPLAIN PLAN FOR SELECT * FROM employees WHERE department_id = 10; ``` 新计划应显示 `INDEX SCAN`,成本降低(如 $2$)。 #### 4. **最佳实践和注意事项** - **更新统计信息**:执行计划准确性依赖统计信息。使用: ```sql EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA_NAME', 'TABLE_NAME'); ``` - **使用工具**:在 SQL Developer 或 Toad 中,可直接运行 `EXPLAIN PLAN` 并图形化查看。 - **局限**:`EXPLAIN PLAN` 是静态分析,不反映实际执行时的锁或资源争用。结合 `AUTOTRACE` 或 `SQL Trace` 进行动态分析[^2]。 - **性能优化**:通过执行计划识别瓶颈后,优化 SQL(如重写查询、添加索引)。例如,避免全表扫描可将成本降低 $O(n)$ 到 $O(\log n)$[^1]。 总结,`EXPLAIN PLAN FOR` 是 Oracle 中强大的性能诊断工具。通过生成、查看和分析执行计划,您可以快速定位查询瓶颈并实施优化。始终确保统计信息最新以提高准确性[^1]。 #### 相关问题 1. 如何更新 Oracle 数据库中的优化器统计信息以提高执行计划准确性? 2. 执行计划中成本(Cost)和行数(Cardinality)的计算原理是什么? 3. Oracle 中除了 EXPLAIN PLAN,还有哪些工具用于动态分析 SQL 性能?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值