MySQL索引小结

1 MySQL的基本存储结构

MySQL的基本存储结构是页(记录都存在页里边): html

  • 各个数据页能够组成一个双向链表
  • 每一个数据页中的记录又能够组成一个单向链表
    • 每一个数据页都会为存储在它里边儿的记录生成一个页目录,在经过主键查找某条记录的时候能够在页目录中使用二分法快速定位到对应的槽,而后再遍历该槽对应分组中的记录便可快速找到指定的记录
    • 以其余列(非主键)做为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录。

因此说,若是咱们写select * from user where indexname = 'xxx'这样没有进行任何优化的sql语句,默认会这样作:面试

  • 定位到记录所在的页:须要遍历双向链表,找到所在的页
  • 从所在的页内中查找相应的记录:因为不是根据主键查询,只能遍历所在页的单链表了

2 为何要使用索引?

  • 经过建立惟一性索引,能够保证数据库表中每一行数据的惟一性。
  • 能够大大加快数据的检索速度(大大减小检索的数据量),这也是建立索引的最主要缘由。
  • 帮助服务器避免排序和临时表。
  • 将随机IO变为顺序IO。
  • 能够加速表与表之间的链接,特别是在实现数据的参考完整性方面特别有意义。

3 索引这么多优势,为何不对表中的每个列建立一个索引呢?

  • 当表中的数据进行增长、删除和修改的时候,索引也要动态的维护,这样会下降的数据的维护速度。
  • 索引须要占用物理空间,除了数据表占数据空间外,每个索引还要占必定的物理空间,若是要创建聚簇索引,那么须要的空间就会更大。
  • 建立索引和维护索引要好费时间,这种时间随着数据量的增长而增长。

4 索引是如何提升查询速度的?

将无序的数据变成有序的数据(就像查目录同样)。 算法

要找到id为8的记录简要步骤:

很明显的是:没有用索引咱们是须要遍历双向链表来定位对应的页,如今经过 “目录” 就能够很快地定位到对应的页上了!(二分查找,时间复杂度近似为O(logn))sql

其实底层结构就是B+树,B+树做为树的一种实现,可以让咱们很快地查找出对应的记录。数据库

5 使用索引注意事项

  • 在常常须要搜索的列上,能够加快搜索的速度;
  • 在常用where子句的列上建立索引,加快条件的判断速度;
  • 在常常须要排序的列上建立索引,由于索引已经排序,这样查询能够利用索引的排序,加快排序查询速度;
  • 对于中到大型表索引都是很是有效的,可是特大型表的话维护开销会很大,不适合建索引;
  • 在常常用在链接的列上建立索引,这些列主要是一些外键,能够加快链接的速度;
  • 避免where子句对字段施加函数,只会形成没法命中索引。
  • 在使用InnoDB时使用与业务无关的自增主键做为主键,即便用逻辑主键,而不要使用业务主键;
  • 将打算加索引的列设置为NOT NULL,不然将致使引擎放弃使用索引而进行全表扫描;
  • 删除长期未使用的索引,无用索引的存在会形成没必要要的性能损耗,MySQL 5.7能够经过查询sys库的chema_unused_indexes视图来查询哪些索引从未被使用
  • 在使用limit offset查询缓慢时,能够借助索引来提升性能。

6 MySQL索引主要使用的两种数据结构

  • 哈希索引

    对于哈希索引来讲,底层的数据结构就是哈希表,所以在绝大多数需求为单条记录查询的时候,能够选择哈希索引,查询性能最快;其他大部分场景,建议选择BTree索引。bash

  • BTree索引

    MySQL的BTree索引使用的是B树中的B+Tree。但对于主要的两种存储引擎(MyIsam和InnoDB)的实现方式是不同的。服务器

7 MySQL索引种类

单列

  • 普通索引:加速查找;
  • 惟一索引:加速查找+约束:不能重复(只能有一个空,否则就重复了);
  • 主键:加速查找+约束:不能重复+不能为空;

多列

  • 联合索引(多个列建立索引)---->至关于单列的普通索引
  • 联合惟一索引 ---->至关于单列的惟一索引

    ps:联合索引的特色:遵循最左前缀的规则数据结构

8 什么是最左前缀原则?

MySQL中的索引能够以必定顺序引用多列,这种索引叫做联合索引。如User表的name和city加联合索引就是(name,city),而最左前缀原则指的是,若是查询的时候查询条件精确匹配索引的左边连续一列或几列,则此列就能够被用到。以下:函数

select * from user where name=xx and city=xx ; //能够命中索引
select * from user where name=xx ; // 能够命中索引
select * from user where city=xx ; // 没法命中索引            
复制代码

这里须要注意的是,查询的时候若是两个条件都用上了,可是顺序不一样,如 city= xx and name =xx,那么如今的查询引擎会自动优化为匹配联合索引的顺序,这样是可以命中索引的。post

因为最左前缀原则,在建立联合索引时,索引字段的顺序须要考虑字段值去重以后的个数,较多的放前面。ORDER BY子句也遵循此规则。

9 MyIsam和InnoDB实现BTree索引方式的区别

  • MyIsam

    B+Tree叶子节点的data域存放的是数据记录的地址。在索引检索的时候,首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,则取出其data域的值,而后data域的值为地址读取响应的数据记录。这被称为“非聚簇索引”。

  • InnoDB

    其数据文件自己就是索引文件。MyIsam中,索引文件和数据文件是分离的,而InnoDB中,其表数据文件自己就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,所以InnoDB表数据文件自己就是主索引。这被称为“聚簇索引(或汇集索引)”,而其他的索引都做为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyIsam不一样的地方。在根据主索引搜索时,直接找到key所在的节点便可取出数据;在根据辅助索引搜索时,则须要先取出主键的值,再走一遍主索引。所以,在设计表的时候,不建议使用过长的字段做为主键,也不建议使用非单调的字段做为主键,这会形成主索引频繁分裂。

【注】更详细的请出门左转: MySQL索引和SQL调优

10 覆盖索引介绍

  • 什么是覆盖索引?

    若是一个索引包含(或者说覆盖)全部须要查询的字段的值,咱们就称之为“覆盖索引”。咱们知道在InnoDB存储引擎中,若是不是主键索引,叶子节点存储的是主键+列值。最终仍是要“回表”,也就是要经过主键再查找一次,这样就会比较慢。覆盖索引就是要查询的列和索引是对应的,不作回表操做。

  • 覆盖索引使用实例

    加入建立了索引(username,age),在查询数据的时候:
    select username,age from user where username = ‘Java’ and age = 22;
    要查询成的列在叶子节点都存在,因此不用回表。

11 选择索引和编写利用这些索引查询的3个原则

  1. 单行访问和很慢的。特别是在机械硬盘存储中。若是服务器从存储中读取一个数据块只是为了获取其中一行,那么就浪费了不少工做。最好读取的块中能尽量多的包含所须要的行。使用索引建立位置引,用以提升效率。
  2. 按顺序访问范围数据是很快的,这有两个缘由:
    • 第一,顺序I/O不须要屡次磁盘寻道,因此比随机I/O要快不少(特别是对机械硬盘)。
    • 第二,若是服务器可以按须要顺序读取数据,那么就不须要额外的排序操做,而且GROUP BY查询也无需再作排序和将行按组进行聚合计算了。
  3. 覆盖索引查询是很快的。若是一个索引包含了查询须要的全部列,那么存储引擎就不须要再回表查找行,这避免了大量的单行访问。

12 列举建立索引可是没法命中索引的8种状况

  1. like '%xx'
select * from tb1 where name like '%cn';
复制代码

以'%'开头:索引失效。
以'%'结尾:索引能够用。

  1. 使用函数
select * from tb1 where reverse(name) = 'wupeiqi';
复制代码
  1. or
select  * from tb1 where nid = 1 or email = 'seven@live.com';
复制代码

特别地:当or条件中有未创建因此的列才失效,如下会走索引

select * from tb1 where nid = 1 or name = 'seven';
	select * from tb1 where nid = 1 or email = 'seven@live.com' and name = 'alex'
复制代码

若是条件中有or,即便其中有条件带索引也不会使用(这也是为何尽可能少于or的缘由)
注意:要想使用or,又想让索引生效,只能将or条件中的每一个列中都加上索引.

  1. 类型不一致 若是列类型是字符串,那必定要在条件中将数据使用引号引用起来,不然不使用索引

  2. 含有'!=' 或者'>'

select * from tb1 where name != 'alex'
复制代码

【特别地】:若是是主键或索引整数类型,则仍是会走索引

select * from tb1 where nid > 123
	select * from tb1 where num > 123
复制代码
  1. 含有'order by'
select email from tb1 order by name desc;
复制代码

当根据索引排序时,选择的映射若是不是索引,则不走索引
【特别地】:若是对主键排序,则仍是会走索引:

select * from tb1 order by nid desc;
复制代码
  1. 组合索引最左前缀

若是组合索引为:(name, email)

- name and email # 使用索引
- name		 # 使用索引
- email		 # 不使用索引
复制代码
  1. 若是MySQL估计使用全表扫描要比使用索引快,则不使用索引
  2. 其余:

注:以上内容整理自:

相关文章
相关标签/搜索