MySQL的索引底层数据结构采用的是 B+树,一个索引对应一个B+树
特点
当我们往表里加入数据的时候,MySQL是将表存储在文件里的
当我们读取表中数据的时候,MySQL需要从磁盘中将数据读取出来,放入内存中
磁盘和内存的数据交互涉及到IO,操作系统存在一个局部性原理,局部性原理是指当操作系统发现我们要取 1 kb 数据的时候,操作系统其实是会取出这一页(通常来说是4kb)的数据,也就是说这 1 kb 相邻的数据也会同时取出来
在InnoDB引擎里也存在 页 的概念,不同于操作系统,InnoDB里 一页 = 16 kb(可手动修改)
真正在执行sql的时候,有一个查询优化器会估算各方法的查询效率,选择最优解
CREATE TABLE `demo_table` (
`id` int(20) NOT NULL,
`name` varchar(45),
`age` int(11),
`score` int(11),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
执行
explain SELECT * FROM demo_table;
执行
explain SELECT * FROM demo_table where id = 4;
如果一张表没有主键,InnoDB会寻找有没有唯一索引,如果有则将唯一索引列作为主键,如果没有,则会添加一个隐含字段作为主键字段(rowid)
除开主键索引,其他索引都可以称之为辅助索引,也叫联合索引
创建辅助索引(非唯一的联合索引)
CREATE INDEX `idx_name_age` ON demo_table(`name`, `age`);
建立索引实际是对数据排序,辅助索引生成的B+树叶子节点顺序是根据索引列表排序。
本例中,排序规则:先将name列排序,如果name相等则再根据 age 进行排序
(字符集的排序规则为 collation 指定,本例为 utf8mb4_0900_ai_ci)
主键索引B+树的叶子节点会包含该行数据的全部字段,但辅助索引B+树的叶子节点只保存该行对应的主键。
根据主键去主键索引B+树中找出该行全部字段数据,这个步骤叫做 回表
执行
explain SELECT * FROM demo_table where name = 'ming' and age = 18;
一条sql能够走索引是因为给出的条件能够根据B+树一步步缩小结果集,所以有 最左前缀原则
根据最左前缀原则,这条语句将不能成功走索引
explain SELECT * FROM demo_table where age = 18;
(1) 分析这条sql,是否能命中索引?
explain SELECT * FROM demo_table where name > 'ming';
按照之前所说的逻辑,只要在B+树中找到 name = ming 的叶子节点,那么右边的叶子节点都是结果集,但为什么不能走索引呢?
原来这条sql的执行过程是
结论:如果根据索引找出的结果集比较少的话,会命中索引;结果集比较多的话,那么会放弃索引走全表扫描
(2) 分析这条语句,是否能命中索引?
explain SELECT name,age FROM demo_table where name > 'ming';
(3) 以字段更多的rank_report表为例,该表主键为id,并以 cate_id 列建立辅助索引,表中数据有上万条。分析这条sql,是否能命中索引?
explain SELECT * FROM rank_report order by cate_id;
(4) 根据主键来排序,则放进内存空间的数据已经是排序的好的,省略了CPU排序这一步,Extra 字段为空,能够命中索引
explain SELECT * FROM rank_report order by id;
(5) 将 select * 换成 select cate_id,那么也能走索引
explain SELECT cate_id FROM rank_report order by cate_id;
(6) 以 demo_table为例,分析这条语句,是否能命中索引?
explain SELECT name,age FROM test.demo_table order by name,age desc;
答案是不能
那要怎么优化这条sql语句呢?
原来 建立索引的时候也是有一个默认的顺序的,即ASC。MySQL里可以指定索引的顺序是ASC还是DESC