mysql索引使用原则与explain分析语句

1. explain分析语句

通过对查浦酒句的分析,可以了解查询语句的执行情况,找出查询语向执行的瓶颈,从而优化查询语句。MySQL中提供了EXPLAIN语句,用来分析查询语句。本节介绍使用EXPLAIN语句分析查询语句的方法。

EXPLAIN语句的基本语法如下:

EXPLAIN [EXTENDED] SELECT select_ options

使用EXTENED关键字,EXPLAIN语句将产生附加信息。select options 是SELECT语句的查询选项,包括FROM WHERE子句等。

执行该语句,可以分析EXPLAIN后面的SELECT语句的执行情况,并且能够分析出所查 询的表的一些特征。

使用EXPLAIN语句来分析1个查询语句,执行如下语句:

mysql> mysql> explain select * from article where status = 0;
+----+-------------+---------+------------+------+---------------+------+---------+------+----------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows     | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+----------+----------+-------------+
|  1 | SIMPLE      | article | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 10856301 |    10.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+----------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

下面我们对查询结果进行解释:

字段描述
idselect识别符。这是select的查询序列号,id的值越大优先级别越高,越先被执行,如果id相同,执行顺序右上至下
select_typeselect_type表示select语句的类型。
tabletype表示这一行的数据是关于哪张表的。
partitions
typetype表示的是表的连接类型
possible_keyspossible_keys列支出mysql能使用哪个索引在该表中找到行。如果这列是null,则没有相关的索引。在这种情况下,可以通过检查where子句查看它是否引用某些列或者适合索引的列来提高查询性能。如果是这样,可以创建适合的索引来提高查询的性能
keykey表示查询实际应用到的索引,如果没有选择索引,该列的值是null。想要强制使mysql使用或者忽视possible_type列中的索引,在查询中使用force index,use index 或者 ignore index。
key_lenkey_len表示mysql选择的索引字段按字节计算的长度,如果键是null,则长度为null,注意通过key_len值可以确定mysql将实际使用一个多列索引中的几个字段
refref表示使用哪个列或常数与索引一起来查询记录
rowsrows显示mysql表中进行查询时必须检查的行数
filtered
Extraextra显示mysql在处理查询时的详细信息。

对于以上字段信息不同值的解释:

1.select_type

(1) SIMPLE

SIMPLE表示简单查询,其中不包括连接查询和子查询。

(2) PRIMARY与SUBQUERY

PRIMARY表示主查询或者最外层的查询语句。SUBQUERY : 子查询
例如:

mysql> explain select * from article where id = (select id from article where content  = 'LrQ2Ievq9P');

+----+-------------+---------+------------+-------+---------------+---------+---------+-------+----------+----------+-------------+
| id | select_type | table   | partitions | type  | possible_keys | key     | key_len | ref   | rows     | filtered | Extra       |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+----------+----------+-------------+
|  1 | PRIMARY     | article | NULL       | const | PRIMARY       | PRIMARY | 4       | const |        1 |   100.00 | NULL        |
|  2 | SUBQUERY    | article | NULL       | ALL   | NULL          | NULL    | NULL    | NULL  | 10856301 |    10.00 | Using where |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+----------+----------+-------------+
2 rows in set, 1 warning (16.28 sec)

(3) DERIVED: 衍生查询-在select出一批自定义列的数据,概念上相当于一张表,但是该表只在语句执行过程出现和

(4) UNION 与 UNION RESULT
UNION :联合查询,union 后面的那张表就会表示成它
UNION RESULT: 联合结果

例如:

mysql> explain select * from article where id = 6565544 union all select * from article where id = 8484848 ;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | PRIMARY     | article | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
|  2 | UNION       | article | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)

2.type

对表访问方式,表示MySQL在表中找到所需行的方式,又称“访问类型”。

常用的类型有: ALL、index、range、 ref、eq_ref、const、system、NULL(从左到右,性能从差到好)

ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行

index: Full Index Scan,index与ALL区别为index类型只遍历索引树

range:只检索给定范围的行,使用一个索引来选择行

ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件

const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system

NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。

3.Extar

Extra列是用来说明一些额外信息的,我们可以通过这些额外信息来更准确的理解MySQL到底将如何执行给定的查询语句。MySQL提供的额外信息有好几十个,就不一个一个介绍了,在这只介绍常见的一些额外信息说明 .

① Using filesort: 如果根据索引列进行排序(order by 索引列)是可以用到索引的,SQL查询引擎会先根据索引列进行排序,然后获取对应记录的主键id执行回表操作,如果排序字段用不到索引则只能在内存中或磁盘中进行排序操作,MySQL把这种在内存或者磁盘上进行排序的方式统称为文件排序(英文名:filesort),如果某个查询需要使用文件排序的方式执行查询,就会在执行计划的Extra列中显示Using filesort

② Using temporary: 许多查询的执行过程中,MySQL会借助临时表来完成一些功能,比如去重、排序之类的,比如我们在执行许多包含distinct、group by、union等子句的查询过程中,如果不能有效利用索引来完成查询,MySQL很有可能寻求通过建立内部的临时表来执行查询。如果查询中使用到了内部的临时表,在执行计划的Extra列将会显示Using temporary提示.

③ USING index: 表示相应的select操作中使用了覆盖索引(Covering Index),避免回表操作,效率不错!
如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表名索引用来读取数据而非执行查找动作。

④ Using where: 使用了where过滤

⑤ using join buffer: 在连接查询执行过程中,当被驱动表不能有效的利用索引加快访问速度,MySQL一般会为其分配一块名叫join buffer的内存块来加快查询速度

⑥ impossible where: where子句的值总是false,不能用来获取任何元组

⑦ select tables optimized away: 在没有GROUPBY子句的情况下,基于索引优化MIN/MAX操作或者对于MyISAM存储引擎优化COUNT(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。

⑧ distinct: 优化distinct,在找到第一匹配的元组后即停止找同样值的工作

⑨ Using index condition:查找使用了索引,但是需要回表查询数据

2. 联合索引结构与索引匹配原则

最左前缀匹配原则:在MySQL建立联合索引时会遵守最左前缀匹配原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。

要想理解联合索引的最左匹配原则,先来理解下索引的底层原理。索引的底层是一颗B+树,那么联合索引的底层也就是一颗B+树,只不过联合索引的B+树节点中存储的是键值。由于构建一棵B+树只能根据一个值来确定索引关系,所以数据库依赖联合索引最左的字段来构建。

举例:创建一个(a,b)的联合索引,那么它的索引树就是下图的样子。
在这里插入图片描述

可以看到a的值是有顺序的,1,1,2,2,3,3,而b的值是没有顺序的1,2,1,4,1,2。但是我们又可发现a在等值的情况下,b值又是按顺序排列的,但是这种顺序是相对的。这是因为MySQL创建联合索引的规则是首先会对联合索引的最左边第一个字段排序,在第一个字段的排序基础上,然后在对第二个字段进行排序。所以b=2这种查询条件没有办法利用索引。

示例:

表:

字段类型描述
idint(11)主键
namevarchar(10)名称
ageint(11)年龄

该表中对id列.name列.age列建立了一个联合索引 id_name_age_index,实际上相当于建立了三个索引(id)(id_name)(id_name_age)。

下面介绍下可能会使用到该索引的几种情况:

1.全值匹配查询时

mysql> explain select * from staffs where name = 'shine' and sex = 1 and age = 20;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref               | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 40      | const,const,const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where age = 20 and name = 'shine' and sex = 1;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref               | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 40      | const,const,const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where sex = 1 and age = 20 and name = 'shine';
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref               | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 40      | const,const,const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

通过观察上面的结果图可知,where后面的查询条件,不论是使用(name,age,sex)(age,name,sex)还是(sex,age,name)顺序,在查询时都使用到了联合索引,可能有同学会疑惑,为什么底下两个的搜索条件明明没有按照联合索引从左到右进行匹配,却也使用到了联合索引? 这是因为MySQL中有查询优化器explain,所以sql语句中字段的顺序不需要和联合索引定义的字段顺序相同,查询优化器会判断纠正这条SQL语句以什么样的顺序执行效率高,最后才能生成真正的执行计划,所以不论以何种顺序都可使用到联合索引

2.匹配最左边列时

mysql> explain select * from staffs where name = 'shine';
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 33      | const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

该搜索是遵循最左匹配原则的,通过key字段也可知,在搜索过程中使用到了联合索引,且使用的是联合索引中的(id)索引

mysql> explain select * from staffs where name = 'shine' and age = 20;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref         | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 38      | const,const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

由于name到age是从左边依次往右边匹配,这两个字段中的值都是有序的,所以也遵循最左匹配原则,通过key字段可知,在搜索过程中也使用到了联合索引,但使用的是联合索引中的(name_age)索引

mysql> explain select * from staffs where name = 'shine' and age = 20 and sex = 1;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref               | rows | filtered | Extra       |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 40      | const,const,const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------------------+------+----------+-------------+
1 row in set, 1 warning (0.01 sec)

由于上面三个搜索都是从最左边name依次向右开始匹配的,所以都用到了name_age_sex联合索引。

那如果不是依次匹配呢?

mysql> mysql> explain select * from staffs where name = 'shine' and sex = 1;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra                    |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 33      | const |    1 |    20.00 | Using where; Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

通过key字段可知,在搜索过程中也使用到了联合索引,但使用的是联合索引中的(name)索引,因为联合索引树是按照name字段创建的,但sex相对于name来说是无序的,只有name是有序的,所以他只能使用联合索引中的name索引。

mysql> explain select * from staffs where age = 20;
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | NULL          | idx_name_age_sex | 40      | NULL |    5 |    20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

通过观察发现上面key字段发现在搜索中也使用了name_age_sex索引,可能许多同学就会疑惑它并没有遵守最左匹配原则,按道理会索引失效,为什么也使用到了联合索引?因为没有从id开始匹配,且name单独来说是无序的,所以它确实不遵循最左匹配原则,然而从type字段可知,它虽然使用了联合索引,但是它是对整个索引树进行了扫描,正好匹配到该索引,与最左匹配原则无关,一般只要是某联合索引的一部分,但又不遵循最左匹配原则时,都可能会采用index类型的方式扫描,但它的效率远不如最做匹配原则的查询效率高,index类型类型的扫描方式是从索引第一个字段一个一个的查找,直到找到符合的某个索引,与all不同的是,index是对所有索引树进行扫描,而all是对整个磁盘的数据进行全表扫描。

mysql> explain select * from staffs where sex = 1;
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | NULL          | idx_name_age_sex | 40      | NULL |    5 |    20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where age = 20 and sex = 1;
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | NULL          | idx_name_age_sex | 40      | NULL |    5 |    20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

这两个结果跟上面的是同样的道理,由于它们都没有从最左边开始匹配,所以没有用到联合索引,使用的都是index全索引扫描。

3.匹配列前缀

对于模糊匹配的查询,如果是前缀匹配用的是索引,中坠和后缀用的是全表扫描

mysql> explain select * from staffs where name like "shie%";
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys    | key              | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | range | idx_name_age_sex | idx_name_age_sex | 33      | NULL |    1 |   100.00 | Using where; Using index |
+----+-------------+--------+------------+-------+------------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where name like "%ara%";
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | NULL          | idx_name_age_sex | 40      | NULL |    5 |    20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select * from staffs where name like "%ara";
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key              | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | NULL          | idx_name_age_sex | 40      | NULL |    5 |    20.00 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

3. mysql对于索引优先考虑的对象


一般我们在开发项目时对于一些业务难免会写出一些条件+分组/排序的sql语句,而通常也是这些sql会导致mysql的性能变差,这个时候我们想到使用mysql的索引来进行优化,但是mysql的索引对于条件,分组,排序都存在的情况下是如何去选择索引的呢?

下面我们根据上面的问题来进行一些测试以及分析。

条件与分组排序共存的情况下

mysql> explain select sex,age from staffs where name = "shine" group by sex order by age;
+----+-------------+--------+------------+------+--------------------------+------------------+---------+-------+------+----------+-----------------------------------------------------------+
| id | select_type | table  | partitions | type | possible_keys            | key              | key_len | ref   | rows | filtered | Extra                                                     |
+----+-------------+--------+------------+------+--------------------------+------------------+---------+-------+------+----------+-----------------------------------------------------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex,idx_sex | idx_name_age_sex | 33      | const |    1 |   100.00 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+--------+------------+------+--------------------------+------------------+---------+-------+------+----------+-----------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)

当sql中where条件,分组,排序同时存在时,MySQL的优化器会优先选择条件来确定使用的索引,因为where可以减少更多的sql扫描,而排序和分组往往进行的是全表扫描。

条件与排序共存

mysql> explain select sex,age from staffs where name = "shine" order by age;
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
| id | select_type | table  | partitions | type | possible_keys    | key              | key_len | ref   | rows | filtered | Extra                    |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_name_age_sex | idx_name_age_sex | 33      | const |    1 |   100.00 | Using where; Using index |
+----+-------------+--------+------------+------+------------------+------------------+---------+-------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

原因:所有的排序都是在条件过滤之后才执行的,所以如果条件过滤了大部分数据的话,几百几千条数据进行排序其实并不是很消耗性能,即使索引优化了排序但实际提升性能很有限。

分组排序共存

mysql> explain select sex,age from staffs group by sex order by age;
+----+-------------+--------+------------+-------+--------------------------+---------+---------+------+------+----------+---------------------------------+
| id | select_type | table  | partitions | type  | possible_keys            | key     | key_len | ref  | rows | filtered | Extra                           |
+----+-------------+--------+------------+-------+--------------------------+---------+---------+------+------+----------+---------------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | idx_name_age_sex,idx_sex | idx_sex | 2       | NULL |    5 |   100.00 | Using temporary; Using filesort |
+----+-------------+--------+------------+-------+--------------------------+---------+---------+------+------+----------+---------------------------------+
1 row in set, 1 warning (0.00 sec)

对于分组和排序共存的情况下,mysql会优先根据分组去选择索引,那是因为sql需要先将要查询的数据进行分组,随后才会进行数据的排序。

5. mysql索引的挑选原则


注:字段一般是推荐重复比较少的字段影响到数据的检索,如果是项目需求(可建立联合索引)

  1. 唯一字段可以单独建立单索引,非唯一考虑联合索引,推荐尽量使用唯一字段建立索引
  2. 索引的个数,联合索引的个数 最佳 6个 以内,如果索引因为项目需求:最多 10个
  3. 索引的使用遵循最左匹配原则其次覆盖索引
  4. 尽量选择小的字段建立索引 int ,varchar(10), char(5)
  5. 避免<,<= ,> ,>= , % ,between 之前的条件。选择索引的字段的范围和模糊之前,因为范围与模糊会引起索引失效,针对于联合索引,就是联合索引的中间尽量不要有范围查询的字段
  6. 尽量多使用explain分析
  7. 避免更新频繁的字段 (二叉树会一直变化,导致性能变慢)
  8. 建立的索引- 优先考虑 建立 联合索引
  9. 索引字段不要有 null, 不是 ‘’
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符 “速评一下”
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页