mysql> explain select a.*,b.*from student a rightjoin exams b on a.uid = b.uid; +----+-------------+-------+------------+--------+---------------+---------+---------+--------------+------+----------+-------+ | id | select_type |table| partitions | type | possible_keys | key | key_len |ref|rows| filtered | Extra | +----+-------------+-------+------------+--------+---------------+---------+---------+--------------+------+----------+-------+ |1| SIMPLE | b |NULL|ALL|NULL|NULL|NULL|NULL|12|100.00|NULL| |1| SIMPLE | a |NULL| eq_ref |PRIMARY|PRIMARY|4| school.b.uid |1|100.00|NULL| +----+-------------+-------+------------+--------+---------------+---------+---------+--------------+------+----------+-------+ 2rowsinset, 1 warning (0.00 sec)
mysql> explain select a.*,b.*from student a leftjoin exams b on a.uid = b.uid; +----+-------------+-------+------------+------+---------------+---------+---------+--------------+------+----------+-------+ | id | select_type |table| partitions | type | possible_keys | key | key_len |ref|rows| filtered | Extra | +----+-------------+-------+------------+------+---------------+---------+---------+--------------+------+----------+-------+ |1| SIMPLE | a |NULL|ALL|NULL|NULL|NULL|NULL|6|100.00|NULL| |1| SIMPLE | b |NULL|ref|PRIMARY|PRIMARY|4| school.a.uid |2|100.00|NULL| +----+-------------+-------+------------+------+---------------+---------+---------+--------------+------+----------+-------+ 2rowsinset, 1 warning (0.00 sec)
mysql>
现在有这么一个情景:我们要查询没有考过试的学生
我们可以使用子连接
1
select*from student where uid notin (select uid from exams);
1 2 3 4 5 6 7 8 9
mysql>select*from student where uid notin (select uid from exams); +-----+--------+-----+-----+ | uid | name | age | sex | +-----+--------+-----+-----+ |6| weiwie |20| M | +-----+--------+-----+-----+ 1rowinset (0.00 sec)
mysql>select a.*from student a leftjoin exams b on a.uid = b.uid where b.cid isnull; +-----+--------+-----+-----+ | uid | name | age | sex | +-----+--------+-----+-----+ |6| weiwie |20| M | +-----+--------+-----+-----+ 1rowinset (0.01 sec)
mysql> explain select a.*from student a leftjoin exams b on a.uid = b.uid where b.cid isnull; +----+-------------+-------+------------+------+---------------+---------+---------+--------------+------+----------+--------------------------------------+ | id | select_type |table| partitions | type | possible_keys | key | key_len |ref|rows| filtered | Extra | +----+-------------+-------+------------+------+---------------+---------+---------+--------------+------+----------+--------------------------------------+ |1| SIMPLE | a |NULL|ALL|NULL|NULL|NULL|NULL|6|100.00|NULL | |1| SIMPLE | b |NULL|ref|PRIMARY|PRIMARY|4| school.a.uid |2|10.00|Usingwhere; Notexists; Using index | +----+-------------+-------+------------+------+---------------+---------+---------+--------------+------+----------+--------------------------------------+ 2rowsinset, 1 warning (0.01 sec)
mysql>
查看没有参加课程号为3考试的学生
1 2 3
select a.*from student a leftjoin exams b on a.uid = b.uid and b.cid =3 where b.cid isnull;
1 2 3 4 5 6 7 8 9 10 11
mysql>select a.*from student a leftjoin exams b on a.uid = b.uid and b.cid =3where b.cid isnull; +-----+----------+-----+-----+ | uid | name | age | sex | +-----+----------+-----+-----+ |1| zhangsan |18| M | |4| linfeng |21| W | |6| weiwie |20| M | +-----+----------+-----+-----+ 3rowsinset (0.01 sec)
mysql>
explain 查询
1 2 3 4 5 6 7 8 9 10 11 12
mysql> explain select a.*from student a leftjoin exams b on a.uid = b.uid and b.cid =3where b.cid isnull; +----+-------------+-------+------------+--------+---------------+---------+---------+--------------------+------+----------+--------------------------------------+ | id | select_type |table| partitions | type | possible_keys | key | key_len |ref|rows| filtered | Extra | +----+-------------+-------+------------+--------+---------------+---------+---------+--------------------+------+----------+--------------------------------------+ |1| SIMPLE | a |NULL|ALL|NULL|NULL|NULL|NULL|5|100.00|NULL | |1| SIMPLE | b |NULL| eq_ref |PRIMARY|PRIMARY|8| school.a.uid,const |1|100.00|Usingwhere; Notexists; Using index | +----+-------------+-------+------------+--------+---------------+---------+---------+--------------------+------+----------+--------------------------------------+ 2rowsinset, 1 warning (0.00 sec)
mysql>set slow_query_log=ON -> ; ERROR 1229 (HY000): Variable 'slow_query_log'is a GLOBAL variable and should be setwithSETGLOBAL mysql>setglobal slow_query_log =ON; Query OK, 0rows affected (0.04 sec)
mysql>SELECT*FROM student WHERE age <18OR name ='zhangsan'; +-----+----------+-----+-----+ | uid | name | age | sex | +-----+----------+-----+-----+ |1| zhangsan |18| M | +-----+----------+-----+-----+ 1rowinset (0.01 sec)
mysql>show profiles; +----------+------------+-----------------------------------------------------------+ | Query_ID | Duration | Query | +----------+------------+-----------------------------------------------------------+ |1|0.00756475|SELECT*FROM student WHERE age <18OR name ='zhangsan'| +----------+------------+-----------------------------------------------------------+ 1rowinset, 1 warning (0.00 sec)
mysql>showcreate table student\G ***************************1.row*************************** Table: student Create Table: CREATE TABLE `student` ( `uid` int unsigned NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL, `age` tinyint unsigned NOT NULL, `sex` enum('M','W') NOT NULL, PRIMARY KEY (`uid`) ) ENGINE=InnoDB AUTO_INCREMENT=7DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1rowinset (0.00 sec)
mysql>select*from student where age =20orderby name; +-----+---------+-----+-----+ | uid | name | age | sex | +-----+---------+-----+-----+ |2| gaoyang |20| W | |6| weiwie |20| M | +-----+---------+-----+-----+ 2rowsinset (0.01 sec)
mysql> explain select*from student where age =20orderby name; +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+ | id | select_type |table| partitions | type | possible_keys | key | key_len |ref|rows| filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+ |1| SIMPLE | student |NULL|ALL|NULL|NULL|NULL|NULL|5|20.00|Usingwhere; Using filesort | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+ 1rowinset, 1 warning (0.00 sec)
mysql>
如何优化上面的查询场景
首先,我们是对age进行查询,所以试着给age添加一个索引
1 2 3 4 5 6 7 8 9 10 11 12 13
mysql>create index age_index on student(age); Query OK, 0rows affected (0.12 sec) Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select*from student where age =20orderby name; +----+-------------+---------+------------+------+---------------+-----------+---------+-------+------+----------+----------------+ | id | select_type |table| partitions | type | possible_keys | key | key_len |ref|rows| filtered | Extra | +----+-------------+---------+------------+------+---------------+-----------+---------+-------+------+----------+----------------+ |1| SIMPLE | student |NULL|ref| age_index | age_index |1| const |2|100.00|Using filesort | +----+-------------+---------+------------+------+---------------+-----------+---------+-------+------+----------+----------------+ 1rowinset, 1 warning (0.01 sec)
mysql>select*from student where name like'zhang%'; +-----+----------+-----+-----+ | uid | name | age | sex | +-----+----------+-----+-----+ |1| zhangsan |18| M | +-----+----------+-----+-----+ 1rowinset (0.00 sec)
mysql> explain select*from student where name like'zhang%';#这里使用前缀索引进行索引查找 +----+-------------+---------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+ | id | select_type |table| partitions | type | possible_keys | key | key_len |ref|rows| filtered | Extra | +----+-------------+---------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+ |1| SIMPLE | student |NULL|range| name_index | name_index |202|NULL|1|100.00|Using index condition| +----+-------------+---------+------------+-------+---------------+------------+---------+------+------+----------+-----------------------+ 1rowinset, 1 warning (0.02 sec)
mysql> explain select*from student where name like'%zhang%';#无法使用前缀索引进行查找,无法使用索引 +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type |table| partitions | type | possible_keys | key | key_len |ref|rows| filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+ |1| SIMPLE | student |NULL|ALL|NULL|NULL|NULL|NULL|5|20.00|Usingwhere| +----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1rowinset, 1 warning (0.00 sec)
mysql>create index age_index on student(age); Query OK, 0rows affected (0.18 sec) Records: 0 Duplicates: 0 Warnings: 0
mysql>select*from student where age notin(18,20); +-----+----------+-----+-----+ | uid | name | age | sex | +-----+----------+-----+-----+ |5| liuxiang |19| W | |4| linfeng |21| W | |3| chenwei |22| M | +-----+----------+-----+-----+ 3rowsinset (0.00 sec)
mysql> explain select*from student where age notin(18,20); +----+-------------+---------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+ | id | select_type |table| partitions | type | possible_keys | key | key_len |ref|rows| filtered | Extra | +----+-------------+---------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+ |1| SIMPLE | student |NULL|range| age_index | age_index |1|NULL|4|100.00|Using index condition| +----+-------------+---------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+ 1rowinset, 1 warning (0.01 sec)
mysql> explain select age from student where age notin(18,20); +----+-------------+---------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+ | id | select_type |table| partitions | type | possible_keys | key | key_len |ref|rows| filtered | Extra | +----+-------------+---------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+ |1| SIMPLE | student |NULL|range| age_index | age_index |1|NULL|4|100.00|Usingwhere; Using index | +----+-------------+---------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+ 1rowinset, 1 warning (0.00 sec)
mysql>
or
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
mysql> explain select age from student where age <18or age > 20; +----+-------------+---------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+ | 1 | SIMPLE | student | NULL | range | age_index | age_index | 1 | NULL | 3 | 100.00 | Using where; Using index | +----+-------------+---------+------------+-------+---------------+-----------+---------+------+------+----------+--------------------------+ 1 row in set, 1warning (0.00 sec)
mysql> explain select * from student where age <18or age > 20; +----+-------------+---------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+ | 1 | SIMPLE | student | NULL | range | age_index | age_index | 1 | NULL | 3 | 100.00 | Using index condition | +----+-------------+---------+------------+-------+---------------+-----------+---------+------+------+----------+-----------------------+ 1 row in set, 1warning (0.00 sec)
mysql>
13、Innodb自适应哈希索引
我们知道,在使用二级索引树的时候,可能会产生回表。当某些查询频繁访问二级索引且模式固定(如 WHERE name = 'zhangsan' 的查询),InnoDB 会在内存中基于 B+ 树的二级索引创建一个哈希索引。自适应哈希索引的作用是加速特定查询,避免二级索引树和主键索引树的回表过程。