当前位置: 首页 > news >正文

Mysql学习第二篇

一、Explain 详解与索引最佳实践

1.准备表

1.1.使用的表

-- 创建学员表(无外键约束)
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (`student_id` INT NOT NULL AUTO_INCREMENT COMMENT '学员ID',`student_name` VARCHAR(50) NOT NULL COMMENT '学员名称',`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',PRIMARY KEY (`student_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学员表';-- 创建课程表(无外键约束)
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (`course_id` INT NOT NULL AUTO_INCREMENT COMMENT '课程ID',`course_name` VARCHAR(100) NOT NULL COMMENT '课程名称',`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',PRIMARY KEY (`course_id`),KEY `idx_course_name` (`course_name`) COMMENT '课程名称索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程表';-- 创建学员课程关联表(无外键约束)
DROP TABLE IF EXISTS `student_course`;
CREATE TABLE `student_course` (`id` INT NOT NULL AUTO_INCREMENT COMMENT '关联表自增主键',`student_id` INT NOT NULL COMMENT '学员ID',`course_id` INT NOT NULL COMMENT '课程ID',`remark` VARCHAR(255) DEFAULT NULL COMMENT '备注',`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',PRIMARY KEY (`id`),KEY `idx_student_course` (`student_id`, `course_id`) COMMENT '学员课程联合索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学员课程关联表';

1.2.向三张表中各添加100条数据

-- ========== 1. 向学员表插入100条数据 ==========
INSERT INTO `student` (`student_name`) VALUES
('学员_001'), ('学员_002'), ('学员_003'), ('学员_004'), ('学员_005'),
('学员_006'), ('学员_007'), ('学员_008'), ('学员_009'), ('学员_010'),
('学员_011'), ('学员_012'), ('学员_013'), ('学员_014'), ('学员_015'),
('学员_016'), ('学员_017'), ('学员_018'), ('学员_019'), ('学员_020'),
('学员_021'), ('学员_022'), ('学员_023'), ('学员_024'), ('学员_025'),
('学员_026'), ('学员_027'), ('学员_028'), ('学员_029'), ('学员_030'),
('学员_031'), ('学员_032'), ('学员_033'), ('学员_034'), ('学员_035'),
('学员_036'), ('学员_037'), ('学员_038'), ('学员_039'), ('学员_040'),
('学员_041'), ('学员_042'), ('学员_043'), ('学员_044'), ('学员_045'),
('学员_046'), ('学员_047'), ('学员_048'), ('学员_049'), ('学员_050'),
('学员_051'), ('学员_052'), ('学员_053'), ('学员_054'), ('学员_055'),
('学员_056'), ('学员_057'), ('学员_058'), ('学员_059'), ('学员_060'),
('学员_061'), ('学员_062'), ('学员_063'), ('学员_064'), ('学员_065'),
('学员_066'), ('学员_067'), ('学员_068'), ('学员_069'), ('学员_070'),
('学员_071'), ('学员_072'), ('学员_073'), ('学员_074'), ('学员_075'),
('学员_076'), ('学员_077'), ('学员_078'), ('学员_079'), ('学员_080'),
('学员_081'), ('学员_082'), ('学员_083'), ('学员_084'), ('学员_085'),
('学员_086'), ('学员_087'), ('学员_088'), ('学员_089'), ('学员_090'),
('学员_091'), ('学员_092'), ('学员_093'), ('学员_094'), ('学员_095'),
('学员_096'), ('学员_097'), ('学员_098'), ('学员_099'), ('学员_100');-- ========== 2. 向课程表插入100条数据 ==========
INSERT INTO `course` (`course_name`) VALUES
('课程_001'), ('课程_002'), ('课程_003'), ('课程_004'), ('课程_005'),
('课程_006'), ('课程_007'), ('课程_008'), ('课程_009'), ('课程_010'),
('课程_011'), ('课程_012'), ('课程_013'), ('课程_014'), ('课程_015'),
('课程_016'), ('课程_017'), ('课程_018'), ('课程_019'), ('课程_020'),
('课程_021'), ('课程_022'), ('课程_023'), ('课程_024'), ('课程_025'),
('课程_026'), ('课程_027'), ('课程_028'), ('课程_029'), ('课程_030'),
('课程_031'), ('课程_032'), ('课程_033'), ('课程_034'), ('课程_035'),
('课程_036'), ('课程_037'), ('课程_038'), ('课程_039'), ('课程_040'),
('课程_041'), ('课程_042'), ('课程_043'), ('课程_044'), ('课程_045'),
('课程_046'), ('课程_047'), ('课程_048'), ('课程_049'), ('课程_050'),
('课程_051'), ('课程_052'), ('课程_053'), ('课程_054'), ('课程_055'),
('课程_056'), ('课程_057'), ('课程_058'), ('课程_059'), ('课程_060'),
('课程_061'), ('课程_062'), ('课程_063'), ('课程_064'), ('课程_065'),
('课程_066'), ('课程_067'), ('课程_068'), ('课程_069'), ('课程_070'),
('课程_071'), ('课程_072'), ('课程_073'), ('课程_074'), ('课程_075'),
('课程_076'), ('课程_077'), ('课程_078'), ('课程_079'), ('课程_080'),
('课程_081'), ('课程_082'), ('课程_083'), ('课程_084'), ('课程_085'),
('课程_086'), ('课程_087'), ('课程_088'), ('课程_089'), ('课程_090'),
('课程_091'), ('课程_092'), ('课程_093'), ('课程_094'), ('课程_095'),
('课程_096'), ('课程_097'), ('课程_098'), ('课程_099'), ('课程_100');-- 需要多次执行直到达到100条不重复记录
INSERT IGNORE INTO `student_course` (`student_id`, `course_id`, `remark`)
SELECT s.student_id,c.course_id,CONCAT('备注_', s.student_id, '_', c.course_id) AS remark
FROM (SELECT student_id FROM student ORDER BY RAND() LIMIT 100) sCROSS JOIN(SELECT course_id FROM course ORDER BY RAND() LIMIT 1) c
UNION ALL
SELECT s.student_id,c.course_id,CONCAT('备注_', s.student_id, '_', c.course_id)
FROM (SELECT student_id FROM student ORDER BY RAND() LIMIT 1) sCROSS JOIN(SELECT course_id FROM course ORDER BY RAND() LIMIT 100) c
LIMIT 100;

2.explain 中的列

2.1.id列

id列的编号是 select 的序号,有几个select 就有几个id,并且id的顺序是按 select 出现的顺序增长的。Mysql将select 查询分为简单查询(SIMPLE)和复杂查询(PRIMARY)。

复杂查询分为三类:简单子查询、派生表(from 语句中的子查询)、union 查询

id列值越大执行优先级越高,id相同则从上往下执行,id为null最后执行

  1. 简单子查询

    mysql> explain select (select 1 from student limit 1) from course;
    +----+-------------+---------+------------+-------+---------------+-----------------+---------+------+------+----------+-------------+
    | id | select_type | table   | partitions | type  | possible_keys | key             | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+---------+------------+-------+---------------+-----------------+---------+------+------+----------+-------------+
    |  1 | PRIMARY     | course  | NULL       | index | NULL          | idx_course_name | 402     | NULL |  100 |   100.00 | Using index |
    |  2 | SUBQUERY    | student | NULL       | index | NULL          | PRIMARY         | 4       | NULL |  100 |   100.00 | Using index |
    +----+-------------+---------+------------+-------+---------------+-----------------+---------+------+------+----------+-------------+
    2 rows in set (0.04 sec)
    
  2. from 子句中的子查询

    mysql> explain select id from (select * from student_course limit 10) temp;
    +----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------+
    | id | select_type | table          | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
    +----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------+
    |  1 | PRIMARY     | <derived2>     | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   10 |   100.00 | NULL  |
    |  2 | DERIVED     | student_course | NULL       | ALL  | NULL          | NULL | NULL    | NULL |  100 |   100.00 | NULL  |
    +----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------+
    2 rows in set (0.08 sec)
    
  3. union 查询

    mysql> explain select 1 union all select 1;
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra          |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
    |  1 | PRIMARY     | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL | NULL     | No tables used |
    |  2 | UNION       | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL | NULL     | No tables used |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
    2 rows in set (0.06 sec)
    

2.2.select_type列

  1. simple:简单查询。查询不包含子查询和union。
  2. primary:复杂查询中最外层的 select。
  3. subquery:包含在select 中的子查询(不在 from 子句中)或者条件中的子查询。
  4. derived:包含在from 子句中的子查询。Mysql会将结果存放在一个临时表中,也称为派生表(derived的英文含义)。
  5. union:在union中的第二个和随后的select

2.3.table列

这一列表示explain 的一行正在访问哪张表。

当from 子句中有子查询时,table列 格式,表示当前查询依赖id=N 的查询结果,于是先查询id列为N 的查询。

2.4.type列

这一列表示关联类型或访问类型,即Mysql 决定如何查找表中的行,查找数据行记录的大概范围。

从优到差分别是:system > const > eq_ref > ref >range > index > all

一般来说,要保证达到range级别,最好达到ref

  1. system 查询的表(派生表)只有一条数据。

  2. const 查询表时使用primary key 或 unique key 的等值查询,只会有一条结果。

    mysql> explain select * from student_course WHERE id= 1;
    +----+-------------+----------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
    | id | select_type | table          | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
    +----+-------------+----------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
    |  1 | SIMPLE      | student_course | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
    +----+-------------+----------------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
    1 row in set (0.05 sec)
    
  3. eq_ref 关联查询时关联到当前表的primary key 或 unique key 。当前表最多只会返回一条数据。关联student的primary key(主键索引)时,student_course表的每一条数据只会关联student的表的一条数据。

    mysql> explain select s.* from student_course as sc left join student as s on sc.student_id = s.student_id;
    +----+-------------+-------+------------+--------+---------------+--------------------+---------+-----------------------+------+----------+-------------+
    | id | select_type | table | partitions | type   | possible_keys | key                | key_len | ref                   | rows | filtered | Extra       |
    +----+-------------+-------+------------+--------+---------------+--------------------+---------+-----------------------+------+----------+-------------+
    |  1 | SIMPLE      | sc    | NULL       | index  | NULL          | idx_student_course | 8       | NULL                  |  100 |   100.00 | Using index |
    |  1 | SIMPLE      | s     | NULL       | eq_ref | PRIMARY       | PRIMARY            | 4       | db_test.sc.student_id |    1 |   100.00 | NULL        |
    +----+-------------+-------+------------+--------+---------------+--------------------+---------+-----------------------+------+----------+-------------+
    2 rows in set (0.04 sec)
    
  4. ref 相比eq_ref,不使用唯一索引,而是用普通索引或者唯一索引的部分前缀,索引要和某个值相比较,可能会找到多个符合条件的行。

    mysql> explain select * from course WHERE course_name = "课程_006";
    +----+-------------+--------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
    | id | select_type | table  | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra |
    +----+-------------+--------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
    |  1 | SIMPLE      | course | NULL       | ref  | idx_course_name | idx_course_name | 402     | const |    1 |   100.00 | NULL  |
    +----+-------------+--------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
    1 row in set (0.09 sec)
    
  5. range 范围查询 in()、between、>、< 等操作中。使用一个索引来检索给定范围。

  6. index 扫描全表索引,所有查询的字段都是索引,都在内存中,这通常比all 快一些(index 从内存中获取,all从硬盘中读取)

  7. all 扫描全表,意味着mysql需要从头到尾区查找所需要的行。

  8. null mysql 能够在优化阶段分解查询语句,在执行阶段用不着在访问表或索引。例如:在索引列中选取最小值,可以单独查找索引来完成,不需要在执行是访问。

    mysql> explain select max(course_id) from course;
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                        |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
    |  1 | SIMPLE      | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL | NULL     | Select tables optimized away |
    +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
    1 row in set (0.07 sec)
    

2.5.possible_keys列

可能用到索引的列

explain 时可能出现 possible_keys 列有值,而key 显示NULL 的情况,这种情况是因为表中数据不多,mysql认为索引对此查询的帮助不大,选择了全表扫描。

explain 时可能出现 possible_keys 列为NULL,而key 显示有值 的情况,这种情况是分析的时候认为不需要使用索引,执行引擎实际上使用了索引。

explain 时可能出现 possible_keys 列为NULL,key 列也为NULL,就要考虑添加索引。

2.6.key列

实际上使用索引的列

2.7.key_len列

这一列显示了mysql 在索引里使用的字节数,通过这个值可以算出具体使用了索引中的哪些列。

student_id = 1 查询使用联合索引idx_student_course 的 student_id,key_len为4。

student_id = 1 and course_id = 1 查询使用联合索引idx_student_course 的 student_id、course_id ,key_len为8

mysql> explain select id from student_course where student_id = 1 and course_id = 1;
+----+-------------+----------------+------------+------+--------------------+--------------------+---------+-------------+------+----------+-------------+
| id | select_type | table          | partitions | type | possible_keys      | key                | key_len | ref         | rows | filtered | Extra       |
+----+-------------+----------------+------------+------+--------------------+--------------------+---------+-------------+------+----------+-------------+
|  1 | SIMPLE      | student_course | NULL       | ref  | idx_student_course | idx_student_course | 8       | const,const |    1 |   100.00 | Using index |
+----+-------------+----------------+------------+------+--------------------+--------------------+---------+-------------+------+----------+-------------+
1 row in set (0.06 sec)

key_len 计算规则如下:

  • 字符串
    • char(n):n字节长度
    • varchar(n):2字节存储字符串长度,如果是utf-8,则长度是3n+2
  • 数值类型
    • tinyint:1字节
    • smallint:2字节
    • int:4字节
    • bigint:8字节
  • 时间类型
    • date:3字节
    • timestamp:4字节
    • datetime:8字节
  • 如果字段允许为NULL,需要1字节记录是否为NULL

2.8.ref列

这一列显示了key列记录的索引中,表查找到值所用到的列或者常量,常见的有:const(常量)、字段值。(就是找到key列对应的索引的值,使用的是常量如:a=1,还是另一个列的字段值如a.id=b.id)

2.9.rows列

这一列是mysql 估计要读取并检测的行数,注意这个并不是结果集里的行数。

2.10.Extra列

这一列是展示的额外的信息。

  1. Using index:查询的列被索引覆盖,并且where筛选条件是索引的前导列。一般使用了覆盖索引(索引包含了所有查询的字段)。对于innodb来说,如果是辅助索引性能会有不少提高。‘

    mysql> explain select student_id,course_id from student_course where student_id = 1;
    +----+-------------+----------------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
    | id | select_type | table          | partitions | type | possible_keys      | key                | key_len | ref   | rows | filtered | Extra       |
    +----+-------------+----------------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
    |  1 | SIMPLE      | student_course | NULL       | ref  | idx_student_course | idx_student_course | 4       | const |    1 |   100.00 | Using index |
    +----+-------------+----------------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------------+
    1 row in set (0.06 sec)
    
  2. Using where:查询的列未被索引覆盖,where筛选条件非索引的前导列。

    mysql> explain select * from student_course where remark = "备注_15_19";
    +----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
    | id | select_type | table          | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | student_course | NULL       | ALL  | NULL          | NULL | NULL    | NULL |  100 |    10.00 | Using where |
    +----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------------+
    1 row in set (0.05 sec)
    
  3. Using where; Using index:查询的列被索引覆盖,并且where筛选条件是索引列之一但不是索引的前导列,意味着无法直接通过索引查找来查询到符合条件的数据。

    --  student_id,course_id 为联合索引,course_id是索引列是索引之一,但不是前导列,查询结果是覆盖索引
    mysql> explain select student_id,course_id from student_course where course_id = 1;
    +----+-------------+----------------+------------+-------+---------------+--------------------+---------+------+------+----------+--------------------------+
    | id | select_type | table          | partitions | type  | possible_keys | key                | key_len | ref  | rows | filtered | Extra                    |
    +----+-------------+----------------+------------+-------+---------------+--------------------+---------+------+------+----------+--------------------------+
    |  1 | SIMPLE      | student_course | NULL       | index | NULL          | idx_student_course | 8       | NULL |  100 |    10.00 | Using where; Using index |
    +----+-------------+----------------+------------+-------+---------------+--------------------+---------+------+------+----------+--------------------------+
    1 row in set (0.04 sec)
    
  4. null :查询的列未被索引覆盖,并且where筛选条件是索引的前导列,意味着用到了索引,但是部分字段未被索引覆盖,必须通过“回表”来查询要的查询的列。

    mysql> explain select student_id,course_id, remark from student_course where student_id = 1;
    +----+-------------+----------------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------+
    | id | select_type | table          | partitions | type | possible_keys      | key                | key_len | ref   | rows | filtered | Extra |
    +----+-------------+----------------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------+
    |  1 | SIMPLE      | student_course | NULL       | ref  | idx_student_course | idx_student_course | 4       | const |    1 |   100.00 | NULL  |
    +----+-------------+----------------+------------+------+--------------------+--------------------+---------+-------+------+----------+-------+
    1 row in set (0.05 sec)
    
  5. Using index condition:与Using where 类似,查询的列不完全被索引覆盖,where条件中是一个前导列的范围(>、<等);

  6. Using temporary:mysql 需要创建一张临时表来处理。出现这种情况一般是要进行优化的,首先是想到使用索引来优化。student_name如果添加完索引就是 Using index。

    mysql> explain select DISTINCT student_name  from student;
    +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------+
    | id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra           |
    +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------+
    |  1 | SIMPLE      | student | NULL       | ALL  | NULL          | NULL | NULL    | NULL |  100 |   100.00 | Using temporary |
    +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------+
    1 row in set (0.04 sec)
    
  7. Using filesort:mysql 会对结果使用一个外部索引排序,而不是按索引次数从表里读取行。此时mysql会根据联结类型浏览所有有符合条件的记录,并保存排序关键字和行指针,然后排序关键字并按顺序检索行信息。这种情况也是要进行索引优化的。

    -- 如果student_name 添加完索引之后就 Using index
    mysql> explain select course_name from course order by course_name;
    +----+-------------+--------+------------+-------+---------------+-----------------+---------+------+------+----------+-------------+
    | id | select_type | table  | partitions | type  | possible_keys | key             | key_len | ref  | rows | filtered | Extra       |
    +----+-------------+--------+------------+-------+---------------+-----------------+---------+------+------+----------+-------------+
    |  1 | SIMPLE      | course | NULL       | index | NULL          | idx_course_name | 402     | NULL |  100 |   100.00 | Using index |
    +----+-------------+--------+------------+-------+---------------+-----------------+---------+------+------+----------+-------------+
    1 row in set (0.04 sec)
    

3.优化建议

3.1.全值匹配

如果使用联合索引,那么联合索引的筛选建议用全。

3.2.最左前缀原则

KEY idx_a_b_c (a,b,c) 联合索引。

先按a排序,a相同再按b排序,b相同再c排序。

where a= 会使用索引

where b= 不会使用索引

where c= 不会使用索引

where b= and c= 不会使用索引

where a= and c= a会使用索引,c不会使用索引

3.3.不要在索引列上做任何操作(计算、函数、类型转换)

mysql> explain select * from course where LENGTH(course_name) = 3;
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | course | NULL       | ALL  | NULL          | NULL | NULL    | NULL |  100 |   100.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set (0.04 sec)

3.4.联合索引字段不要使用范围查询

KEY idx_a_b_c (a,b,c) 联合索引。

where a> and b= and c= , 只有a,可以用到索引,b的等值查询无法使用索引,b需要去每一个a下面的每一个b对比,c亦然。

where a= and b> and c= ,a,b可以用到索引,c的等值查询无法使用索引,c需要去每个b下面的每一个c对比。

where a= and b= and c> ,a,b,c都可以到索引。

mysql> explain select id from student_course where student_id >10 and course_id = 20;
+----+-------------+----------------+------------+-------+--------------------+--------------------+---------+------+------+----------+--------------------------+
| id | select_type | table          | partitions | type  | possible_keys      | key                | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+----------------+------------+-------+--------------------+--------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | student_course | NULL       | range | idx_student_course | idx_student_course | 4       | NULL |   90 |    10.00 | Using where; Using index |
+----+-------------+----------------+------------+-------+--------------------+--------------------+---------+------+------+----------+--------------------------+
1 row in set (0.05 sec)

3.5.尽量使用覆盖索引,减少select * 语句

当待查询的字段在联合索引中,使用select 列名,达到覆盖索引,避免回表。

3.6.mysql在使用不等于时,无法使用索引

mysql在使用不等于时,无法使用索引,会导致全表扫描

mysql> explain select * from course where course_name != "你好";
+----+-------------+--------+------------+------+-----------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys   | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+-----------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | course | NULL       | ALL  | idx_course_name | NULL | NULL    | NULL |  100 |   100.00 | Using where |
+----+-------------+--------+------------+------+-----------------+------+---------+------+------+----------+-------------+
1 row in set (0.09 sec)

3.7.is null , is not null 也无法使用索引

mysql> explain select * from course where course_name is not null;
+----+-------------+--------+------------+------+-----------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys   | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+-----------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | course | NULL       | ALL  | idx_course_name | NULL | NULL    | NULL |  100 |    90.00 | Using where |
+----+-------------+--------+------------+------+-----------------+------+---------+------+------+----------+-------------+
1 row in set (0.09 sec)

3.8.like 以通配符开头("%abc")mysql 索引会失效变成全表扫描

mysql> explain select * from course where course_name like "%程%";
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | course | NULL       | ALL  | NULL          | NULL | NULL    | NULL |  100 |    11.11 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set (0.09 sec)-- 使用覆盖索引
mysql> explain select course_name from course where course_name like "%程%";
+----+-------------+--------+------------+-------+---------------+-----------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key             | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+-----------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | course | NULL       | index | NULL          | idx_course_name | 402     | NULL |  100 |    11.11 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+-----------------+---------+------+------+----------+--------------------------+
1 row in set (0.09 sec)

3.9.字符串不加单引号或者双引号可能会索引失效

3.10.少用or,用它连接时可能会索引失效

本文来自博客园,作者:TheLifelongLearner,转载请注明原文链接:https://www.cnblogs.com/The-Lifelong-Learner/p/20350757

http://www.jsqmd.com/news/964409/

相关文章:

  • 2026年6月百达翡丽中国区官方售后服务体系完成优化与全面升级 - 资讯速览
  • 2026积家维修避坑指南|认准官方变迁后正规网点 - 资讯速览
  • 2026年无锡六西格玛试听课怎么咨询?绿带1580黑带1980说明 - 众智商学院职业教育
  • 第1篇:《面试题:画一个STM32最小系统电路,每个元件的作用》
  • 破解复杂地形作业困境:非标履带底盘ACM全场景适配方法论如何提升作业效率? - 资讯速览
  • 2026百达翡丽官方售后布局全新调整,官方服务联络通道全面更新 - 资讯速览
  • GitHub 多项功能与解决方案揭秘:lowfat 轻量级 CLI 工具降低 AI 令牌成本
  • 140 美元的 Skylight Buddy 平板:孩子爱不释手,家长省心省力!
  • CatRouter网络评测:2026年AI API网关的技术真相
  • 北京丰宝斋:天津上门回收,不止是变现,更是文化的守护 - 深鉴新闻
  • M9A:重返未来1999智能自动化助手终极指南
  • 如何在Inkscape中实现专业级光学设计:免费光线追踪插件完整指南
  • ThinkPad双风扇终极控制指南:从噪音困扰到静音高效的全流程解决方案
  • 从VGG到ResNet:一张参数表看懂深度学习模型是如何‘变深’又‘变瘦’的
  • 2026指南:涡旋压缩机领域实力品牌深度分析 - 品牌企业推荐师(官方)
  • 量子计算工程实践笔记:Sycamore硬件运维与噪声治理实录
  • 寄大件家电用什么快递最省钱 2026物流价格对比 - 快递物流资讯
  • 第2篇:《面试题:LDO和DC-DC的区别?分别用在什么场景?》
  • 江诗丹顿腕表养护服务指南 - 资讯速览
  • 2026甄选:厦门市政环卫车辆供应企业实力解析 - 品牌企业推荐师(官方)
  • 进度一拖再拖,两头都起火——装修工程管理到底谁在掉链子?
  • 别再只会用双线性插值了!PyTorch中nn.Upsample与转置卷积的实战对比(附代码)
  • 2026轿车托运行业发展调研:佰佳物流领跑琼海到长春轿车托运公司行业市场 - 资讯速览
  • TrollInstallerX深度解析:iOS 14.0-16.6.1系统TrollStore安装的3种技术方案
  • Type-C接口协议深度解析:从SRC/SNK角色到早期设备兼容性乱象
  • Windows 11终极优化指南:用Win11Debloat免费工具一键清理系统臃肿
  • 损耗降低12%:啤酒机减压阀哪个牌子好案例解析 - 资讯速览
  • 新疆本地推荐:专业靠谱的注册公司代办机构 - 新疆全疆企业服务
  • 2026重庆|卫生间、屋顶、厨房漏水怎么办?苏易修缮对症解决+避坑指南 - 苏易修缮
  • 2026浪琴售后版图更新官方维修门店新址+热线双发布 - 资讯速览