1、背景描述

索引是帮助MySQL高效查找的数据结构,他的本质是空间换时间。查找可以分为两种,第一种顺序查找,mysql中最差的情况下就是全表扫描,一行一行数据找直到找到全部符合条件的项目,显然在大量数据情况下查找数据是非常慢的,所以就有了另一种查找方式–索引查找,索引相当于书籍的目录,图书馆的书籍ISBN号,一个国家中学生的学籍号,通过索引可以非常快的、精确地查找到需要的数据。
在mysql的优化中索引占据半壁江山,索引设置的好坏直接影响相关sql语句的执行效率,一次查询,可能执行时间50s,也可能倍数减少到几秒,零点几秒。

鲁迅说:索引是门艺术

2、MySQL中的索引分类

索引按照类型条件划分为

  1. 主键索引
    主键索引,表中添加主键时自动创建,查找速度最快。
  2. 唯一索引
    唯一索引,即用unique修饰的索引,顾名思义,索引包含的字段唯一存在,不能有重复的值。
  3. 全文索引
    全文索引,即用fulltext修饰的索引,可以类比模糊查询,在大量文字中查询相关内容,支持的字段有varchar、char、text,以前只有myisam引擎支持全文索引,但是在MySQL5.6版本以后Innodb也开始支持全文索引。
  4. 普通索引
    好像没啥可解释的,普通嘛,没有那么多限制就对了,就是除了主键索引、唯一索引、全文索引等剩下的就是了。

按照包含列分为

  1. 单列索引
    即只包含单个字段的索引。
  2. 联(复)合索引(多列索引)
    即包含多个字段的索引。

按照数据结构:

  1. 聚簇索引(聚集索引)
    聚簇索引中叶子节点中包含记录数据和索引,可以直接通过主键查询到对应数据。
  2. 二级索引(辅助索引)
    二级索引中只包含主键值和对应索引列的值,每次查询都需要先查询到主键值,然后通过使用主键值再去聚簇索引中查询对应数据内容,这个过程也叫作“回表”操作。
  3. 哈希索引
    在Innodb等引擎中,哈希索引是由mysql本身维护的,不可以手动使用,又名自适应哈希索引,Adapter Hash Index(AHI),计算索引列字段哈希值和记录指针地址保存在内存中,不保存其他字段数据,以索引列字段值哈希值排序,适合等值精确查找。
    在但有个例外memory引擎中,用户可以手动创建使用哈希索引。

其他:

  1. 覆盖索引
    覆盖索引即查询时的字段是索引的子集,查询时使用的字段一定在索引包含的字段中,这时候使用的索引就叫做覆盖索引。explain执行计划中Extra列值为using index是使用覆盖索引的显著标志。

3、索引数据结构

mysql处理数据是在内存中的,存储数据是在硬盘上,所以mysql在进行sql操作时,就需要数据在内存和磁盘中来回交互,即我们通常所说的IO操作。这种交互操作是以“数据页”的方式来交换数据,每次交换的数据大小默认为16K,也就是说内存每次从硬盘中拿到16K的数据信息进行操作,操作完成后每次刷新16K的数据到硬盘中。

说到索引的数据结构,最核心最重要的就是B+树了,每一条索引就是一棵B+树。
在InnoDB引擎中只有主键索引是聚簇索引,其他索引列和复合索引都是二级索引。
聚簇索引的结构如图:

由图中可以看出,聚簇索引B+树中根节点和非叶子节点只保存主键和页号,而在叶子节点中除了主键和页号外,还保存了完整的数据列内容,同时在一棵树上同时保存了索引和数据,这也是“聚簇索引”这个名字的由来。主键是维系聚簇索引的核心要素,B+树中的所有元素都是依据主键而存在。同时数据页之间通过主键的大小排序形成了双向链表,数据记录之间通过主键大小排序形成了单向链表。
由于聚簇索引的数据的物理存储顺序和索引顺序一致,即只要索引相邻,那么对应的物理存储也会存储在磁盘相邻的位置,这样就能得出一个结论,主键靠的越近,聚簇索引数据越紧凑,产生的磁盘锁片也会越少,效率也会更高。这也是为什么推荐主键自增的原因,在往磁盘中写数据时就是一页一页挨着写,避免出现“页分裂”,自然效率也就相对于不断调整数据顺序调换位置更高。

二级索引的结构如图

二级索引中保存了对应的索引列和主键值,当没有使用覆盖索引查询时,二级索引中只能找到索引信息,想要完整数据记录信息就需要在二级索引中找到对应的主键值,然后再回到聚簇索引中查询相应的完整数据记录。这就是前面提到的"回表”操作。

myisam中索引结构:
接下来说说myisam引擎中的数据结构,与Innodb相同的一点是,myisam中索引也同样采用B+树存储,myisam的存储有三个文件,除了frm文件以外有myi索引文件和myd数据文件,区别于innodb引擎,除了frm文件以外就只有一个ibd文件,myisam索引和数据本身就是独立存储的。myisam中的B+树中根节点和非叶子节点存储主键值,叶子节点存储数据对应指针地址。

myisam中存在的都是二级索引,但与Innodb中的二级索引不同的是,myisam中的二级索引叶子节点存储的是数据记录指针地址,查询数据时不再需要回表操作,而是直接通过指针地址查找对应的数据记录,这也是myisam之所以说查询速度快的很重要的原因。

4.为什么是B+树

数据结构那么多,光一个树就有好多种,为什么单单就选择了B+树呢,为什么不是哈希表,为什么不是AVL平衡二叉树,为什么不是B树多叉平衡查找树?总结一下原因:

  1. B+树特点:非叶子节点中只存储主键/索引列等,不存储完整数据,完整数据都存放在叶子节点中,在内存寸土寸金的情况下,一个16KB大小的数据页当然是存储的索引越多越好,相对于AVL和B-树,这一点就很明显的把B+树和其他的树划开了界限。同时B+树中索引和存放在叶子节点中的数据具有天然的已排序特点,在范围查找和数据排序方面具有得天独厚的条件;再者B+树中叶子节点中存储了完整数据结构,更有利于提高全表扫描的速度。
  2. 哈希表只存储索引列哈希值和行记录指针,不存储其他列数据,并且存在于内存中,同时哈希算法得出的哈希值绕不开的存在哈希碰撞,任何时候都需要全表扫描,解决哈希碰撞带来的额外消耗显然是得不偿失的。并且对于数据排序,范围查询等不友好,缺点太多啦,pass!!
  3. AVL平衡二叉树,数据越多,树的深度/层高也会越来越高。相对B+树每个节点有多个子节点带来的矮胖,AVL每个节点最多只有两个子节点,伴随着数据的增加,不可避免的树的深度也会相应的快速增加,直到长成一根瘦高的"旗杆",在大量数据中查找数据时,会带来过多的IO操作次数,影响效率,所以pass!
  4. B树,相对于AVL,B树具有和B+树一样的矮胖特征,也没有哈希表带来的哈希碰撞,但是第一点已经提到了,B树非叶子节点中需要存储数据区,一个16KB的数据页中,存储的索引肯定要少于B+树,大量数据查询时,需要更多的IO操作来获取数据,当然相同层高的B+树和B树在查询根节点和非叶子节点数据的时候,查询特定数据的情况下,B树IO次数一定是少于B+树的,越靠近根节点的数据查询越快,反之越慢,适合单条查询。MongoDB使用的就是这种数据结构。

这里再插一句嘴,MySQL设计者认为范围查询优先于单条数据查询,所以选择B+树这种数据结构契合需求,而MongoDB设计者认为还是普通单条查询使用场景更多,所以选择了B树这种数据结构,在查询单条数据时可以直接在数据区中拿到完整数据,并且平均查询速度一般优于B+树。

5.索引带来的好处和坏处

索引的好处前面都已经说了很多了,以空间换时间的方式,提高mysql获取数据的速度和效率嘛,查询更快,排序更快,挺好啊,所以更多的索引是不是更好,NO!单看索引带来的好处确实是越多越好,但是索引是空间换时间,这样就意味着更多的索引占据更多的存储空间,同时,索引建立后,mysql每次修改、更新、删除数据的时候,都需要同时去维护相应的索引,会带来大量的时间消耗,这显然是和空间换时间的初衷相悖,这个时候索引当然是越少越好了,所以需要在多和少之间需要一个平衡,适合的才是最好的嘛,至于怎么平衡,往后看第8条- 如何选择索引列~

6.为什么要手动创建主键

1.手动创建主键int占4个字节,如果没有手动创建,mysql也会贴心照顾你的懒惰或者粗心,会自动创建主键_rowid,占用6个字节,你要不要偷这个懒?
2.Innodb自动创建的主键在使用行锁时会自动升级成表锁,并发能力降低。为什么?原谅我还没有研究到,就当甲鱼屁股-规定对待就好,要不要手动创建,你看着办吧。

7.最左匹配原则

最左匹配原则是应用在联合索引中,是联合索引最重要的属性,没有之一。
虽然我们创建了索引,但是很多情况下,不合适的语句是不能使用索引来提高效率的,至于有哪些情况,我自己总结了个口诀,献丑~

字段使用从左起,查询顺序没关系,
索引字段要连续,范围对比后无匹,
排序顺序要一致,谨慎包含表达式,
前缀索引忌排序,左百分号不考虑

举个栗子:建立联合索引(name,age,sex)
字段使用从左起,意味着使用索引字段一定是从最左边name开始的,类似于select name,age from table where age=10或者select name,age from table where sex=1这样的查询中索引是不生效的。
查询顺序没关系,查询时where条件中的索引字段顺序是不影响索引生效与否的。例如select name,age from table where age=10 and name='tom’依旧会使用索引,mysql的查询优化器会帮我们调整顺序使用索引。
索引字段要连续,查询时where条件中使用的索引字段一定是连续的,中间不能有丢失,例如select name from table where name=‘tom’ and sex=1中用到的索引字段只有name,而sex不会生效。
范围对比后无匹,范围对比之后的字段都会失效无法匹配索引,例如select name from table where name>‘tom’ and age=10只会使用到name字段,而不会使用age。
排序顺序要一致,即索引字段在用于排序时,排序方式必须相同,不能是name desc降序,age asc升序,这时索引也会失效。
谨慎包含表达式,查询字段必须是单独存在,不能存在表达式中,例如select name from table where upper(name)='TOM’不能使用索引。
前缀索引忌排序,左百分号不考虑。使用列前缀添加索引一般是varchar,text等存储大量文本内容内容的列,一般和like结合使用,官方推荐使用这种方式来降低索引占用存储空间,但是在使用列前缀添加索引时不能使用该字段索引排序,例如select name from table where name like ‘t%’ order by name,查询时使用索引,但排序时不能使用索引排序。并且百分号出现在左边的时候是不能使用索引的,例如select name from table where name like '%t’这种情况下是不能使用索引查找的。

8. 如何选择索引列

条件一:列值离散程度:比如性别列,就男和女,数据库里可能就是0和1或者male和female(你要愣说太监、人妖、中性人,那我也无力反驳),当数据列数据只有几种情况,或者几乎一致的时候,创建了索引和全表扫描差别不大,还要占用额外的空间,那为什么要创建索引呢,所以在可能需要创建索引的情况下,数据列值离散性越大越好。

条件二:在条件一的基础上,where后面的列,order by后面的列,分组group的列一般要创建索引。

条件三:对于char,varchar,text这些类型存储很多字节的数据列,在条件一和二的基础上根据业务情况选择特征值明显的列前缀建立索引,减小索引所占空间嘛,在计算机里精打细算总是没错的。
条件四 索引列的类型尽量小,能使用int就不使用bigint,索引数据越小,一个数据页存储的索引越多,减少IO消耗
条件五 主键自增,避免“页分裂“ 和数据迁移。
条件六 尽量避免重复索引,一样的效果,一般来说数据库查询时只会使用一条二级索引,重复了没有意义,浪费存储空间。当然什么情况不一般,这个要说到 索引合并 的内容,比较复杂,需要的可以百度一下。

总结:适合的才是最好的,索引列的选择一定要根据实际业务情况来选择,不能生搬硬套。

7.索引优化,执行计划Explain

这部分内容较多,留着下一篇文章来写。

8.覆盖索引的典型场景

使用覆盖索引在Innodb引擎中的最重要优点就是不需要回表。覆盖索引查询时的字段是索引的子集,即需要查询的字段在索引中都已经有了,这时候查询时就不需要回表操作了。

这里说到覆盖索引的典型场景–大数据量下分页查询
例如这样的sql语句,

select * from table where age>10 limit 1000000,10;

分页越往后,查询时间越久,就可以使用覆盖索引来优化,将sql改写为

select * from table t1 join (select id from table where age >10 limit 100000,10)t2 on t1.id=t2.id

或者

select * from table where id>=(select id from table where age>10 limit 100000,1) limit 10

就可以有效提高查询效率,降低查询时间。

9.唯一索引和普通索引的区别

说到唯一索引和普通索引的差别,就要分析一下使用这两种索引在增删改查时都发生了什么。
查找时:由于唯一索引限制,符合条件的只会有一条数据,即查找时找到一次即返回,而普通索引需要 接着查找所有符合条件的数据,感觉查询同样的数据时可能唯一索引更快一些,但是有索引的存在,两者之间的差别微乎其微,可以说忽略不计。
插入时:普通索引直接插入就好,唯一索引由于unique修饰,只能存在一条列值为插入的值的记录,所以在插入之前就需要判断是不是已经有符合条件的数据,如果没有就插入,反之报错,所以唯一索引插入时等于进行了一次查找+一次插入,显而易见,插入时唯一索引会更慢一些。
删除时:与查找相同,删除时唯一索引找到一次删除即返回。普通索引需要接着查找符合条件的数据并删除,感觉删除同样的数据可能唯一索引更快一些,但是有索引的存在,两者之间的差别微乎其微,可以说忽略不计。
修改时:与查找相同,修改时唯一索引找到一次修改即返回。普通索引需要接着查找符合条件的数据并修改,感觉修改同样的数据可能唯一索引更快一些,但是有索引的存在,两者之间的差别微乎其微,可以说忽略不计。
综上所述:在写操作多的情况下不建议选择使用唯一索引。

10.索引的增删查SQL语句

创建表时即创建索引

create  table t1 (id int,name char(5),sex char(5),unique key uni_name(name),primary key(id),index idx_sex(sex)
);

创建索引

CREATE [UNIQUE/FULLTEXT] INDEX `index_name` ON `table_name` (column_list);
// 或者
ALTER TABLE `table_name` ADD INDEX|UNIQUE|PRIMARY KEY|FULLTEXT  `index_name` (column list);

删除索引

DROP index `index_name` ON `table_name` (column list);
// 或者
ALTER TABLE `table_name` DROP INDEX|UNIQUE|PRIMARY KEY `index_name` (column list);

查看索引

SHOW INDEX FROM|IN `table_name`;

修改索引:删除后重新创建

11.写在最后

文中内容都是我自己的理解,如果有错误或者不足请一定评论告诉我,万分感谢。

MySQL索引的详细分析和数据结构相关推荐

  1. mysql如何建立索引workbench_如何对MySQL索引进行优化分析

    为什么你写的sql查询慢?为什么你建的索引常失效?通过本章内容,你将学会MySQL性能下降的原因,索引的简介,索引创建的原则,explain命令的使用,以及explain输出字段的意义.助你了解索引, ...

  2. MySQL索引(详细,1万字长文)

    本文知识点较多,篇幅较长,请耐心学习 题记: 文章内容输出来源:拉勾教育Java高薪训练营. 本篇文章是 MySQL 学习课程中的一部分笔记. MySQL索引 索引类型 索引可以提升查询速度,会影响w ...

  3. MySQL索引机制(详细+原理+解析)

    MySQL索引机制 永远年轻,永远热泪盈眶 一.索引的类型与常见的操作 前缀索引 MySQL 前缀索引能有效减小索引文件的大小,提高索引的速度.但是前缀索引也有它的坏处:MySQL 不能在 ORDER ...

  4. MySQL索引面试题分析(索引分析,典型题目案例)

    [建表语句] create table test03(id int primary key not null auto_increment,c1 char(10),c2 char(10),c3 cha ...

  5. MySQL间隙锁详细分析

    什么是间隙锁 间隙锁(Gap Lock):间隙锁是(RR级别下)一个在索引记录之间的间隙上的锁,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间 首先要明确,间隙锁是为了防止 ...

  6. MySQL索引实现原理分析

    目前大部分数据库系统及文件系统都采用B-Tree(B树)或其变种B+Tree(B+树)作为索引结构.B+Tree是数据库系统实现索引的首选数据结构.在MySQL中,索引属于存储引擎级别的概念,不同存储 ...

  7. MySQL索引原理和引擎

    目录 一 索引原理 1. 循环遍历查找 2. 二分法查找 3. 有序数组 4. 链表 5. 二叉查找树 6. 平衡二叉树(AVL树) 7. B-树 8. b+树 二 MySQL存储引擎和索引 1 概念 ...

  8. MySQL索引的正确使用姿势

    B+树索引 索引设计是数据库设计最重要的一环.InnoDB 存储引擎支持的索引有 B+ 树索引.全文索引.R 树索引.下面重点讲下B+树索引. 那为什么关系型数据库都热衷支持 B+树索引呢?因为它是目 ...

  9. mysql索引优化cbo

    在实际工作中,我也经常会遇到一些同学提出这样的问题:MySQL 并没有按照自己的预想来选择索引,比如创建了索引但是选择了全表扫描,这肯定是 MySQL 数据库的 Bug,或者是索引出错了?当然不是! ...

最新文章

  1. CVPR2018上关于目标检测(object detection)
  2. 中国牛逼的程序员有哪些?入职华为两天转正,半个月升主任
  3. vscode 插件使用(前端力推)
  4. 如何避免DevOps变革的六大“焦油坑”
  5. spark-OutOfMemory:GC overhead limit exceeded 解决,timelimitexceeded
  6. x390拆机_用了七八年的笔记本电脑依然流畅如初,从X230i换到X390
  7. 20191011:冒泡排序的改良版--Shaker排序
  8. k8s的精简版k3s安装
  9. 计算机会计系统管理,会计电算化系统管理实验报告.doc
  10. Cisco思科交换机Vlan划分
  11. httpclient4下载图片 java实现
  12. 卸载office2010后 再安装2013 错误1706 安装程序找不到需要的文件
  13. iPhone所有手机型号屏幕尺寸及H5的CSS适配
  14. 台式计算机用什么网卡,台式电脑无线网卡怎么用 台式机无线网卡使用教程 - WiFi共享大师...
  15. 恶意程序检测之malconv模型
  16. Qt 窗口属性简介之Qt::WA_DeleteOnClose
  17. route和bridge是什么意思_请问ROUTE 和 BRIDGE 是怎么分别的!
  18. 2021-12-27 Java String contains() 方法用于判断字符串中是否包含指定的字符或字符串。用.toLowerCase().contains忽视大小写。
  19. 软件项目开发,交付文档(全)
  20. html5是什么意思,html5是什么意思?

热门文章

  1. 简历制作 | 保研 | 考研复试
  2. Excel的外部数据的引用
  3. 「Adobe国际认证」运用“对象选择”工具,在PS中快速建立选区
  4. 控件的颜色设置(本景色,文本色,文本背景色)
  5. 网络处理中TLV形式的不固定格式匹配
  6. 手算梯度下降法,详解神经网络迭代训练过程
  7. html盒子两个背景图片,css怎么实现两张图片叠加在一起,css添加盒子背景图片
  8. Redis 各种用法总结,你知道几种?
  9. 四川地区办理增值电信经营许可证
  10. Error response from daemon: Container 2c6d35b44a9862c63a6caf11a5622a33fe27979e12e51f9bd96f8dad98521c