MySQL学习笔记Ⅱ-索引

  • 索引是什么

    索引图解

    ​ 数据库索引,是数据库管理系统(DBMS)中的一个排序的数据结构,以协助快速查询、更新数据库表中数据。

    image-20230106112752440

    ​ 数据是以文件的形式存放在磁盘上面的,每一行数据都有它的磁盘地址。而索引就是存放这些地址的数据结构。

    索引类型

    • Normal:非唯一索引,是最普通的索引,没有任何限制
    • Unique:唯一索引要求键值不能重复。主键索引是一种特殊的唯一索引,它要求键值不能为空。主键索引使用primary key创建。
    • Fulltext:全文索引。针对比较大的数据,几KB大小的数据情况,如果要解决like查询效率低的问题,可以创建全文索引。只有文本类型的字段才能创建全文索引,比如char、varchar、text。
    • MyISAM和InnoDB都支持全文索引。

    索引存储模型推演

    • 二叉查找树(BST,Binary Search Tree):左小右大。当插入的数据刚好是有序的时候,就会变成链表,斜树。不够平衡。

    • 平衡二叉树(AVL Tree):左右子树的深度差不超过1。需要通过左旋右旋,RL,LR进行调整确保平衡。每个节点都必须包含索引字段键值、数据磁盘地址(一行)、左右子树的引用。一个树的节点就是16K的大小,但实际使用不需要这么多,所以访问一个树节点进行一次IO的时候就浪费了大量的空间。

    • 多路平衡查找树(B Tree):分裂合并。节点关键字大于等于1,每个节点的分叉数永远比关键字树多1。每个节点的存储内容和AVL Tree差不多。节点的分裂和合并,其实就是InnoDB页(page)的分裂和合并。

    • B+树

      特点:

      1. 它的关键字的数量和路数是相等的。

      2. B+Tree的根节点和枝节点中都不会存储数据,只有叶子结点才存储数据。找到关键字并不会返回,而是将继续到叶子结点。

      3. B+树的每个叶子结点增加了一个指向相邻节点的指针,它的最后一个数据会指向下一个叶子节点的第一个数据,形成一个有序的链表结构。

      优势:

      1. 是B树的变种,B树能解决的它都能解决。(每个节点存储更多的关键字,路数更多)
      2. 扫库、扫表的能力更强(对表进行全表扫描,只需要遍历叶子结点就可以了,不需要遍历整棵树)
      3. B+树对磁盘读写能力相对于B树来说更强(根节点和枝节点不保存数据区,所以一个节点可以保存更多的关键字,一次磁盘加载的关键字更多)
      4. 排序能力更强(叶子节点上有下一数据区的指针,数据形成了链表)
      5. 效率更稳定(B+树永远是在叶子结点上拿到数据,所以IO次数是稳定的)
    • 索引的实现方法:

      • B/B+树:适合用来查找某一范围内的数据,支持数据排序。MyISAM表数据文件和索引文件是分离的,索引文件仅保存数据记录的磁盘地址;InnoDB表数据文件本身就是主索引,叶节点data域保存了完整的数据记录。
      • Hash:仅支持”=”,”IN”和”<=>”精确查询,不范围查询,不支持排序,不支持排序,只有Memory引擎支持显式的Hash索引,但是它的Hash是nonunique的,冲突太多时也会影响查找性能。
      • R-Tree:仅支持geometry数据类型。
    • MyISAM

      MyISAM有两个文件:

      • .MYD:D代表Data,是数据文件,存放数据记录,所有表数据。

      • .MYI:I表示Index,是索引文件,存放索引。

      在MyISAM中数据和索引是两个独立的文件。MyISAM的B+树中叶子结点存储的是数据文件对应的磁盘地址。所以从.MYI中找到键值之后,会到数据文件.MYD中获取相应的数据记录。

    • InnoDB

      在InnoDB中,是以主键为索引来组织数据的存储的,所以索引文件和数据文件是同一个文件,都在.ibd文件里面。在InnoDB的主键索引的叶子节点上,直接存储了数据。

    • 聚集索引(聚簇索引)

      索引键值的逻辑顺序和表数据行的物理存储顺序是一致的。在InnoDB中,它组织数据的方式叫做(聚集)索引组织表(clustered index organize table),所以主键索引是聚集索引,非主键索引都是非聚集索引。

    • 辅助索引

      非主键索引,非聚集索引。辅助索引存储的是辅助索引和主键值,如果使用辅助索引查询,会根据主键值在主键索引中查询,最终取得数据。

    • 无主键情形

      未指定主键时,InnoDB会选择第一个不包含有NULL值的唯一索引作为主键索引,如果没有这样的唯一索引,啧InnoDB会选择内置6字节长的ROWID作为隐藏的聚集索引,会随着行记录的写入而逐渐递增。

    索引使用原则

    列的离散度

    count(distinct(column_name)):count(*),列的全部不同值和所有数据行的比例。数据行数相同的情况下,分子越大列的离散度就越高。如果列的重复值越多,离散度就越低。离散度越高需要扫描的行数就越少,查询速度就越快。

    联合索引最左匹配

    • 多条件查询时会建立联合索引,单列索引可看做是特殊的联合索引。

    • alter table user_innodb add index comidx_name_phone(name,phone),这就是给user表建立了一个name和phone的联合索引。联合索引在B+Tree中是复合的数据结构,它是按照从左到右的顺序来建立搜索树的(name在左边,phone在右边)。name是有序的,phone是无序的。当name相等的时候phone才是有序的。在使用wher name='name' and phone='131***'去查询数据的时候,会优先比较name来确定下一步应该搜索的方向,在name相同的时候再比较phone。但是如果查询条件中没有name,就不知道第一步应查询哪个节点,因为建立搜索树的时候name是第一个比较因子,所以用不到索引。

    • 对于联合索引(a,b)只有在使用右边的b字段时,无法使用索引,会全表扫描,其他情况都会使用联合索引。

    • 创建联合索引:

      create index idx_name_phone on user_innodb(name,phone)

      create index idx_a_b_c on user_innodb(a,b,c),想当于创建三个索引:index(a),index(a,b),index(a,b,c)即最左边的是必要条件。

    覆盖索引

    ​ 回表:非主键索引,先通过索引找到主键索引的键值,再通过主键值查出索引里面没有的数据,它比基于主键索引的查询多扫描了一棵索引树,这个过程就叫做回表。

    ​ 例如:select * from user_innodb where name='qqs'

    ​ 在辅助索引里面,不管是单列索引还是联合索引,如果select的数据列只用从索引中就能取得,不必从数据区中读取,这时候使用的索引就叫做覆盖索引,这样就避免了回表。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    -- 创建联合索引
    alter table user_innodb drop index comixd_name_phone;
    alter table user_innodb add index 'comixd_name_phone' ('name','phone');
    -- 下面三个查询语句都用到了覆盖索引
    explain select name,phone from user_innodb where name='qq2' and phone='1232321';
    explain select name from user_innodb where name='qq2' and phone='12313123';
    explain select phone from user_innodb where name='qq2' and phone='12313123';
    -- 下条查询语句用不到覆盖索引
    select *

    ​ 假若一个主键索引,其他3个字段建立辅助索引,select *也属于覆盖索引。

    索引的创建与使用

    • 索引的创建

      1. 在用于where判断order排序和join的(on)字段上创建索引。
      2. 索引的个数不要过多–浪费空间,更新变慢。
      3. 过长的字段,建立前缀索引。
      4. 区分度低的字段,例如性别,不要建索引。离散度太低,导致扫描行数太多。
      5. 频繁更新的值,不要作为主键或者索引。 页分裂。
      6. 随机无序的值,不建议作为主键索引,例如身份证,UUID。无序,分裂。
      7. 组合索引把散列性高(区分度高)的值放在前面。
      8. 创建复合索引,而不是修改单列索引。
    • 什么时候用不到索引

      1. 索引列上使用函数(replace\substr\concat\sum count avg)、表达式(+-*/)。

      2. 字符串不加引号,出现隐式转换。

      3. like条件中前面带%,where条件中like abc%,like %23432%,like %777都用不到索引,因为过滤的开销太大,这时候可以用全文索引。

      4. 负向查询 not like 不能;!=, <>,not in在某些情况下可以:

        1
        2
        3
        explain select * from employees where emp_no not in (1);
        explain select * from employees where emp_no <> 1
        -- 跟数据库版本、数据量、数据选择度都有关系
      5. 其实,用不用索引,最终都是优化器说了算。

        Optimizer,是基于cost开销(cost base optimizer),不是基于规则(rule-based optimizer),也不是基于语义。怎么开销小就怎么来。

    • 使用索引有基本原则,但没有具体规则

    作者: Meow Mii

    本文链接: https://blog.yiochin.top/p/7e988c00.html

    版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议,转载请注明出处!


    📝 Comment