索引是一个排序的列表,存储着索引的值和包含这个值的数据所在行的物理地址,在数据十分庞大的时候,索引可以大大加快查询的速度。因为使用索引后可以不用扫描全表来定位某行的数据,而是先通过索引表找到该行数据对应的物理地址然后访问相应的数据。
索引以文件的形式存储在磁盘上,所以只使用更少的磁盘io 次数的数据结构更适合做索引。b 树和b+树是多叉树,树的度大,所以高度低。内存和磁盘交互的单位是页,将b 树和b+树的一个节点的大小设置为一个页,能保证一次io 就能读到一个页,同时磁盘采用预读策略,一次性读取相邻的几个页,读入内存后在进行二分查找。
MySQL 的基本存储结构是页
MySQL页和页之间、页和数据之间的关系:
一个表的数据块以链表的方式串联在一起,数据以行为单位一行一行的存放在磁盘的块中,扇区是对硬盘而言,块是对文件系统而言,块是文件存取的最小单位,一般一个块由连续的几个扇区组成。
以MySQL的B+树为例,简单说下几种常见场景下数据的定位过程:
第一种场景:索引精确查找
select * from user_info where id = 23 ;
确定定位条件, 找到根节点Page No, 根节点读到内存, 逐层向下查找, 读取叶子节点Page,通过 二分查找找到记录或未命中。
第二种场景:索引范围查找
select * from user_info where id >= 18 and id < 22 ;
读取根节点至内存, 确定索引定位条件id=18, 找到满足条件第一个叶节点, 顺序扫描所有结果, 直到终止条件满足id >=22。
第三种场景:全表扫描
select * from user_info where name = 'abc' ;
直接读取叶节点头结点, 顺序扫描, 返回符合条件记录, 到最终节点结束
第四种场景:二级索引查找
create table table_x(int id primary key, varchar(64) name , key sec_index(name) ) ;
select * from table_x where name = 'd' ;
通过二级索引查出对应主键,拿主键回表查主键索引得到数据, 二级索引可筛选掉大量无效记录,提高效率
应该在这些列上创建索引:
主键索引、唯一索引、普通索引、全文索引、组合索引
1、INDEX(普通索引)
2、UNIQUE(唯一索引):索引列的值必须唯一,允许有空值
3、PRIMARY KEY(主键索引):是一种特殊的唯一索引,不允许有空值
4、FULLTEXT(全文索引):仅可用于MyISAM和InoDB,针对较大的数据,生成全文索引很耗时耗空间
5、组合索引:遵循“最左前缀”原则。创建复合索引应该将最常用做限制条件的列放在最左边,依次递减。最左前缀匹配规则:MySQL会一直向右匹配直到遇到范围查询(>,<,between,like)就停止匹配
聚簇索引与非聚簇索引
MySQL中最常见的两种存储引擎分别是MyISAM和InnoDB,分别实现了非聚簇索引和聚簇索引。聚簇索引的顺序就是数据的物理存储顺序。非聚簇索引的索引顺序与数据物理排列顺序无关。
非聚簇索引(MyISAM)
聚簇索引(InnoDB)
对比
MySQL索引主要有两种结构:B+Tree索引和Hash索引
Hash索引
MySQL中,只有Memory存储引擎显示支持Hash索引,是Memory表的默认索引类型。
哈希索引适合于等值查询,只需要经过一次算法即可找到相应的键值,检索效率非常高。但是不支持范围查询检索,也没办法利用索引完成排序以及like ‘xxx%’ 这样的部分模糊查询。哈希索引也不支持多列联合索引的最左匹配规则。
B+Tree索引
B+Tree是MySQL使用最频繁的一个索引数据结构,是Inodb和Myisam存储引擎模式的索引类型。B+Tree所有索引数据都在叶子节点上,并且增加了顺序访问指针,每个叶子节点都有指向相邻叶子节点的指针。这样提高区间效率,只要顺着节点和指针顺序遍历就可以以此向访问到所有数据节点,极大提高了区间查询效率。大大减少磁盘I/O读取,数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点需要一次I/O就可以完全载入。
B树与B+树
MySQL的优化主要分为结构优化(Scheme optimization)和查询优化(Query optimization)。本章讨论的高性能索引策略主要属于结构优化范畴。本章的内容完全基于上文的理论基础,实际上一旦理解了索引背后的机制,那么选择高性能的策略就变成了纯粹的推理,并且可以理解这些策略背后的逻辑。
联合索引(复合索引)
首先介绍一下联合索引。联合索引其实很简单,相对于一般索引只有一个字段,联合索引可以为多个字段创建一个索引。它的原理也很简单,比如,我们在(a,b,c)字段上创建一个联合索引,则索引记录会首先按照A字段排序,然后再按照B字段排序然后再是C字段,因此,联合索引的特点就是:
| A | B | C |
| 1 | 2 | 3 |
| 1 | 4 | 2 |
| 1 | 1 | 4 |
| 2 | 3 | 5 |
| 2 | 4 | 4 |
| 2 | 4 | 6 |
| 2 | 5 | 5 |
其实联合索引的查找就跟查字典是一样的,先根据第一个字母查,然后再根据第二个字母查,或者只根据第一个字母查,但是不能跳过第一个字母从第二个字母开始查。这就是所谓的最左前缀原理。
最左前缀原理
我们再来详细介绍一下联合索引的查询。还是上面例子,我们在(a,b,c)字段上建了一个联合索引,所以这个索引是先按a 再按b 再按c进行排列的,所以:
以下的查询方式都可以用到索引
select * from table where a=1;
select * from table where a=1 and b=2;
select * from table where a=1 and b=2 and c=3;
上面三个查询按照 (a ), (a,b ),(a,b,c )的顺序都可以利用到索引,这就是最左前缀匹配。如果查询语句是:
select * from table where a=1 and c=3; 那么只会用到索引a。
如果查询语句是:
select * from table where b=2 and c=3; 因为没有用到最左前缀a,所以这个查询是用户到索引的。
如果用到了最左前缀,但是顺序颠倒会用到索引码?比如:
select * from table where b=2 and a=1;
select * from table where b=2 and a=1 and c=3;
如果用到了最左前缀而只是颠倒了顺序,也是可以用到索引的,因为mysql查询优化器会判断纠正这条sql语句该以什么样的顺序执行效率最高,最后才生成真正的执行计划。但我们还是最好按照索引顺序来查询,这样查询优化器就不用重新编译了。
前缀索引
除了联合索引之外,对mysql来说其实还有一种前缀索引。前缀索引就是用列的前缀代替整个列作为索引key,当前缀长度合适时,可以做到既使得前缀索引的选择性接近全列索引,同时因为索引key变短而减少了索引文件的大小和维护开销。一般来说以下情况可以使用前缀索引:
一些文章中也提到:
MySQL 前缀索引能有效减小索引文件的大小,提高索引的速度。但是前缀索引也有它的坏处:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前缀索引,也不能把它们用作覆盖索引(Covering Index)。
SELECT * FROMhoudunwangWHEREunameLIKE'后盾%' -- 走索引
SELECT * FROMhoudunwangWHEREunameLIKE "%后盾%" -- 不走索引
CREATE TABLEa(achar(10));
EXPLAIN SELECT * FROMaWHEREa="1" – 走索引
EXPLAIN SELECT * FROM a WHERE a=1 – 不走索引
正则表达式不使用索引,这应该很好理解,所以为什么在SQL中很难看到regexp关键字的原因
以Innodb引擎为准,按照表空间、段、簇、页进行存储。