MySQL索引之谜

分享主题

MSQL架构、索引介绍及原理

分享目标

  1. 理解MySQL架构组件及功能
  2. 理解MySQL执行流程
  3. 掌握MySQL中的表数据和索引在底层是如何被存储
  4. 掌握聚集索引(IOT索引组织表)和非聚集索引(堆组织表)的存储方式
  5. 掌握索引覆盖和回表概念

MySQL架构篇

Mysql架构

Connectors

连接器,指的是不同语言中与SQL的交互

Management Serveices & Utilities

系统管理和控制工具

Connection Pool: 连接池

  • 管理缓冲用户连接,线程处理等需要缓存的需求。
  • 负责监听对 MySQL Server 的各种请求,接收连接请求,转发所有连接请求到线程管理模块。每一个连接上MySQL Server 的客户端请求都会被分配(或创建)一个连接线程为其单独服务。
  • 而连接线程的主要工作就是负责 MySQL Server 与客户端的通信,接受客户端的命令请求,传递 Server 端的结果信息等。线程管理模块则负责管理维护这些连接线程。包括线程的创建,线程的 cache 等。

SQL Interface: SQL接口

接受用户的SQL命令,并且返回用户需要查询的结果。比如select from就是调用SQL Interface

Parser: 解析器

SQL命令传递到解析器的时候会被解析器验证和解析。

主要功能:

  • 将SQL语句进行语义和语法的分析,分解成数据结构,然后按照不同的操作类型进行分类,然后做出针对性的转发到后续步骤,SQL语句的传递和处理就是基于这个结构的。
  • 如果在分解过程中遇到错误,那么就说明这个sql语句是不合理的。

Optimizer: 查询优化器

SQL语句在查询之前会使用查询优化器对查询进行优化。explain语句查看的SQL语句执行计划,就是由查询优化器生成的。

Cache和Buffer: 查询缓存

他的主要功能是将客户端提交给MySQL的 select请求的返回结果集 cache 到内存中,与该 query 的一个 hash 值 做一个对应。该 Query 所取数据的基表发生任何数据的变化之后, MySQL 会自动使该 query 的Cache 失效。在读写比例非常高的应用系统中, Query Cache 对性能的提高是非常显著的。当然它对内存的消耗也是非常大的。
如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据。这个缓存机制是由一系列小缓存组成的。比如表缓存,记录缓存,key缓存,权限缓存等

Pluggable Storage Engines:存储引擎

与其他数据库例如Oracle 和SQL Server等数据库中只有一种存储引擎不同的是,MySQL有一个被称为“PluggableStorage Engine Architecture”(可插拔的存储引擎架构)的特性,也就意味着MySQL数据库提供了多种存储引擎。
而且存储引擎是针对表的,用户可以根据不同的需求为数据表选择不同的存储引擎,用户也可以根据自己的需要编写自己的存储引擎。也就是说,同一数据库不同的表可以选择不同的存储引擎

存储引擎介绍

  • MySQL存储引擎类型
存储引擎 说明
MyISAM 高速引擎,拥有较高的插入,查询速度,但不支持事务
InnoDB 5.5版本后MySQL的默认数据库,支持事务、行级锁、表级锁,比MyISAM处理速度稍慢
ISAM MyISAM的前身,MySQL5.0以后不再默认安装
MRG_MyISAM(MERGE) 将多个表联合成一个表使用,在超大规模数据存储时很有用
Memory 内存存储引擎,拥有极高的插入,更新和查询效率。但是会占用和数据量成正比的内存空间。只在内存上保存数据,意味着数据可能会丢失
Falcon 一种新的存储引擎,支持事务处理,传言可能是innoDB的替代者
Archive 将数据压缩后进行存储,非常适合存储大量的独立的历史记录数据,但是只能进行插入和查询操作
CSV CSV存储引擎是基于CSV格式文件存储数据(应用于跨平台的数据交换)
  • MyISAM和InnoDB存储引擎的区别
InnoDB MyISAM
存储文件 .frm 表定义文件
.ibd 数据文件和索引文件
.frm 表定义文件
.MYD 数据文件
.MYI 索引文件
存储限制 64TB
行锁、表锁 表锁
事务 支持 不支持
CRUD 读、写
count 扫表 专门存储的地方
支持外键 支持 不支持
索引结构 B+ Tree B+ Tree

简版执行流程图

简版执行流程图

详细执行流程图

详细执行流程图

物理文件

日志文件(顺序IO)

MySQL通过日志记录了数据库操作信息和错误信息。常用的日志文件包括错误日志、二进制日志、查询日志、慢查询日志、事务Redo日志、中继日志等。

错误日志(errorlog)

默认是开启的,而且从5.5.7以后无法关闭错误日志,错误日志记录了运行过程中遇到的所有严重的错误信息,以及MySQL每次启动和关闭的详细信息。

二进制日志(bin log)

默认是关闭的,binlog记录了数据库所有的ddl语句和dml语句,但不包括select语句内容,语句以时间的形式保存,描述了数据的变更顺序,binlog还包括了每个更新语句的执行时间信息。如果是DDL语句,则直接记录到binlog日志,而DML语句,必须通过事务提交才能记录到binlog日志中。

binlog主要用户实现mysql主从复制数据备份数据恢复数据监听

通用查询日志(general query log)

默认情况下通用查询日志是关闭的。查询日志记录客户端的所有语句(所有连接和语句都记录到通用日志)。

慢查询日志(slow query log)

慢查询日志记录了所有执行时间超过参数long_query_time设置值并且扫描记录数不小于min_examined_row_limit的所有SQL语句的日志(获得表锁定的时间不能算作执行时间)。

  • show_query_log:参数设置为ON,可以捕获执行时间超过一定数值的SQL语句。

  • long_query_time:默认是10秒,最小为0,精度可以到微妙

  • min_examined_row_limit:查询检查返回少于该参数指定行的SQL不被记录到慢查询日志

重做日志(redo log)

  • 确保事务的持久性。
  • 防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性。

回滚日志(undo log)

保存了事务发生之前数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),即非锁定读。

中继日志(relay log)

  • 是在主从复制环境中产生的日志。
  • 主要作用是为了从机可以从中继日志中获取到主机同步过来的SQL语句,然后执行到从机中。

数据文件(随机IO)

InnoDB数据文件

  • .frm文件:主要存放于表相关的数据信息,主要包括表结构的定义信息。
  • .ibd文件:使用独享表空间存储表数据和索引信息,一张表对应一个ibd文件。
  • ibdata文件:使用共享表空间存储表数据、索引和缓存信息,所有表共同使用一个或者多个ibdata文件。

MyISAM数据文件

  • .frm文件:主要存放于表相关的数据信息,主要包括表结构的定义信息。
  • .myd文件:主要用来存储表数据信息。
  • .myi文件:主要用来存储表数据文件中任何索引的数据树。

MySQL性能分析

  • 通过观察查询慢日志,确定具体哪些SQL比较慢(功能默认是关闭,需要手动开启)
  • 通过explain和慢SQL分析
  • 通过show profile查看慢SQL执行的性能情况,包括CPU、IO、执行SQL锁消耗的时间等。该工具在5.0.37以及以上版本中才能使用(功能默认是关闭,需要手动开启)

MySQL索引

索引是什么

  • 索引可以大大提高MySQL的检索速度
  • 使用B+树结构(多路搜索树,并不一定是二叉)
  • 索引是存储在磁盘文件中的(可能在单独的索引文件,也可能和数据存在一起)

索引的优势和劣势

优点

  • 索引大大减少了服务器需要扫描的数据量
  • 索引可以帮助服务器避免排序的临时表
  • 索引可以将随机I/O变成顺序IO

缺点

  • 索引需要占用磁盘空间
  • 索引在更新表时会降低效率,新增、修改、删除

哪些情况需要创建索引

  • 主键自动建立唯一索引
  • 频繁作为查询条件的字段应该创建索引
  • 多表关联查询中,关联字段应该创建索引 on 两边都要创建索引
  • 查询中排序的字段,应该创建索引
  • 频繁查找字段 覆盖索引
  • 查询中统计或者分组字段,应该创建索引 group by

哪些情况不需要创建索引

  • 表记录太少
  • 经常进行增删改操作的表
  • 频繁更新的字段
  • where条件里使用频率不高的字段

为什么使用组合索引

mysql创建组合索引的规则是首先会对组合索引的最左边的,也就是第一个a字段的数据进行排序,在第一个字段的排序基础上,然后再对后面第二个的b字段进行排序。其实就相当于实现了类似 order by a b这样一种排序规则。

为了节省mysql索引存储空间以及提升搜索性能,可建立组合索引(能使用组合索引就不使用单列索引)

例如:
创建组合索引(相当于建立了col1,col1 col2,col1 col2 col3三个索引):

1
ALTER TABLE 'table_name' ADD INDEX index_name('col1','col2','col3')

常用索引分类

单列索引

普通索引

  • 这是最基本的索引,它没有任何限制

唯一索引

  • 这种索引和普通索引基本相同,但有一个区别:索引列的所有值都只能出现一次,即必须唯一,允许为空
  • 主键索引(PRIMARY KEY)是一种特殊的唯一索引,创建类型推荐使用int类型
  • 建表时未设置主键索引,mysql会默认新建6byte空间的自动增长主键,可以用select _rowid from table来查询

组合索引

  • 在表中的多个字段组合成一个索引
  • 建议使用组合索引代替单列索引
  • 组合索引需要遵循最左原则

索引存储结构

索引存储结构介绍

  • 索引是在存储引擎中实现的。
  • MyISAM和InnoDB存储引擎:只支持BTREE索引,也就是默认使用BTREE,不能修改

B树和B+树

数据结构示例网站:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

B树示图
  • B树是为了磁盘或其它存储设备而设计的一种多叉(下面你会看到,相对于二叉,B树每个内结点有多个分支,即多叉)平衡查找树。 多叉平衡
  • B树的高度一般都是在2-4这个高度,树的高度直接影响IO读写的次数,以及查询时间复杂度(log(n))
  • 如果是三层树结构—支撑的数据可以达到20G,如果是四层树结构—支撑的数据可以达到几十T

B树

B+树示图

B+树

B树和B+树的区别

B树和B+树的最大区别在于非叶子节点是否存储数据的问题。

1
2
3
- B树是非叶子节点和叶子节点都会存储数据,存放数据地址
- B+树只有叶子节点才会存储数据,而且存储的数据都是在一行上,而且这些数据都是有指针指向的,也就是有顺序的。
索引列 order by id会使用到主键索引
非聚集索引(MyISAM)
  • B+树叶子节点只会存储数据行(数据文件)的指针,简单来说数据和索引不在一起,就是非聚集索引
  • 非聚集索引包含主键索引和辅助索引都会存储指针的值
主键索引

主键索引

这里设表一共有三列,假设我们以 Col1 为主键,则上图是一个 MyISAM 表的主索引(Primary key)示意。可以看出
MyISAM 的索引文件仅仅保存数据记录的地址。

辅助索引(次要索引)

在 MyISAM 中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求 key 是唯一的,而辅助索引的 key 可以重复。如果我们在 Col2 上建立一个辅助索引,则此索引的结构如下图所示

辅助索引

聚集索引(InnoDB)
  • 主键索引(聚集索引)的叶子节点会存储数据行,也就是说数据和索引是在一起,这就是聚集索引
  • 辅助索引只会存储主键值
  • 如果没有没有主键,则使用唯一索引建立聚集索引;如果没有唯一索引,MySQL会按照一定规则创建聚集索
主键索引
  • InnoDB 要求表必须有主键(MyISAM 可以没有),如果没有显式指定,则 MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL 自动为 InnoDB 表生成一个隐含字段作为主键,类型为长整形。

主键索引

  • 上图是 InnoDB 主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为 InnoDB 的数据文件本身要按主键聚集,
辅助索引(次要索引)
  • 第二个与 MyISAM 索引的不同是 InnoDB 的辅助索引 data 域存储相应记录主键的值而不是地址。换句话
    说,InnoDB 的所有辅助索引都引用主键作为 data 域。

    辅助索引

  • 聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。

1
2
3
select * from user where name='Alice' ##回表 
select id,name from user where name='Alice' ##覆盖索引
select id, age,name from user where name='Alice' ##覆盖索引

索引覆盖与回表查询

  • 索引覆盖是指返回的列在针对辅助索引树上都存在,name,age形成组合索引,查询返回的列id,name,age都在索引树上,不需要再次回表查询主键索引树就能返回查询数据
  • 回表是指查询条件非主键索引,通过辅助索引去查询数据,无法在辅助索引列上一次获取到所有需要的列字段,通过辅助索引查询获取到值(主键索引ID)再去查询主键索引树来获取返回列数据



【参考链接】

https://blog.csdn.net/zwjyyy1203/article/details/80664545

觉得文章有用的话,欢迎点赞打赏~