【mysql】explain语句的输出格式

本文详细介绍MySQL的EXPLAIN语句,包括其输出字段的意义、连接类型及优化技巧等内容,帮助理解SQL执行计划。

本文是翻译而来的,原文地址:EXPLAIN Output Format

explain语句会为我们显示select语句的执行计划相关的信息。

每个在select语句中用到的table,explain语句都会为其返回一行信息,而且这些table的顺序是按照MYSQL查询时的顺序输出的。MYSQL使用内嵌循环的方式来解决所有连表。这意味着,MYSQL会先从第一张表中读取一行数据,然后去第二张表中寻找匹配的行,然后以此类推。当所有的table都处理后,MYSQL输出查询的字段,然后通过explain输出的数据表列表回溯,直到它找到一个匹配了最多行的表。下一行从该表开始读取直到处理下一张表。

如果使用了extended关键字,那么explain会产生额外的信息,这些信息可以使用show warnings语句来查看。(注意:在explain语句中,不能同时使用extended和partitions这两个关键字)

一、explain的输出字段

这一小节介绍explain输出的各个字段的含义,下一小节会更加详细的介绍type和extra字段。

explain输出的每一行都对应的select查询所用到的table。每一行的字段和每个字段的含义如下表所示:

字段含义
id查询的标识
select_type查询的类型
table该行信息对应的table
partitions匹配到的分区
type连接类型
possible_keys可以选择使用的索引
key实际使用的索引
key_len实际使用的索引的长度
ref与索引对照的字段
rows预估将要扫描的行
filtered通过条件过滤的行数的占比
extra额外信息

id
查询标识,它是一系列连续的数字。但如果该行表示的是从其他联合查询返回的结果,那么它的值将会是null,而且该行的table字段的值会是这个样子:<unionM,N>,它表示,改行是从id为M和N的联合查询中返回的结果。
select_type
查询的类型,它的值有如下几种:

select_type的值该值的含义
SIMPLE简单的查询,没有使用UNION或者子查询
PRIMARY最外层的查询
UNION在UNION语句中的第二层或者之后的查询
DEPENDENT UNIONUNION语句中的第二层或者之后的查询,取决于对外的查询
UNION RESULTUNION查询的结果
SUBQUERY第一层子查询
DEPENDENT SUBQUERY第一层子查询,取决于对外的查询
DERIVED衍生的查询
UNCACHEABLE SUBQUERY不可缓存的子查询
UNCACHEABLE UNIONUNION语句的第二层查询的select_type是UNCACHEABLE SUBQUERY

DEPENDENT SUBQUERY不同于UNCACHEABLE SUBQUERY。对于DEPENDENT SUBQUERY来说,只有外层的值发生改变时,才会重新查询,并返回结果。而UNCACHEABLE SUBQUERY,外层的每一行,都会导致它重新执行查询。

可缓存的子查询并不是意味着把查询结果缓存在缓存池中。子查询的结果缓存只会发生在查询期间,当查询结束后,子查询在缓存中的数据就会被销毁。

table
该行所对应的表名称。但也有可能是如下值:
<unionM,N>:从union语句的结果中查询,这些union语句的id是从M到N;
<derivedN>:该行是从id为N的查询结果中衍生出来的。

partitions
被匹配到的分区。该字段只有当使用关键字 PARTITIONS时,才会出现。对于非分区表,该字段的值是NULL。

type
连接类型。这块内容详见下一小节。

possible_keys
该字段显示哪些索引是本次查询可以使用的。需要注意的是:该字段完全取决于所在行在explain输出中的顺序。这就意味着,有些索引虽然在该字段中显示了,但在实际的查询中,并不会用得上。

如果这个字段的值为NULL,那就意味着没有相关的索引。这种情况下,你可能需要通过修改你的where子句,使用那些有建立索引的字段来匹配,以此来优化查询。也可以自己建立一个新的索引,然后再用explain检查一下。

可以使用 SHOW INDEX FROM tbl_name 语句来查看表中有哪些索引。

key
该字段显示的是在查询中,实际使用的索引。如果mysql决定使用possible_keys中的一个索引来检索数据,那么该字段的值就会在possible_keys字段中。

也有可能,该字段会出现一个不在possible_keys中的值。这种情况一般出现在,possible_keys中的所有索引都不适合用于本次查询,但存在一个索引,涵盖了所有要查询的字段,所以,虽然直接检索这个索引,就可以得到想要的数据,索引检索远远比数据检索快得多。

如要要强制使用或者强制忽略某个在possible_keys中的索引,可以在查询中使用 FORCE INDEX、USE INDEX、IGNORE INDEX来实现。

对于MYISAM或者NDB来说,执行ANALYZE TABLE可以帮助优化器选择更好的索引。对于MYISAM表,执行myisamchk –analyze和执行ANALYZE TABLE是一样的。

key_len
该字段显示本次查询,mysql使用的索引的长度。如果key字段的值是NULL ,那么该字段的值也会是NULL 。注意的是:该字段可以帮助你判断,在一次查询中,使用了联合索引中的几个字段来检索数据。

ref
该字段显示哪些字段或者常数与key一起从表中选择行。

rows
这字段显示mysql觉得将要有多少行的数据需要被检索。

对于Innodb表来说,这个字段的值是估算的,并不是总是精确的。

filtered
该字段显示mysql预估被过滤掉的数据占比。rows字段显示预估的行数,rows * filtered / 100 表示的是有多少行数据和前一个表连表。这一字段只有在使用explain extended时才显示。

extra
该字段显示mysql在本次查询中的额外的信息。在下下小节中,会详细说明该字段的含义。

二、explain的连接类型
explain语句输出结果中的type字段描述表与表之间是如何连接的。接下来会按从最优到最差列出所有的连接的类型:

system
该表中只有一行数据,这其实是const类型的一个特例。

const
该表中最多有一行数据匹配。由于只有一行数据,所以对于优化器来说,该行中的数据可以当成常量来处理。读取常量表的数据,只需读取一次,所以是很快的。
当你匹配主键中的所有字段或者唯一键中的所有字段时,就会使用const的连接类型。在下面的查询中,tbl_name表可以被当做是常量表。

SELECT * FROM tbl_name WHERE primary_key=1;

SELECT * FROM tbl_name WHERE primary_key_part1=1 AND primary_key_part2=2;

eq_ref
对于每个来自于前面的表的行组合,从该表中读取一行。除了system和const类型,这可能是最好的连接类型。当部分主键或者部分唯一建被使用时,连接类型就会是eq_ref。

当索引字段用等号(=)来匹配的时候,就会使用eq_ref类型。用来比较的值,可以是常数或者是其他表格的字段。以下列举了几个使用eq_ref的列子:

SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column;

SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1;

ref
对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取。如果联接只使用键的最左边的前缀,或如果键不是UNIQUE或PRIMARY KEY(换句话说,如果联接不能基于关键字选择单个行的话)则使用ref。如果使用的键仅仅匹配少量行,该联接类型是不错的。

当索引字段用等号(=)或者(<=>)来匹配的时候,就会使用ref类型。以下列举了几个使用ref的列子:

SELECT * FROM ref_table WHERE key_column=expr;

SELECT * FROM ref_table,other_table WHERE ref_table.key_column=other_table.column;

SELECT * FROM ref_table,other_table WHERE ref_table.key_column_part1=other_table.column AND ref_table.key_column_part2=1;

fulltext
使用全文索引。

ref_or_null
这种连接类型与ref类似,但是mysql会额外判断是否含有null的行。在子查询中,优化器最经常使用这种连接类型。下面这个列子,mysql使用的就是ref_or_null

SELECT * FROM ref_table WHERE key_column=expr OR key_column IS NULL;

index_merge
这种类型表明优化器使用了索引合并。这种情况下,key字段的值会是一个列表,列出本次查询使用的所有索引,key_len字段也会是一个列表,列出每个索引使用的长度。

unique_subquery
在某些子查询中,会使用这种类型代替ref类型,如下面这种情况:

value IN (SELECT primary_key FROM single_table WHERE some_expr)

unique_subquery只是之中使用索引来代替子查询,以此提高查询效率的方法。

index_subquery
这种类型和unique_subquery有点类似。它也是用来代替子查询,只是没有唯一索引。如下列子:

value IN (SELECT key_column FROM single_table WHERE some_expr)

range
使用一个索引来检索一个范围的数据。key字段显示哪个索引被使用,key_len显示使用索引的长度,这种情况下,ref字段的值为null。

当索引使用=, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, 或者 IN()来匹配时,使用range类型。如下列子:

SELECT * FROM tbl_name WHERE key_column = 10;

SELECT * FROM tbl_name WHERE key_column BETWEEN 10 and 20;

SELECT * FROM tbl_name WHERE key_column IN (10,20,30);

SELECT * FROM tbl_name WHERE key_part1= 10 AND key_part2 IN (10,20,30);

index
这种类型和all类型有点类似,只不过这种方式扫描的是索引树。有两种情况会产生这种类型:

  1. 如果要查询的列都在一个索引中,那么mysql将只扫描索引树来获取数据。这种情况下,extra字段会显示”Using index”。index会比all类型快的原因在于,索引树的大小一般比真实的数据表小。
  2. 虽然是一个全表扫描,但会按着索引树的顺序,先扫描索引再去表中获取数据。这种情况下,extra字段不会显示”Using index”。

all
对于每个来自于先前的表的行组合,进行一次全表扫描。如果表是第一个没有标记为const的表,那么性能会比较差,而且在一般在所有的情况,性能都会极差。一般可以通过添加索引来避免产生all类型。

三、explain的extra字段信息

extra字段提供mysql在本次查询中的额外信息。下面列出该字段可能会出现的值,如果你想让你的查询尽可能的快,一定要留心该字段中出现”Using filesort”和”Using temporary”。

* Child of ‘table’ pushed join@1*
在NDB引擎中,该表被当做是一个其他表的子表。只有在NDB引擎7.2.0版本后会出现。

const row not found
比如这类查询:SELECT …… FROM tbl_name,然后表是空的。

Distinct
当mysql只需要查找唯一的值,如果它找到一行匹配的数据,它就会停止寻找这类数据。

Full scan on NULL key
在子查询中,如果优化没有使用索引来找出数据。

Impossible Having
having字段一直返回的是false,或者不能选择任何行。

Impossible WHERE noticed after reading const tables
mysql把所有的读完所有的常量表后发现where一直都是没有作用的。

No matching min/max row
没有相关的行来记录这类查询的数据:SELECT MIN(…) FROM … WHERE condition。

no matching row in const table
在一个连表查询中 ,所连接的表是空表,或者该表中没有匹配的数据。

No tables used
查询没有from字段。

Not exists
在本次查询中,mysql可以执行left join,但根据left join的条件,为当前的行组合查找到一条数据之后,就知道没有其他匹配的数据了。以下给出一个可以被优化成这个方式的实例

SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id WHERE t2.id IS NULL;

假设t2.id被定义为not null。在这种情况下,mysql先扫面t1,然后通过t1的id去t2中匹配数据。当mysql在t2中id与t1相等的行,但它知道t2的id将永远不会是null,那么mysql就不会继续在t2表中需找与t1表id相同的行了。换句话说,每扫描一行t1的数据,当去t2中寻找匹配的数据时,mysql只需要找到一行id与t1相同的数据,就不需要继续扫描了,不论t2表中有多少行的id与t1中相等。

Range checked for each record (index map : N)
mysql发现没有一个好的索引可以使用,但是当前面表中的数据已知之后,有些索引是可以用的。这种情况下,mysql会为前面表的每个行组合判断是否可以使用rang或者index_merge的方法去匹配行数据。这种方式并不快,但是总比没有使用索引的连表来得快。

索引是从1开始编号的,展示的顺序和使用show index时是一样的。索引映射值N是一个位掩码表明哪些索引会被考虑使用。举个列子,如果值是0x19(二进制是11001),则表明,索引编号为1、4和5会被考虑使用。

Scanned N databases
这表明当查询表中数据时,有多少个目录扫面了服务器执行,该值可以是0、1或者all。

Select tables optimized away
该查询只包含聚合的函数(min()、max()),那些只是用一个索引就可以解决的,或者是count(*),而且没有groub by字段。那么优化器只需要返回一行数据。

Skip_open_table,Open_frm_only,Open_trigger_only,Open_full_table
这些值表明在表中查询数据时,文件打开的优化选择,这些值的含义如下:

  1. Skip_open_table:数据表对应的文件不需要被打开,它的信息已经变成变量包含检索数据库目录的查询中。
  2. Open_frm_only:只需要打开数据表对应的.frm文件就可以了。
  3. Open_trigger_only:只需要打开数据表对应的.TRG文件就可以了。
  4. Open_full_table:没有优化的信息可以使用,表格对应的.frm、.MYD和.MYI文件都需要被打开。

unique row not found
对于这样的查询:select …… from tbl_name,没有任何行可以满足唯一索引或者主键的条件。

Using filesort
MySQL需要额外的一次操作,以找出如何按排序顺序检索行。这次排序是通过扫面所有的行,根据连接类型,先把那些符合刷选添加的行对应的key的值和行号存起来。然后排序这些key,排序完以后,安装key的顺序把行数据返回。

Using index
字段信息只需要从索引树种获取,而不需要做一次额外的查询去读取真实的行数据。当查询中需要的字段都在索引中时,可以使用这种方法。

如果这时候Extra字段还显示“Using where”,这意味着该索引被用来查找行数据对应的指针。如果没有Using where ,优化器只需要读取索引树。举个例子,如果索引覆盖了要查询的字段,那么优化器则是扫描索引数,而不是用它来查询数据。

对于InnoDB的表来说,它有用户定义的聚合索引,这种索引即使Extra字段中没有显示“Using where”时,也会被使用。这种情况出现在,当type字段是index,且key字段是PRIMARY时。

Using index for group-by
获取表数据的方式和Using index类似,Using index for group-by表明mysql发现有一个索引可以返回group by子句或者distinct子句的所有字段,这样就不用为存储排序的key而额外扫描一次表。另外,对于分组操作,索引是最有效的方法,只有少数的索引需要被读取。

Using join buffer
在join前面的表中的部分数据被存放到缓存中,这样,在后面的表查询中就可以从缓存中获取数据。

Using sort_union(…),Using union(…),Using interesect(…)
当type为index_merge时,这些信息表明索引查找出来的数据是如何合并的。

Using temporary
为了执行本次查询,mysql需要建一个临时表来存放结果集。出现这种情况,有代表性的列子是,查询中包含group by和order by子句,而且这两个子句包含的字段是不同的。

Using where
WHERE子句用于限制哪一个行匹配下一个表或发送到客户。除非你专门从表中索取或检查所有行,如果Extra值不为Using where并且表联接类型为ALL或index,查询可能会有一些错误。即使where子句中所有的字段都包含在一个索引中 ,也可以能会看到Using where,因为有些字段可能会是NULL。

Using where with pushed condition
这种情况只会出现在NDB表中。

四、explain输出解释

从explain的输出结果中,你可以得到一个很好地启示。它会大致的告诉你,mysql会检查多少行来完成本次查询。如果你通过系统变量max_join_size来限制查询,那么explain的输出也可以告诉你在select语句中的哪些表被查询了,哪些表被放弃了。

以下列子展示一个多表的连表查询,通过explain提供的信息,是如何渐渐地被优化的。

假设你有一个查询语句如下,然后你想通过explain来检测该语句。

EXPLAIN SELECT tt.TicketNumber, tt.TimeIn,
               tt.ProjectReference, tt.EstimatedShipDate,
               tt.ActualShipDate, tt.ClientID,
               tt.ServiceCodes, tt.RepetitiveID,
               tt.CurrentProcess, tt.CurrentDPPerson,
               tt.RecordVolume, tt.DPPrinted, et.COUNTRY,
               et_1.COUNTRY, do.CUSTNAME
        FROM tt, et, et AS et_1, do
        WHERE tt.SubmitTime IS NULL
          AND tt.ActualPC = et.EMPLOYID
          AND tt.AssignedPC = et_1.EMPLOYID
          AND tt.ClientID = do.CUSTNMBR;

对于这个列子,做如下假设:
这些表的字段定义如下:

表名字段字段类型
ttActualPCCHAR(10)
ttAssignedPCCHAR(10)
ttdClientIDCHAR(10)
etEMPLOYIDCHAR(15)
doCUSTNMBRCHAR(15)

该表有如下索引

表名索引
ttActualPC
ttAssignedPC
ttClientID
etEMPLOYID (primary key)
doCUSTNMBR (primary key)

tt表的ActualPC字段的值不是均匀分布的。

一开始,没有任何的优化,explain的结果如下:

tabletypepossible_keyskeykey_lenrefrowsExtra
etALLPRIMARYNULLNULLNULL74
doALLPRIMARYNULLNULLNULL2135
et_1ALLPRIMARYNULLNULLNULL74
ttALLAssignedPC,ClientID,ActualPCNULLNULLNULL3872Range checked for each record (index map: 0x23)

由于所有的type都是All,这个结果表明,对于每个表,mysql都产生了笛卡尔积。这表明,每个组合的行,都会使用相当长的时间,由于每张表的每行数据都要被检索。对于上述情况,会产生 74 × 2135 × 74 × 3872 = 45,268,558,720行。如果表格更大一些,你可以想象这会执行多长的时间。

这里的一个问题是,如果这些字段有相同的类型,那么mysql可以使用索引来使查询更加的高效。VARCHAR和CHAR,如果定义的长度相同,那么会被认为是相同的类型。tt.ActuralPC被定义为CHAR(10),et.EMPLOYID被定为CHAR(15),所以长度不匹配。为了解决这个问题,用alter table命令把ActualPC的长度变成15。

ALTER TABLE tt MODIFY ActualPC VARCHAR(15);

现在,tt.ActuralPC和et.EMPLOYID的长度都是15了。再次执行上述语句,结果如下:

tabletypepossible_keyskeykey_lenrefrowsExtra
ttALLAssignedPC,ClientID,ActualPCNULLNULLNULL3872Using where
doALLPRIMARYNULLNULLNULL2135Range checked for each record (index map: 0x1)
et_1ALLPRIMARYNULLNULLNULL74Range checked for each record (index map: 0x1)
eteq_refPRIMARYPRIMARY15tt.ActualPC1

这并不完美,但是已经好多了。它产生的行数,比之前少了74倍。这个版本需要执行几秒钟。

第二可以做如下改变,消除tt.AssignedPC = et_1.EMPLOYID 和tt.ClientID = do.CUSTNMBR的长度不匹配问题。

ALTER TABLE tt MODIFY AssignedPC VARCHAR(15),MODIFY ClientID VARCHAR(15);

做了这一改变后,再次执行explain,结果如下:

tabletypepossible_keyskeykey_lenrefrowsExtra
etALLPRIMARYNULLNULLNULL74
ttrefAssignedPC,ClientID,ActualPCActualPC15et.EMPLOYID52Using index
et_1eq_refPRIMARYPRIMARY15tt.AssignedPC1
doeq_refPRIMARYPRIMARY15tt.ClientID1

到目前为止,这个查询已经尽可能的优化了。剩下的问题是,mysql会默认假设tt.ActualPC字段上的数据是均匀分布的,然后tt表的情况并不是这样。幸运的是,这是很容易告诉mysql去分析键的分布。

ANALYZE TABLE tt

加上额外的索引信息,该连表会变得完美。explain的输出结果如下:

tabletypepossible_keyskeykey_lenrefrowsExtra
ttALLAssignedPC,ClientID,ActualPCNULLNULLNULL3872Using index
eteq_refPRIMARYPRIMARY15tt.ActualPC1
et_1eq_refPRIMARYPRIMARY15tt.AssignedPC1
doeq_refPRIMARYPRIMARY15tt.ClientID1

explain输出的rows字段的值,是根据mysql优化器的分析得出的。判断这个rows字段的值是否接近于实际的值,可以将该值与本次查询返回的行数进行比较。如果这两个值相差很大,你可以在select子句中加入STRAIGHT_JOIN语句,然后尝试把join的表的顺序变换一下,以此来减小差距。

<think>好的,我现在要解决用户关于MySQL EXPLAIN语句中filtered参数的问题。首先,我需要回忆一下自己对filtered的理解,然后结合提供的引用资料来验证和补充。 用户的问题是询问filtered参数的含义和作用。根据已有的知识,我知道EXPLAIN语句用于分析查询的执行计划,而filtered列显示的是返回结果的行数占存储引擎读取行数的百分比。不过,用户提供的引用中提到了一些细节,比如filtered在MySQL 5.7默认显示,以及某些情况下可能总是100%,这可能让用户感到困惑。 先看引用[2],里面提到filtered是返回结果的行占需要读到的行的百分比,这对JOIN操作很重要,因为它影响循环次数。但用户测试时发现filtered总是100%,这可能是因为查询条件在存储引擎层已经过滤了所有行,或者索引覆盖了所有条件,导致优化器认为没有额外的过滤。 引用[4]指出filtered不是万能的,应结合其他列优化,比如避免filesort,即使filtered低也没问题。这可能意味着用户需要关注其他优化点,而不仅仅是filtered的值。 引用[3]提到EXPLAIN的统计信息是估算的,并非精确值,所以filtered也是一个估算值。这可能解释了为什么有时候值不准确或者看起来不直观。 接下来需要整理这些信息,以清晰的方式解释filtered参数。要分点说明其含义、作用、如何应用以及注意事项。例如: 1. 定义:filtered表示存储引擎返回的数据中,经过服务器层过滤后剩余的比例。 2. 作用:帮助评估JOIN顺序,优化器选择较小结果集的表作为驱动表。 3. 应用场景:在分析多表JOIN时,低filtered值可能意味着需要优化索引或查询条件。 4. 注意事项:它是估算值,可能不准确;某些情况下可能显示100%,如单表查询或索引覆盖。 同时,要结合用户提供的引用,引用[2]和[4]中的观点来支持解释,并提醒用户不要过分依赖该参数,而应综合其他执行计划信息进行优化。 最后,生成相关问题,可能涉及如何优化高filtered值的情况,与其他参数的关系,以及在查询优化中的实际应用案例等。这样能帮助用户进一步探索相关主题。</think>### MySQL EXPLAIN中filtered参数详解 #### 一、filtered参数定义 - **含义**:表示存储引擎返回的数据中,经过服务器层**WHERE条件过滤**后剩余行数的百分比。例如$filtered=10\%$表示存储引擎读取100行后,最终仅保留10行[^2][^4]。 - **计算公式**:$$filtered = \frac{\text{最终返回行数}}{\text{存储引擎读取行数(rows列值)}} \times 100\%$$ #### 二、核心作用 1. **优化JOIN顺序** MySQL优化器会优先选择$filtered$值更小的表作为驱动表(即外层循环表),从而减少内层循环的数据处理量[^2][^4]。 2. **索引效率评估** 低$filtered$值可能表明当前索引过滤效果差,例如: ```sql -- 假设索引仅覆盖`age`字段 EXPLAIN SELECT * FROM users WHERE age > 30 AND salary > 50000; ``` 若`rows=1000`且$filtered=5\%$,说明虽然有1000条`age>30`的记录,但仅有50条满足`salary>50000`,此时可考虑创建联合索引`(age, salary)`[^4]。 #### 三、典型场景分析 | 场景类型 | filtered表现 | 优化建议 | |-------------------|--------------------|----------------------------------| | 单表查询 | 始终显示100% | 正常现象,因过滤在存储引擎层完成[^2] | | 覆盖索引查询 | 可能显示100% | 检查是否需要调整查询条件 | | 多条件非索引过滤 | 值通常较低(如10%) | 添加联合索引或优化查询条件 | #### 四、使用注意事项 1. **估算值特性** $filtered$基于统计信息估算,实际值可能偏差较大[^3]。 2. **优先级判断** 当出现`Using filesort`或`Using temporary`时,应优先解决排序/临时表问题而非单纯关注$filtered$。 3. **版本差异** MySQL 5.6需使用`EXPLAIN EXTENDED`才能显示该参数,5.7+版本默认包含。 #### 五、实操验证方法 ```sql EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status='shipped' AND total_amount > 1000; ``` 观察JSON输出中的`filtered`字段,可精确到小数点后两位(如`"filtered": 23.45`)。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值