暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL 单表查询的底层实现(上)

一只可爱的小码农 2021-10-18
416

一、简介

MySQL有一个称为 查询优化器的模块,查询语句进行语法解析之后就会交给查询优化器来进行优化,优化的结果就是生成一个所谓的 执行计划,这个执行计划表明了应该使用哪些索引和表之间的连接顺序,最后会按照执行计划中的步骤调用存储引擎提供的方法来执行真正的查询。

情节需要,先建一个表single_table

  1. CREATE TABLE single_table (

  2.    id INT NOT NULL AUTO_INCREMENT,

  3.    key1 VARCHAR(100),

  4.    key2 INT,

  5.    key3 VARCHAR(100),

  6.    key_part1 VARCHAR(100),

  7.    key_part2 VARCHAR(100),

  8.    key_part3 VARCHAR(100),

  9.    common_field VARCHAR(100),

  10.    PRIMARY KEY (id),

  11.    KEY idx_key1 (key1),

  12.    UNIQUE KEY idx_key2 (key2),

  13.    KEY idx_key3 (key3),

  14.    KEY idx_key_part(key_part1, key_part2, key_part3)

  15. ) Engine=InnoDB CHARSET=utf8;

复制

我们为这个 single_table
表建立了1个聚簇索引和4个二级索引,分别是:

  • 为 id
    列建立的聚簇索引。

  • 为 key1
    列建立的 idx_key1
    二级索引。

  • 为 key2
    列建立的 idx_key2
    二级索引,而且该索引是唯一二级索引。

  • 为 key3
    列建立的 idx_key3
    二级索引。

  • 为 key_part1
    key_part2
    key_part3
    列建立的 idx_key_part
    二级索引,这也是一个联合索引。

插入10000行记录。

访问方法(access method)的概念

我们平时所写的那些查询语句本质上只是一种声明式的语法,只是告诉 MySQL我们要获取的数据符合哪些规则,至于 MySQL背地里是怎么把查询结果搞出来的那是 MySQL自己的事儿。对于单个表的查询来说,设计MySQL的大叔把查询的执行方式大致分为下边两种:

1.使用全表扫描进行查询

把表的每一行记录都查询一遍。不管是任何查询都可以使用这种方式执行,当然,这种也是最笨的执行方式。

2.使用索引进行查询

全表扫描要遍历好多记录,代价太大。如果查询语句中的搜索条件可以使用到某个索引,那直接使用索引来执行查询可能会加快查询执行的时间。使用索引分为许多种类:

  • 针对主键或唯一二级索引的等值查询

  • 针对普通二级索引的等值查询

  • 针对索引列的范围查询

  • 直接扫描整个索引

设计 MySQL的大叔把 MySQL执行查询语句的方式称之为“访问方”或者“访问类型”。同一个查询语句可能可以使用多种不同的访问方法来执行,虽然最后的结果一样,但是执行的时间可能相差甚远,就像是从南京到北京,你可以坐火箭去,也可以坐高铁去,当然也可以坐乌龟去。下边详述各种访问方法的具体内容。

const

根据主键定位记录:

  1. SELECT * FROM single_table WHERE id = 1438;

复制

示意图如下:

原谅我把聚簇索引对应的复杂的 B+树结构搞了一个极度精简版,为了突出重点,我们忽略掉了 页的结构,直接把所有的叶子节点的记录都放在一起展示,而且记录中只展示我们关心的索引列,对于 single_table表的聚簇索引来说,展示的就是 id列。我们想突出的重点就是: B+树叶子节点中的记录是按照索引列排序的,对于的聚簇索引来说,它对应的 B+树叶子节点中的记录就是按照 id列排序的。 B+树本来就是一个矮矮的大胖子,所以这样根据主键值定位一条记录的速度贼快。

类似的,我们根据唯一二级索引列来定位一条记录的速度也是贼快的,比如下边这个查询:

  1. SELECT * FROM single_table WHERE key2 = 3841;

复制

示意图如下:

可以看到这个查询的执行分两步,第一步先从 idx_key2对应的 B+树索引中根据 key2列与常数的等值比较条件定位到一条二级索引记录,然后再根据该记录的 id值到聚簇索引中获取到完整的用户记录。

设计 MySQL的大叔认为通过主键或者唯一二级索引列与常数的等值比较来定位一条记录是像坐火箭一样快的,所以他们把这种通过主键或者唯一二级索引列来定位一条记录的访问方法定义为: const,意思是常数级别的,代价是可以忽略不计的。不过这种 const访问方法只能在主键列或者唯一二级索引列和一个常数进行等值比较时才有效,如果主键或者唯一二级索引是由多个列构成的话,索引中的每一个列都需要与常数进行等值比较,这个 const访问方法才有效(这是因为只有该索引中全部列都采用等值比较才可以定位唯一的一条记录)。

对于唯一二级索引来说,查询该列为 NULL
值的情况比较特殊,比如这样:

  1. SELECT * FROM single_table WHERE key2 IS NULL;

复制

因为唯一二级索引列并不限制 NULL
值的数量,所以上述语句可能访问到多条记录,也就是说上边这个语句不可以使用 const
访问方法来执行。

稍微休息会儿,下篇继续。。。。。。





加我微信进交流群,多个朋友多条路。


文章转载自一只可爱的小码农,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论