回顾

  • 每个索引都对应一棵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. 位运算实现加减乘除四则运算(Java)

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 本文是继< ...

  2. 【DMCP】2020-CVPR-DMCP Differentiable Markov Channel Pruning for Neural Networks-论文阅读

    DMCP 2020-CVPR-DMCP Differentiable Markov Channel Pruning for Neural Networks Shaopeng Guo(sensetime ...

  3. Java WebService学习笔记 - Axis(一)

    WebService 简介 实际开发中,很多系统都是基于历史遗留系统进行开发,有时,这些系统基于不同的语言,如C,C++,C#,java,PHP等等.为了实现历史系统的再利用,或向外部程序暴露调用接口 ...

  4. Struts2 执行流程 以及 Action与Servlet比较 (个人理解)

    上图提供了struts2的执行流程.如下: 1:从客户端发出请求(HTTPServletRequest). 2:请求经过各种过滤器(filter),注:一般情况下,如SiteMesh等其他过滤器要放在 ...

  5. Python3-json & pickle 序列化

    JSON(JavaScript Object Notation, JS对象标记)是一中轻量级的数据交换格式,大部分语言都支持 Python3中的json提供了对json格式数据的序列化支持 json. ...

  6. Python实用笔记 (26)面向对象高级编程——定制类

    Python的class允许定义许多定制方法,可以让我们非常方便地生成特定的类.以下是集中常见的定制方法: 怎么才能打印得好看呢?只需要定义好__str__()方法,返回一个好看的字符串就可以了: _ ...

  7. Nginx 反向代理可以缓存 HTTP POST 请求页面吗?

    摘要: Nginx 反向代理可以缓存 HTTP POST 请求页面吗?  2017-09-05 景峯 Netkiller 本文节选自<Netkiller Web 手札> 作者:netkil ...

  8. POJ 3057 Evacuation 题解

    题目 Fires can be disastrous, especially when a fire breaks out in a room that is completely filled wi ...

  9. Python-自动用0补取长度

    描述 Python zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0. 语法 zfill()方法语法: str.zfill(width) 参数 width -- 指定字符串的长度. ...

  10. 安装更强大更美观的zsh,配置oh my zsh及插件

    安装更强大更美观的zsh,配置oh my zsh及插件 #0x0 安装zsh #0x1 安装oh my zsh #0x2 配置zshrc #0x3 配置主题 #0x4 安装插件 #0x5 小结 #0x ...