回顾

  • 每个索引都对应一棵B+树,B+树分为好多层,最下边一层是叶子节点,其余的是内节点。所有用户记录都存储在B+树的叶子节点,所有目录项记录都存储在内节点。
  • InnoDB 存储引擎会自动为主键(如果没有它会自动帮我们添加)建立聚簇索引,聚簇索引的叶子节点包含完整的用户记录。
  • 我们可以为自己感兴趣的列列建立二级索引 ,二级索引 的叶子节点包含的用户记录由索引列列+主键组成,所以如果想通过 二级索引来查找完整的用户记录的话,需要通过回表操作,也就是在通过二级索引找到主键值之后再到聚簇索引中查找完整的用户记录。
  • B+树中每层节点都是按照索引列列值从小到大的顺序排序而组成了了双向链表,而且每个页内的记录(不不论是用户记录还是目录项记录)都是按照索引列列的值从小到大的顺序而形成了了一个单链表。如果是联合索引的话,则页面和记录按照先按照联合索引前边的列列排序,如果该列列值相同,再按照联合索引后边的列列排序。
  • 通过索引查找记录是从B+树的根节点开始,一层一层向下搜索。由于每个页面都按照索引列列的值建立了了Page Directory(页目录),所以在这些页面中的查找非常快。

代价

  • 空间上的代价

    • 这个是而易见的,每建立一个索引都为要它建立一棵B+树,每一棵B+树的每一个节点都是一个数据页,一个页默认会占用 16KB的存储空间,一棵很大的B+树由许多数据页组成,那可是很大的一片存储空间
  • 时间上的代价
    • 每次对表中的数据进行增、删、改操作时,都需要去修改各个B+树索引。而且我们讲过,B+树每层节点都是按照索引列的值从小到大的顺序排序而组成了了双向链表。不不论是叶子节点中的记录,还是内节点中的记录(也就是不不论是用户记录还是目录项记录)都是按照索引列列的值从小到大的顺序而形成了一个单向链表。而增、删、改操作可能会对节点和记录的排序造成破坏,所以存储引擎需要额外的时间进行一些记录移位,页面分裂、页面回收啥的操作来维护好节点和记录的排序。如果我们建了了许多索引,每个索引对应的B+树都要进行相关的维护操作,这还能不不给性能拖后腿么?
  • 所以说,一个表上索引建的越多,就会占用越多的存储空间,在增删改记录的时候性能就越差。为了了能建立又好又少的索引,我们先得学学这些索引在哪些条件下起作用的。

适用条件

  • 前提:idx_name_date_age为索引

    • 全值匹配

      • 搜索条件中的列列和索引列一致的话,这种情况就称为全值匹配

        • select * from tableName where name = 'xxx' and date = '2020-01-01';,,包含查询中的2个列,查询过程:

          • 因为 B+ 树的数据页和记录先是按照name列的值进行排序的,所以先可以很快定位name列列的值是xxx的记录位置。
          • name列相同的记录里是按照date列的值进行排序的,所以在name列的值是xxx的记录里又可以快速定位 date列的值是 '2020-01-01' 的记录。

      上述条件的顺序不影响索引的命中,因为mysql有个查询优化器,会分析这些搜索条件并且按照可以使用的索引中列的顺序来决定使用哪个搜索条件,后使用哪个搜索条件。

    • 匹配左边的列(最左原则)

      • 搜索语句句中也可以不用包含全部联合索引中的列列,只包含左边的就行,比方说下边的查询语句句:

        • select * from tableName where name = 'xxx';或者select * from tableName where name = 'xxx' and date = 'xxx';
      • 对于下面的语句就用不到索引了
        • select * from date = 'xxx';
        • 原因:因为B+树的数据页和记录先是按照name列的值排序的,在name·列的值相同的情况下才使用date列进⾏行排序,也就是说name列的值不同的记录中date的值可能是无序的。而现在你跳过name列直接根据date`的值去查找,自然是用不上索引。
      • 对于下面只能用到name索引
        • select * from tableName where name = 'xxx' and age = 'xxx';
        • 原因:因为name值相同的记录先按照date的值进行排序,date值相同的记录才按照age值进行排序。
    • 匹配列前缀

      • 索引创建时候会按照索引列的字母开始进行排序,如name的情况
      • aaaa aaab aaac ...
        • 先比较字符串串的第一个字符,第一个字符小的那个字符串就比较小。
        • 如果两个字符串的第一个字符相同,那就再比较第二个字符,第二个字符比较小的那个字符串串就比较小。
        • 如果两个字符串的第二个字符也相同,那就接着比较第三个字符,依此类推。
      • 所以一个排好序的字符串列其实有这样的特点:
        • 先按照字符串串的第一个字符进行排序。
        • 如果第一个字符相同再按照第二个字符进行排序。
        • 如果第二个字符相同再按照第三个字符进行排序,依此类推。
      • 那么查询name为'AB'开头,可以这么写:
        • select * from tableName where name like 'AB%';
      • 假如仅仅是后缀或者中间,如'XXAB'或者'XABX',是不用到索引的,因为这时的'AB'并未排好序。
        • select * from tableName where name like '%AB%'
    • 匹配范围值

      • 语句查询过程为:

        • select * from tableName where name > 'AA' and name < 'BB';

          • 找到name值为AA的记录。
          • 找到name值为BB的记录。
          • 由于所有记录都是由链表连起来的(记录之间用单链表,数据页之间用双链表),所以他们之间的记录都可以很容易易的取出来
          • 找到这些记录的主键值,再到聚簇索引 中回表查找完整的记录。

        在使用联合索引进行范围查找的时候需要注意,如果对多个列同时进行范围查找的话,只有对索引最左边的那个列进行范围查找的时候才能用到B+树索引

        • select * from tableName where name > 'AA' AND name < 'BB' AND date > '2020-01-01';
        • 上边这个查询可以分成两个部分:
          1. 通过条件 name > 'AA' AND name <'BB'来对name进行范围,查找的结果可能有多条name值不不同的记录;
          1. 对这些name值不不同的记录继续通过date > '2020-01-01条件继续过滤。
        • 这样子对于联合索引idx_name_date_age来说,只能用到name列的部分,而用不到date 列的部分,因为只有 name值相同的情况下才能用date列的值进行排序,而这个查询中通过name进行范围查找的记录中可能并不是按照 date列进行排序的,所以在搜索条件中继续以date列进行查找时是用不到这个B+树索引的。
    • 精确匹配某一列并范围匹配另外一列

      • 对于同一个联合索引来说,虽然对多个列都进行范围查找时只能用到最左边那个索引列,但是如果左边的列是精确查找,则右边的列列可以进行范围查找

        • select * from tableName where name = 'AA' AND date > '2020-01-01' AND date < '2020-12-31' AND age > '10';
        • 这个查询的条件可以分为3个部分:
          • name = 'AA' ,对name列进行精确查找,当然可以使用 B+ 树索引了。
          • date > '2020-01-01'AND birthday<'2020-12-31',由于name列是精确查找,所以通过name='AA'条件查找后得到的结果的name 值都是相同的,它们会再按照date的值进行排序。所以此时对date列进行范围查找是可以用到B+树索引的。
          • age > '10',通过age的范围查找的记录的date的值可能不同,所以这个条件无法再利用B+树索引了了,只能遍历上一步查询得到的记录。
    • 用于排序

      • select * from table order by name,date,age limit 1;

        • 这个查询的结果集需要先按照name值排序,如果记录的name值相同,则需要按照date来排序,如果date的值相同,则需要按照age排序。

        关于联合索引,order by的子句句后边的列的顺序也必须按照索引列的顺序给出,如果给出order by date, age, name的顺序,那也是用不了B+树索引

    • 不可以使用索引进行排序的几种情况

      • ASC、DESC混用,要么全是ASC,要么全是DESC
      • WHERE子句中出现非排序使用到的索引列
      • 排序列列包含非同一个索引的列
      • 排序列列使用了复杂的表达式
    • 用于分组

      • select * from tableName group by name,date,age;
      • 上述语句原理跟order by一致,分组列的顺序也需要和索引列的顺序一致
  • 回表的代价

    • 关于使用一个二级索引

      • 会使用到两个B+树索引,一个二级索引,一个聚簇索引
      • 访问二级索引使用顺序I/O,访问聚簇索引使用随机I/O
    • 如何减少回表
      • 覆盖索引:查询的列包含索引列
  • 索引的挑选

    • 只为用于搜索、排序或分组的列创建索引
    • 考虑列的基数
    • 索引列的类型尽量小
      • 数据类型越小,在查询时进行的比较操作越快
      • 数据类型越小,索引占用的存储空间就越少,在一个数据页内就可以放下更更多的记录,从而减少磁盘I/O带来的性能损耗,也就意味着可以把更更多的数据页缓存在内存中,从而加快读写效率。

总结

  • B+树索引在空间和时间上都有代价,所以没事儿别瞎建索引。
  • B+树索引适用于下边这些情况:
    • 全值匹配
    • 匹配左边的列
    • 匹配范围值
    • 精确匹配某一列并范围匹配另外一列
    • 用于排序
    • 用于分组
  • 在使用索引时需要注意下边这些事项:
    • 只为用于搜索、排序或分组的列列创建索引
    • 为列列的基数大的列创建索引
    • 索引列列的类型尽量小
    • 可以只对字符串值的前缀建立索引
    • 只有索引列在比较表达式中单独出现才可以适用索引
    • 为了了尽可能少的让 聚簇索引发生页面分裂和记录移位的情况,建议让主键拥有AUTO_INCREMENT属性。
    • 定位并删除表中的重复和冗余索引
    • 尽量适用覆盖索引进行查询,避免回表带来的性能损耗

【mysql】- 索引使用篇的更多相关文章

  1. 初识mysql索引 - 小白篇

    :接触mysq也有两年左右的时间了,但是对该数据库的理解自认还比较初级,看过很多文章,也看过一些相关的书籍,依然小白....(这里个人总结是两点主要原因:1.对mysql的学习大部分都是源于看一些杂七 ...

  2. MySQL索引——总结篇

    MySQL索引 MySQL索引 数据库的三范式,反模式 零碎知识 索引 索引原理 B Tree索引 B+Tree索引 B Tree 与 B+Tree的比较 聚集索引和辅助索引 聚集索引的注意事项 索引 ...

  3. Mysql高手系列 - 第22篇:深入理解mysql索引原理,连载中

    Mysql系列的目标是:通过这个系列从入门到全面掌握一个高级开发所需要的全部技能. 欢迎大家加我微信itsoku一起交流java.算法.数据库相关技术. 这是Mysql系列第22篇. 背景 使用mys ...

  4. Mysql索引(一篇就够le)

    我想很多人对mysql的认知可能就是CRUD(代表创建(Create).更新(Update).读取(Retrieve)和删除(Delete)操作),也不敢说自己会用和熟悉mysql,当然我就是其中一个 ...

  5. 「 MySQL高级篇 」MySQL索引原理,设计原则

    大家好,我是melo,一名大二后台练习生,大年初三,我又来充当反内卷第一人了!!! 专栏引言 MySQL,一个熟悉又陌生的名词,早在学习Javaweb的时候,我们就用到了MySQL数据库,在那个阶段, ...

  6. 「MySQL高级篇」MySQL索引原理,设计原则

    大家好,我是melo,一名大二后台练习生,大年初三,我又来充当反内卷第一人了!!! 专栏引言 MySQL,一个熟悉又陌生的名词,早在学习Javaweb的时候,我们就用到了MySQL数据库,在那个阶段, ...

  7. mysql 索引篇

    一.索引优化   索引优化主要还是依赖explain命令,关于explain命令相信大家并不陌生,具体用法和字段含义可以参考官网explain-output,这里需要强调rows是核心指标,绝大部分r ...

  8. MySQL索引优化看这篇文章就够了!

    阅读本文大概需要 5 分钟. 来源:cnblogs.com/songwenjie/p/9410009.html 本文主要讨论MySQL索引的部分知识.将会从MySQL索引基础.索引优化实战和数据库索引 ...

  9. MySQL 第五篇:索引原理与慢查询优化

    一 介绍 为何要有索引? 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,因此对查询语句 ...

  10. 讲真,MySQL索引优化看这篇文章就够了

    本文主要讨论MySQL索引的部分知识.将会从MySQL索引基础.索引优化实战和数据库索引背后的数据结构三部分相关内容,下面一一展开. 一.MySQL——索引基础 首先,我们将从索引基础开始介绍一下什么 ...

随机推荐

  1. js事件入门(4)

    4.表单事件 表单事件处理主要用来验证表单,可以处理用户在表单上所做的任何操作. 4.1.onsubmit事件 当用户点击submit按钮来提交表单时,就会触发onsubmit事件,如果事件处理程序返 ...

  2. Linux hostname主机名配置文件/etc/hosts详解

    这篇文章为大家介绍linux hostname主机名配置文件/etc/hosts,包括主机名的用途.配置文件的操作方法等,有需要的朋友,可以参考下 1.什么是Linux主机名 无论在局域网还是INTE ...

  3. Taro 3 正式版发布:开放式跨端跨框架解决方案

    作者:凹凸曼 - yuche 从 Taro 第一个版本发布到现在,Taro 已经接受了来自于开源社区两年多的考验.今天我们很高兴地在党的生日发布 Taro 3(Taro Next)正式版,希望 Tar ...

  4. 记一次WIN10 WLAN消失修复

    事故现场:在win10自动更新后 在网路和Internet中WLAN消失 无法发现wifi了 设备管理器中wireless驱动上有黄色感叹号 解决办法: 右键有感叹号的wireless驱动,选择属性, ...

  5. 深入理解letter-spacing,word-spacing的对比区别

    letter-spacing lletter-spacing 属性增加或减少字符间的空白(字符间距). 该属性定义了在文本字符框之间插入多少空间.由于字符字形通常比其字符框要窄,指定长度值时,会调整字 ...

  6. 线性dp打鼹鼠

    题目大意 鼹鼠是一种很喜欢挖洞的动物,但每过一定的时间,它还是喜欢把头探出到地面上来透透气的.根据这个特点阿Q编写了一个打鼹鼠的游戏:在一个 的网格中,在某些时刻鼹鼠会在某一个网格探出头来透透气.你可 ...

  7. C#foreach原理

    本文主要记录我在学习C#中foreach遍历原理的心得体会. 对集合中的要素进行遍历是所有编码中经常涉及到的操作,因此大部分编程语言都把此过程写进了语法中,比如C#中的foreach.经常会看到下面的 ...

  8. java 面向对象(三十七):反射(一) 反射的概述

    1.本章的主要内容 2.关于反射的理解 Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属 ...

  9. web 部署专题(三):压力测试(一)工具 siege

    1.介绍 Siege是一个压力测试和评测工具,设计用于WEB开发这评估应用在压力下的承受能力:可以根据配置对一个WEB站点进行多用户的并发访问,记录每个用户所有请求过程的相应时间,并在一定数量的并发访 ...

  10. 机器学习实战基础(三十五):随机森林 (二)之 RandomForestClassifier 之重要参数

    RandomForestClassifier class sklearn.ensemble.RandomForestClassifier (n_estimators=’10’, criterion=’g ...