一起来看看 MySQL 吧 —— 索引篇

135 阅读7分钟

索引 Index

MySQL中高效获取数据有序数据结构

以某种方式引用(指向)数据

MySQL 无索引的查询方式

全表扫描

优点缺点
提高数据检索效率,降低数据库IO成本占用空间
降低数据排序的成本,降低 CPU 的性能消耗降低了 CRD 的效率

索引结构

MySQL 的索引实现在存储引擎层实现,不同存储引擎有不同的结构

索引结构描述支持情况
B+ Tree最常见的索引类型,大部分引擎都支持 B+ 树索引All
Hash底层数据结构通过 Hash 表实现,只支持精确匹配索引列的查询才有效,不支持范围查询Memory
R-tree (空间索引)MyISAM 引擎的特殊索引类型,主要用于地理空间数据类型,使用较少MyISAM
Full-text (全文索引)通过建立倒排索引,快速匹配文档InnoDB >= 5.6、MyISAM

B+ Tree

二叉树的缺点

  • 顺序插入时,会形成一个链表,查询性能大大降低
  • 数据量大的情况下,层级深,检索速度慢

解决方法

  • 红黑树(自平衡二叉树)

    解决顺序插入问题

  • B-tree(多路平衡查找树)

    每个节点最多存储的key=最大度数1每个节点最多存储的 key = 最大度数 - 1
    每个节点最多存储的指针=最大度数每个节点最多存储的指针 = 最大度数

    B-tree 数据结构可视化

  • B+Tree

    • 所有的元素都会存在在叶子节点
    • 叶子结点形成一个单向链表

    B+Tree 数据结构可视化

在 MySQL 中索引数据结构对 B+Tree 进行了优化,通过增加一个指向相邻子节点的链表指针,形成了带有顺序指针的 B+Tree,提高了区间访问性能,即叶子结点之间形成双向循环链表

InnoDB 存储引擎选择 B+Tree 索引结构的原因

  • 相对于二叉树,层级更少,搜索效率更高
  • 对于 B-tree,无论是叶子节点还是非子夜节点都会保存数据,导致一页中存储的键值减少、指针减少,导致在保存大量数据时,只能增加树的高度,从而导致性能降低;而 B+Tree 数据存储在叶子节点,查询效率稳定而且叶子节点形成的双向链表容易实现范围查找
  • 相对 Hash 索引,B+Tree 支持范围匹配与排序操作

索引类型

按数据结构

类型含义特点关键字
主键索引针对表中主键创建的索引默认自动创建唯一PRIMARY
唯一索引避免同一个表中某个数据列的值重复可以有多个UNIQUE
常规索引快速定位特定数据可以有多个
全文索引全文查找文本中的关键词,而不是比较索引中的值可以有多个FULLTEXT

以下 SQL 语句哪个执行效率高

# id 为主键, name 为索引字段
​
SELECT * FROM `user` WHERE id = 10;
​
SELECT * FROM `user` WHERE name = 'Arm';

主键查询效率最高

建立索引的字段查询后获取行数据需要回表查询(根据主键查询行数据)

按照存储形式

分类含义特点
聚集索引 Clustered Index将数据存储与索引存储到一起,索引结构的叶子结点保存了行数据必须有,而且只能有一个
二级索引将数据与索引分开存储,存储结构的叶子结点关联的事对应的主键可以存储多个

聚集索引选取规则

  • 如果存在主键,主键索引就是聚集索引
  • 如果不存在主键,将使用第一个唯一索引作为聚集索引
  • 如果表没有主键,或者没有合适的唯一索引,InnoDB会自动生成一个 rowid 作为隐藏的聚集索引

按字段个数

分类特点
单列索引单个字段
联合索引多个字段

索引操作

  • 创建

    CREATE [UNIQUE|FULLTEXT] INDEX index_name ON table_name (index_column_name,...);
    
  • 查看

    SHOW INDEX FROM table_name;
    
  • 删除

    DROP INDEX index_name ON table_name;
    

索引使用

SQL 性能分析

  • SQL 执行频率

    SHOW GLOBAL STATUS LIKE 'Com_______';

  • 慢查询日志

    • 慢查询日志记录了所有执行时间超过了指定参数(long_query_time,, 单位: 秒,默认 10 秒)的所有 SQL 语句
    • MySQL 的慢查询日志默认没有开启

    慢查询日志配置

    # /etc/my.cnf
    # 开启 MySQL 慢日志查询
    slow_query_log = 1# 设置慢日志的时间为 n 秒
    long_query_time = n
    

    慢查询日志文件

    /var/lib/mysql/localhost-slow.log
    
  • profile 详情

    • 查看是否支持

      SELECT @@have_profiling;

    • 设置 profile

      SET profiling = 1; // 1 - session 2 - global

    • 查看 profile

      SHOW profiles;
      ​
      SHOW profile FOR QUERY query_id;
      ​
      SHOW profile CPU for QUERY query_id;
      
    • explain 执行计划

      通过 EXPLAIN 或者 DESC 命令获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序

      [EXPLAIN | DESC] SELECT 字段 FROM 表名 WHERE 条件;
      
      列名含义
      idselect 查询的序列号,表示查询中执行 select 自居或者是操作表的顺序( id 相同,执行顺序从上到下;id 不同,值越大,优先级越高)
      select_type表示 select 的类型,常见的取值有 SIMPLE (简单表,不实用表连接或者子查询)、PRIMARY(主查询,外层的查询)、UNION(UNION 中的第二个或者后面的查询语句)、SUBQUERY(select/where 之后包含了子查询)等
      type表示连接类型,性能由好到差的连接类型为 NULL、system、const、eq_ref、ref、range、index、all
      possible_key当前表上可能用到的索引
      key当前表实际用到的索引
      key_len表示索引中使用的字节数,为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,越短越好
      ref
      rowsMySQL 判断的需要执行查询的行数,在 InnoDB 中,是一个估计值
      filtered表示返回结果的行数占需读取行数的百分比,值越大性能越好
      extra额外信息

1. 前缀索引

使用磨个字段中字符串的前几个字符建立索引

使用过前缀索引可以见效索引字段的大小,增加一个索引页中存储的索引值,从而有效提高索引的查询速度

同时,在一些大字符串的字段作为索引是,可以减小索引项的大小

局限性

  • order by 无法使用前缀索引
  • 前缀索引无法用作覆盖索引

2. 覆盖索引

SQL 中 query 的所有字段,在 B+Tree 的叶子上都可以找到,从而使从二级索引中查询得到记录,而不需要通过聚簇索引查询,避免回表查询的操作

优点

不需要查询包含整行记录的所有信息,大大的节省了 IO 操作

3. 主键索引最好是自增的且长度尽量小

InnoDB 创建的主键索引默认为聚簇索引,数据存放在 B+Tree 的叶子结点上,即同一个叶子结点内的各个数据是按照主键顺序存放的。同时主键字段长度越小,二级索引的叶子结点越小,减小空间开销

优点

  • 在使用自增主键时,插入的新数据可以按顺序添加到当前索引的节点,因此每插入一条新纪录都是追加操作,不需要重新移动数据
  • 在使用非自增主键时,插入的索引值都是随机的,甚至需要从一个索引页移动到另一个索引页,从而导致也分裂,可能造成大量内存碎片,从而导致索引结构不紧凑,从而影响查询效率

4. 索引最好为 NOT NULL

  • NULL 会导致优化器在做索引选择时更加复杂且难以优化
  • NULL 没有实际意义但是会占用物理空间

5. 防止索引失效

索引失效的场景

最左前缀法则

如果使用了联合索引,要遵守最左前缀法则(查询从索引最左列开始,并不跳过索引中的列)

如果跳过索引中的列,索引将部分失效

优先使用>=<=

联合索引中出现范围查询范围查询右侧的列索引失效

避免在索引列进行运算

索引列上进行运算操作,索引失效

字符串字段加引号

字符串类型字段使用时,不加引号,索引失效

避免头部模糊匹配

尾部模糊匹配,索引不会失效,而头部模糊匹配,索引失效

or 分割开的连接条件都要有索引

如果 or 前的条件中的列有索引,而后面的列中没有索引,索引失效

数据分布影响

如果 MySQL 评估使用索引比全表查询更慢,索引失效