MySQL Explain 执行计划详细信息

MySQL知识总结

《MySQL是怎样运行的》知识总结

单表访问方法

表的连接原理

优化 基于查询成本的优化

优化需要的统计数据

优化 基于规则的优化

Explain详解

InnoDB缓冲区

事务

redo日志

undo 日志

MVCC原理

MySQL 锁

15 Explain

表结构

-- auto-generated definition
create table single_table (
    id           int auto_increment primary key,
    key1         varchar(100) null,
    key2         int          null,
    key3         varchar(100) null,
    key_part1    varchar(100) null,
    key_part2    varchar(100) null,
    key_part3    varchar(100) null,
    common_field varchar(100) null,
    constraint uk_key2
        unique (key2)
);

create index idx_key1
    on single_table (key1);

create index idx_key3
    on single_table (key3);

create index idx_part
    on single_table (key_part1, key_part2, key_part3);

​ MySQL优化器基于成本、规则对查询语句进行优化后,就会生成一个执行计划,使用explain语句可以查看某个查询语句的执行计划

explain select 1;
idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLENULLNULLNULLNULLNULLNULLNULLNULLNULLNo tables used

explain表格解释

列名描述
id每个select关键字对应一个唯一Id
select_typeselect关键字对应的查询类型
table对应单表访问的表名
partitions匹配的分区信息,不介绍
type对单表进行访问的访问方法
possible_keys查询可能用到的索引
key实际使用的索引
key_len实际使用索引的长度
ref使用索引列进行等值匹配时,与索引列进行匹配的信息
rows预估读取记录的条数
filtered对预计读取的记录,进行查询条件过滤后,满足条件的记录的比值
Extra执行计划的额外信息

id

查询语句可能出现多个select子句,每出现一个select子句,MySQL都会为他分配一个唯一的Id。

对于连接查询来说,一个select子句后面的from子句可以跟着多个表,在连接查询的执行计划中,每个表都对应一条记录,但这些记录的id是相同的,出现在前面的记录对应的表是驱动表

explain
select *
from single_table s1
         join single_table s2;
id11
select_typeSIMPLESIMPLE
tables1s2
partitionsNULLNULL
typeALLALL
possible_keysNULLNULL
keyNULLNULL
key_lenNULLNULL
refNULLNULL
rows99379937
filtered100100
ExtraNULLUsing join buffer (Block Nested Loop)

包含子查询的查询语句,就可能涉及到多个select关键字,所以在包含子查询的查询语句的执行计划中,每个select子句都对应一个唯一id。

explain
select *
from single_table s1
where key1 in (select key1 from single_table s2)
   or key3 = 'a';
id12
select_typePRIMARYSUBQUERY
tables1s2
partitionsNULLNULL
typeALLindex
possible_keysidx_key3idx_key1
keyNULLidx_key1
key_lenNULL303
refNULLNULL
rows99379937
filtered100100
ExtraUsing whereUsing index

优化器可能会对涉及子查询的查询语句进行重写,从而转换为半连接

explain
select *
from single_table s1
where key1 in (select common_field from single_table s2 where s1.key3 = 'a');
id11
select_typeSIMPLESIMPLE
tables2s1
partitionsNULLNULL
typeALLref
possible_keysNULLidx_key1,idx_key3
keyNULLidx_key1
key_lenNULL303
refNULLmysql_run.s2.common_field
rows99372
filtered1002.1
ExtraUsing where; Start temporaryUsing where; End temporary

使用union时,会把多个查询得到的结果进行去重操作,MySQL会在内部创建一个临时表

id为Null,表明这个临时表是为了合并两个查询结果而创建的。

explain
select *
from single_table s1
union
select *
from single_table s2;
id12NULL
select_typePRIMARYUNIONUNION RESULT
tables1s2<union1,2>
partitionsNULLNULLNULL
typeALLALLALL
possible_keysNULLNULLNULL
keyNULLNULLNULL
key_lenNULLNULLNULL
refNULLNULLNULL
rows99379937NULL
filtered100100NULL
ExtraNULLNULLUsing temporary

使用union all,不会对结果集进行去重,不需要创建临时表

explain
select *
from single_table s1
union all
select *
from single_table s2;
id12
select_typePRIMARYUNION
tables1s2
partitionsNULLNULL
typeALLALL
possible_keysNULLNULL
keyNULLNULL
key_lenNULLNULL
refNULLNULL
rows99379937
filtered100100
ExtraNULLNULL

table

不管查询语句有多复杂,查询中有多少个表,最终也是对每个表进行单表访问

type

对表进行查询的访问方法,下方的SQL语句中,对表进行的方法方法是ref

explain
select *
from single_table
where key1 = 'a';
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typeref
possible_keysidx_key1
keyidx_key1
key_len303
refconst
rows138
filtered100
ExtraNULL

访问方法

  1. system
  2. const
  3. eq_ref
  4. ref
  5. fulltext
  6. ref_or_null
  7. index_merge
  8. unique_subquery
  9. index_subquery
  10. range
  11. index
  12. ALL

system

表中只有一条记录,并且该表使用的存储引擎的统计数据是精确的(MyISAM等)

const

对表进行访问时,使用到了主键索引或唯一索引与常数进行等值匹配

eq_ref

执行连接查询时,被驱动表是通过主键不允许为Null的唯一索引(联合索引要求所有列都进行等值匹配)进行等值匹配的方式进行访问

explain
select *
from single_table s1
         join single_table s2 on s1.id = s2.id;
id11
select_typeSIMPLESIMPLE
tables1s2
partitionsNULLNULL
typeALLeq_ref
possible_keysPRIMARYPRIMARY
keyNULLPRIMARY
key_lenNULL4
refNULLmysql_run.s1.id
rows99371
filtered100100
ExtraNULLNULL

ref

使用普通索引与常量进行等值匹配的方式查询表。

如果执行的是连接查询,被驱动表上的普通索引与驱动表的某一个列进行等值匹配时,也可能使用到ref访问方法

explain
select *
from single_table s1
         join single_table s2 on s1.key1 = s2.key1;
id11
select_typeSIMPLESIMPLE
tables1s2
partitionsNULLNULL
typeALLref
possible_keysidx_key1idx_key1
keyNULLidx_key1
key_lenNULL303
refNULLmysql_run.s1.key1
rows99372
filtered100100
ExtraUsing whereNULL

ref_or_null

使用普通索引进行等值匹配且索引列允许为null

explain
select *
from single_table
where key1 = 'a'
   or key1 is null;
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typeref_or_null
possible_keysidx_key1
keyidx_key1
key_len303
refconst
rows1144
filtered100
ExtraUsing index condition

index_merge

索引合并,索引合并有交集、并集、排序并集

explain
select *
from single_table
where key1 = 'a'
   or key3 = 'a';
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typeindex_merge
possible_keysidx_key1,idx_key3
keyidx_key1,idx_key3
key_len303,303
refNULL
rows295
filtered100
ExtraUsing union(idx_key1,idx_key3); Using where

unique_subquery

类似两表查询中被驱动表的eq_ref访问方法,它针对的是包含in子查询的查询语句,优化器决定将in子查询转换为exists子查询,并且子查询转换之后,可以使用主键不允许为null的唯一索引

explain
select *
from single_table s1
where common_field in (select id from single_table s2 where s1.common_field = s2.common_field)
   or key3 = 'a';
   
# 转换为exists子查询
explain
select *
from single_table s1
where exists(select id from single_table s2 where s1.common_field = s2.common_field and s1.common_field = s2.id)
   or key3 = 'a';
id12
select_typePRIMARYDEPENDENT SUBQUERY
tables1s2
partitionsNULLNULL
typeALLunique_subquery
possible_keysidx_key3PRIMARY
keyNULLPRIMARY
key_lenNULL4
refNULLfunc
rows99371
filtered10010
ExtraUsing whereUsing where

index_subquery

访问子查询的表时,使用的是普通索引,转换为exists

explain
select *
from single_table s1
where common_field in (select key3 from single_table s2 where s1.common_field = s2.common_field)
   or key3 = 'a';
id12
select_typePRIMARYDEPENDENT SUBQUERY
tables1s2
partitionsNULLNULL
typeALLindex_subquery
possible_keysidx_key3idx_key3
keyNULLidx_key3
key_lenNULL303
refNULLfunc
rows99372
filtered10010
ExtraUsing whereUsing where

range

使用索引进行查询,扫描区间是一些单点扫描区间或范围区间

explain select *
from single_table
where key1 in ('a', 'b');
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typerange
possible_keysidx_key1
keyidx_key1
key_len303
refNULL
rows274
filtered100
ExtraUsing index condition

index

使用到索引覆盖,且扫描了所有的索引

因为普通索引的记录只包含索引列、主键值,聚簇索引记录包含用户定义的所有列和隐藏字段,扫描所有普通索引的成本比扫描全部聚簇索引的成本低一些

explain
select key_part1, key_part2
from single_table
where key_part3 = 'b';
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typeindex
possible_keysNULL
keyidx_part
key_len909
refNULL
rows9937
filtered10
ExtraUsing where; Using index

select_type

MySQL为每一个select关键字代表的小查询都定义了select_type,根据select_type,我们可以知道这个select关键字的小查询的类型

类型

  1. simple
  2. primary
  3. union
  4. union result
  5. subquery
  6. dependent subquery
  7. dependent union
  8. derived
  9. materialized
  10. uncacheable subquery
  11. uncacheable union

simple

不包含子查询或union的查询、连接查询的select_type是simple

primary

对于包含子查询、union、union all的查询语句,最左边的查询就是primary

union

对于包含union、union all的查询语句,除了最左边的查询,其余都是小查询的类型都是union

explain
select *
from single_table s1
union
select *
from single_table s2;
id12NULL
select_typePRIMARYUNIONUNION RESULT
tables1s2<union1,2>
partitionsNULLNULLNULL
typeALLALLALL
possible_keysNULLNULLNULL
keyNULLNULLNULL
key_lenNULLNULLNULL
refNULLNULLNULL
rows99379937NULL
filtered100100NULL
ExtraNULLNULLUsing temporary

union result

MySQL使用临时表完成union子句的去重操作

explain
select *
from single_table s1
union
select *
from single_table s2;
id12NULL
select_typePRIMARYUNIONUNION RESULT
tables1s2<union1,2>
partitionsNULLNULLNULL
typeALLALLALL
possible_keysNULLNULLNULL
keyNULLNULLNULL
key_lenNULLNULLNULL
refNULLNULLNULL
rows99379937NULL
filtered100100NULL
ExtraNULLNULLUsing temporary

subquery

包含子查询的查询语句不能转换为半连接,并且子查询是不相关子查询,优化器决定采用将该子查询物化的方案来执行该子查询时,该子查询的select_type是subquery

explain
select *
from single_table s1
where key1 in (select key1 from single_table s2)
   or key3 = 'a';
id12
select_typePRIMARYSUBQUERY
tables1s2
partitionsNULLNULL
typeALLindex
possible_keysidx_key3idx_key1
keyNULLidx_key1
key_lenNULL303
refNULLNULL
rows99379937
filtered100100
ExtraUsing whereUsing index

dependent subquery

包含子查询的查询语句不能转换为半连接,并且子查询是相关子查询,子查询的第一个select关键字代表的查询的select_type是dependent subquery

select_type为dependent subquery的子查询可能会被执行多次

explain
select *
from single_table s1
where key1 in (select key1 from single_table s2 where s1.key2 = s2.key2)
   or key3 = 'a';
id12
select_typePRIMARYDEPENDENT SUBQUERY
tables1s2
partitionsNULLNULL
typeALLref
possible_keysidx_key3uk_key2,idx_key1
keyNULLuk_key2
key_lenNULL5
refNULLmysql_run.s1.key2
rows99371
filtered10010
ExtraUsing whereUsing where

dependent union

包含union或union all的查询语句,如果各个小查询都依赖于外层查询,那么除了最左边的小查询之外,其余的小查询的select_type都是dependent union

下方的查询语句中,select key1 from single_table s2 where key1 = 'a'是最左边的小查询,它的select_type是dependent subquery,

select key1 from single_table s1 where key3 = 'a'的select_type是dependent union

explain
select *
from single_table s1
where key1 in
      (select key1 from single_table s2 where key1 = 'a' union select key1 from single_table s1 where key3 = 'a');
id123NULL
select_typePRIMARYDEPENDENT SUBQUERYDEPENDENT UNIONUNION RESULT
tables1s2single_table<union2,3>
partitionsNULLNULLNULLNULL
typeALLrefrefALL
possible_keysNULLidx_key1idx_key1NULL
keyNULLidx_key1idx_key1NULL
key_lenNULL303303NULL
refNULLconstfuncNULL
rows99371382NULL
filtered100100100NULL
ExtraUsing whereUsing where; Using indexUsing where; Using indexUsing temporary

derived

包含派生表的查询中,以物化表的方式进行查询,派生表对应的子查询的select_type是dervied

explain
select *
from (select key1, count(*) from single_table group by key1) t;
id12
select_typePRIMARYDERIVED
table<derived2>single_table
partitionsNULLNULL
typeALLindex
possible_keysNULLidx_key1
keyNULLidx_key1
key_lenNULL303
refNULLNULL
rows99379937
filtered100100
ExtraNULLUsing index

materialized

对于包含子查询的查询语句,优化器选择将子查询物化后与外层查询进行连接,子查询的select_type就是materialized

下面的执行计划中,查询优化器把子查询转换为物化表,第二条记录中,的table是<subquery2>,这个表是id为2的子查询进行查询后产生的物化表,然后在与s1表进行连接

explain
select *
from single_table s1
where key1 in (select key1 from single_table s2);
id112
select_typeSIMPLESIMPLEMATERIALIZED
tables1<subquery2>s2
partitionsNULLNULLNULL
typeALLeq_refindex
possible_keysidx_key1<auto_key>idx_key1
keyNULL<auto_key>idx_key1
key_lenNULL303303
refNULLmysql_run.s1.key1NULL
rows993719937
filtered100100100
ExtraUsing whereNULLUsing index

possible_keys和key

possiable_keys列是对表进行查询时,可能使用到的索引

key列是对表进行查询时,实际使用的索引

优化器进行成本计算后,选择使用uk_key2索引对表进行查询

possiable_keys并不是越多越好,在执行查询前,优化器需要对索引进行成本,索引越多,计算成本时的花费时间也就越长

explain
select *
from single_table
where key1 > 'a'
  and key2 = 13467;
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typeconst
possible_keysuk_key2,idx_key1
keyuk_key2
key_len5
refconst
rows1
filtered100
ExtraNULL

key_len

当使用索引来执行查询时,要弄清楚查询条件形成的扫描区间,以及扫描区间的边界条件

我们可以根据执行计划的key_len了解边界条件有关索引列的相关信息

explain
select *
from single_table
where key1 > 'a'
  and key1 < 'd';
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typerange
possible_keysidx_key1
keyidx_key1
key_len303
refNULL
rows899
filtered100
ExtraUsing index condition

key_len的值为303

key_len的值由3部分组成:

  1. 该列数据实际最多占用的字节数
  2. 如果该列可以存储Null,需要有1字节来维护
  3. 如果该列数据是变长的,需要2字节来维护

在这个表中,key1列数据类型是varchar(100),使用的是utf8字符集,且它允许为空,它的key_len组成是

  1. 3×100=300(utf8字符最多占用3字节,key1的类型长度是100)
  2. 1字节维护存储Null值
  3. 2字节维护变长存储

在InnoDB的记录行格式中,存储变长字段的部分可能是1字节或2字节,但执行计划是server层的功能,server层与存储引擎表示记录的方式是不一样的

对于Id列,它是int型,占用4字节,且不是变长、不允许存储Null

explain
select *
from single_table
where id > 10
  and id < 100;
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typerange
possible_keysPRIMARY
keyPRIMARY
key_len4
refNULL
rows89
filtered100
ExtraUsing where

联合索引与key_len

key_len主要是为了让我们在使用联合索引进行查询时,了解到优化器使用索引涉及到哪些列

explain
select *
from single_table
where key_part1 = 'a'
  and key_part2 > 'c';
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typerange
possible_keysidx_part
keyidx_part
key_len606
refNULL
rows106
filtered100
ExtraUsing index condition

key_len为606,MySQL在使用上面的查询语句时,会使用到key_part1、key_part2这两个列来形成扫描区间

ref

ref列的内容表示与索引列进行等值匹配的内容(常数、表的一个列)

explain
select *
from single_table
where key1 = 'a';
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typeref
possible_keysidx_key1
keyidx_key1
key_len303
refconst
rows138
filtered100
ExtraNULL
explain
select *
from single_table s1
         join single_table s2
where s1.key1 = s2.common_field;
id11
select_typeSIMPLESIMPLE
tables2s1
partitionsNULLNULL
typeALLref
possible_keysNULLidx_key1
keyNULLidx_key1
key_lenNULL303
refNULLmysql_run.s2.common_field
rows99372
filtered100100
ExtraUsing whereNULL

执行计划中的第2条记录中的ref是 mysql_run.s2.common_field,表示在对s1表进行访问时,与s1表的key1列的等值匹配的是s2.common_field的列

explain
select *
from single_table s1
         join single_table s2 on s2.key1 = lower(s1.key3);
id11
select_typeSIMPLESIMPLE
tables1s2
partitionsNULLNULL
typeALLref
possible_keysNULLidx_key1
keyNULLidx_key1
key_lenNULL303
refNULLfunc
rows99372
filtered100100
ExtraNULLUsing index condition

对被驱动表s2进行查询时,与s2表的key1进行等值匹配的对象是一个函数

rows

如果优化器决定采用全表扫描的方式进行查询,那么rows的值就是对表进行全表扫描时预估的行数。

如果优化器决定采用索引来执行查询,那么rows的值就代表预计扫描索引的记录行数。

filterd

在分析连接扫描的成本时,会进行条件过滤

如果MySQL采用全表扫描的方式来进行单表查询,那么在计算驱动表扇出时,需要估计满足全部查询条件的记录的行数。

如果MySQL采用索引来执行单表扫描,那么计算驱动表扇出时,需要估计满足索引扫描区间外,还满足其他查询条件的记录行数

explain
select *
from single_table s1
         join single_table s2 on s1.key1 = s2.key3 and s1.common_field < 'b';
id11
select_typeSIMPLESIMPLE
tables1s2
partitionsNULLNULL
typeALLref
possible_keysidx_key1idx_key3
keyNULLidx_key3
key_lenNULL303
refNULLmysql_run.s1.key1
rows99372
filtered33.33100
ExtraUsing whereNULL

在单表查询里,filtered没有什么意义,在上面的执行计划中我们可以了解到,优化器在执行上面的查询语句时,选择将s1表作为驱动表,s2表作为被驱动表,在执行计划的第一条记录中,filtered的值是1/3,9937÷3≈3312,也就是说,还需要对被驱动表执行大约3312次

Extra

执行计划的额外信息

No tables used

没有from子句

explain select 1

Impossible WHERE

where子句永远为false

explain
select *
from single_table
where false = true;

No matching min/max row

查询语句有min、max,但记录不满足where的条件

explain
select min(key2)
from single_table
where key2 = 10;

Using index

使用覆盖索引进行查询

Using index condition

索引条件下推,搜索条件出现了索引列,但没有利用它形成扫描区间(不能使用这个条件来减少记录扫描的数量)

explain
select *
from single_table
where key_part1 < 'd' # 条件1
  and key_part2 < 'd'; # 条件2
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typerange
possible_keysidx_part
keyidx_part
key_len303
refNULL
rows1080
filtered33.33
ExtraUsing index condition

key_len为303,上面的查询,只使用了key_part1列来形成扫描区间。

对于条件2 key_part2 < 'd',MySQL使用了索引条件下推

如果没有索引下推,在扫描到符合条件1 key_part1 < 'd'的记录后,MySQL会根据记录的主键进行回表操作,将完整的用户记录返回给客户端,再server层判断该记录是否符合条件2

如果查询的条件中只有条件1,在执行计划额外信息中也会包含Using index condition,这可能是MySQL在编码上的处理

InnoDB规定索引条件下推只适用于非聚簇索引

Using where

某个查询条件需要在server层进行判断

Using join buffer (Block Nested Loop)

在连接查询中,对被驱动访问不能有效利用索引加快访问速度时,MySQL会为其分配一块连接缓冲区,它是
基于块的嵌套循环算法来执行连接查询的。

explain
select *
from single_table s1
         join single_table s2 on s1.common_field = s2.common_field;
id11
select_typeSIMPLESIMPLE
tables1s2
partitionsNULLNULL
typeALLALL
possible_keysNULLNULL
keyNULLNULL
key_lenNULLNULL
refNULLNULL
rows99379937
filtered10010
ExtraNULLUsing where; Using join buffer (Block Nested Loop)

Using [intersect, union, sort_union]

使用了索引合并

Zero limit

出现了 limt 0

Using filesort

对结果集的记录进行排序时,但没有使用到索引

MySQL把在内存或磁盘中进行的排序称为文件排序

explain
select *
from single_table
order by common_field limit 10;
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typeALL
possible_keysNULL
keyNULL
key_lenNULL
refNULL
rows9937
filtered100
ExtraUsing filesort

Using temporary

MySQL需要借助临时表来完成一些功能(去重、排序等),如果不能有效利用索引完成这些功能,MySQL很有可能创建临时表来执行一些查询(包含distinct、order by、union等),应尽量避免查询中出现创建临时表的情况

explain
select  common_field
from single_table group by common_field;
id1
select_typeSIMPLE
tablesingle_table
partitionsNULL
typeALL
possible_keysNULL
keyNULL
key_lenNULL
refNULL
rows9937
filtered100
ExtraUsing temporary; Using filesort

使用group by时,将默认使用order by

Start temporary与End temporary

优化器优先将in子查询转换为半连接,如果采用的半连接策略是重复消除(Duplicate Weedout),也就是通过建立临时表为外层查询去重时,驱动表的执行计划记录额外信息就包含Start temporary,被驱动表包含 End temporary

explain
select *
from single_table s1
where key1 in (select common_field from single_table s2 where s1.key3 = 'a');
id11
select_typeSIMPLESIMPLE
tables2s1
partitionsNULLNULL
typeALLref
possible_keysNULLidx_key1,idx_key3
keyNULLidx_key1
key_lenNULL303
refNULLmysql_run.s2.common_field
rows99372
filtered1002.1
ExtraUsing where; Start temporaryUsing where; End temporary

LooseScan

半连接的执行策略是LooseScan

FirstMatch(表名)

半连接的执行策略是FirstMatch

explain
select *
from single_table s1
where key1 in (select common_field from single_table s2 where s1.key3 = s2.key3);
id11
select_typeSIMPLESIMPLE
tables1s2
partitionsNULLNULL
typeALLref
possible_keysidx_key1,idx_key3idx_key3
keyNULLidx_key3
key_lenNULL303
refNULLmysql_run.s1.key3
rows99372
filtered10010
ExtraUsing whereUsing where; FirstMatch(s1)

optimizer trace

MySQL提供了 optimizer trace 功能,它可以让用户查看优化器生成执行计划的整个过程

show variables like 'optimizer_trace';
set optimizer_trace = 'enabled=on';

开启optimizer trace功能后,我们进行查询(使用explain语句也可以)后,可以在相关表中查看执行计划生成的过程。

这个表的结构为:

  • query:输入的语句
  • trace:优化过程的JSON文本
  • missing bytes beyond max mem size:被忽略的文本字节数
select *
from information_schema.OPTIMIZER_TRACE;

优化的过程大致分为三个阶段:

  1. prepare
  2. optimize
  3. execute

基于成本的优化主要集中在optimize阶段

对于单表查询,我们关注的是optimize阶段中的rows_estimation阶段(预估不同单表访问的成本)

对于多表连接查询,我们更关注·considered_execution_plans·阶段(分析可能的执行计划)

  • 0
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:创作都市 设计师:CSDN官方博客 返回首页
评论

打赏作者

011eH

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

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值