MySQL 之 InnoDB Index 浅谈

  MySQL 一直是 RDBMS 型数据库中用户最多,使用最广的数据库。主要是因为它开源、免费、文档丰富等。在 MySQL 查询优化中,提得最多、用得最广的就是 Index, 那我们就来慢慢的揭开它神秘的面纱吧。

Structure

InnoDB-physical-structure-overview.png

  MySQL 是基于磁盘的数据库,所以 MySQL 的所有信息皆是以文件的形式存储于磁盘,我想这是借鉴了 Linux 的设计哲学 "Everything is a file" 吧。进入 MySQL 使用如下命令查看数据目录。

mysql> show variables where Variable_Name LIKE "%dir";
+---------------------------+----------------------------+
| Variable_name             | Value                      |
+---------------------------+----------------------------+
| basedir                   | /usr/                      |
| character_sets_dir        | /usr/share/mysql/charsets/ |
| datadir                   | /var/lib/mysql/            |
| innodb_data_home_dir      |                            |
| innodb_log_group_home_dir | ./                         |
| innodb_tmpdir             |                            |
| lc_messages_dir           | /usr/share/mysql/          |
| plugin_dir                | /usr/lib/mysql/plugin/     |
| slave_load_tmpdir         | /tmp                       |
| tmpdir                    | /tmp                       |
+---------------------------+----------------------------+
10 rows in set (0.00 sec)

Spaces

  进入 datadir/database 目录, 文件分为 .frm.ibd 两类文件,分别是表的结构文件与数据文件。以 var/lib/mysql 为数据目录,repldbdb_name, repl_tab1 作为表名,展示如下:

root@mysql-master:/var/lib/mysql/repldb# ls -l
-rw-r----- 1 mysql mysql    61 Apr 28  2019 db.opt
-rw-r----- 1 mysql mysql  8676 Apr 28  2019 repl_tb1.frm
-rw-r----- 1 mysql mysql 98304 Apr 28  2019 repl_tb1.ibd

  以上是我们可以简单看到的物理文件,那在 MySQL 的逻辑当中,它又是如何的呢? InnoDB 的数据存储使用 "spaces" 进行存储,通常我们叫它 "tablespaces", 有时候 InnoDB 内部也叫它 "filespaces"。一个 space 可由 1 个或 n 个实际的文件组成(如: ibdata1,ibdata2 等, 多个文件时,它们带有特定的顺序和序号),但不管它由多少个实际的文件进行存储,它只组成一个逻辑文件, 每个 space 文件在 InnoDB 中被赋于一个 32-bit Int 的 space_id 作为标识符。.idb 可以包含多个表文件,但是在 MySQL 的实现中,一个 .ibd 文件只包含一个表,所以把一个 .ibd 文件等价于一个表文件。

Pages

  前面已经知道 MySQL 把每个表的数据存储在一个 data file 也就是 .ibd 文件中,又因为 MySQL 是基于磁盘的数据库系统,那是不是查询的时候把物理文件一次性加载到内存当中,然后再进行过滤查询呢?或者进一步的也文件流的形式载入内存,然后再过滤呢? 是的,MySQL 的查询确实需要把物理文件加载到内存才能进行处理,但是 MySQL 的操作都不是前面提到的两种方式。MySQL 采用 "分而治之" 的办法,把 .ibd 逻辑分割成小块,然后每次加载一个或几个块到内存再进行处理,这就是 MySQL 的查询处理方式,这些小块就是 MySQL 的数据处理最小单元。这些块在 MySQL 中称为一个Page, 有时也叫一个block, 每个PageSpace一样有一个32-bitInt 的标识符page_no, 每个page默认固定大小为16KB, 可通过参数 innodb_page_size 进行调整,如: 4K、8K、32K、64K 等。Page 由以下几部分组成 详细:

  • Fil Header
  • Page Header
  • Infimun + Supermum Records
  • User Records
  • Free Space
  • Page Directory
  • Fil Trailer

  Page 与 Page 之间是否是互不相关呢? 各 Page 之间的关系在 Page 的 Fil Header 中进行定义, Page 之间以 Doubly-Linked的方式进行关联,Page 的结构详细如下:

FIL_PAGE_SPACE //就是所谓的 `page_no`
FIL_PAGE_OFFSET
FIL_PAGE_PREV //指向前一个 Page
FIL_PAGE_NEXT //指向后一个 Page
FIL_PAGE_LSN  //LSN: log serial number
FIL_PAGE_TYPE
FIL_PAGE_FILE_FLUSH_LSN
FIL_PAGE_ARCH_LOG_NO

Records

  经过前面的了解,我们已经知道真实的数据是分布在 space 空间的每个 page 内,那如何才能得得到每行数据记录呢?Page 的组成部分中有一项 User Records, 那就是真实数据存储位置。当然了,Record 有自己的 结构,主要分为 Compact 和 Redundant 两种格式,但不管何种格式,它里一个指向下一条记录的数据 next_record, 通过这样的方式,各 Record 就以 Singly-Linked 的形式进行连接。

Index

  通过前面的介绍,我们已经知道了 MySQL InnoDB 引擎对于数据的处理方式和存储结构,当我们查询数据时,先定位 Table Space、再定位 Page、最后定位 Record,这是一个完整的数据查询过程。那 Index 能够帮助我们做什么呢? Index 其实就是帮助我们快速定位记录,缩短 space --> page --> record 这个过程。

  MySQL 是基于磁盘的数据库系统,有说过它的设计哲学和 Linux 的 "Everything is a file" 的设计哲学相符,那么 MySQL 的 Index 的设计哲学又是什么呢?

"Everything is an index in InnoDB".

  Index 分为 Clustered IndexSecondary Indexes 两类, MySQL 中的索引的存储根据不同的数据表和引擎又采用不同的形式,一般情况通常采用 B-Tree 索引, 空间数据采用 R-Tree索引, 内存数据采用 Hash 索引,文档数据采用 Full Text 索引等。

Clustered Index

  MySQL 中以 InnoDB 作为引擎时,每张表有且仅有一个 Clustered Index,而且每张表必须有一个 Clustered Index,下面详细了解一下 Clustered Index 的生成规则。

  • 有主键时,InnoDB 使用它作为 Clustered Index;
  • 无主键
    • unique 列且 not null 列, MySQL 用第一个符合该条件的列作为 Clustered Index;
    • 且无合适的 unique 列, InnoDB 会内部生成一个名称为 GEN_CLUST_INDEX 的隐藏索引,该索引以隐藏的 6-byte 的 row_id 列作为索引列,该列以数据行插入的顺序进行排序;

Q: 那为什么一定要有一个Clustered Index呢?

A: 因为 MySQL 需要快速定位 Record, 所以必须要有一个索引,就像书的目录一样,通过索引就可以快速定位到记录对应的 Page

Q: 那索引里面存储的是什么呢?

A: Clustered Index 里面包含了 索引列page 的映射关系,而且直接指向了 page 中的行记录, 而这也是Clustered Index 之所以快的原因。

Secondary Indexes

  MySQL 中有仅仅有一个 Clustered Index,但是对于 Secondary Indexes 则可以存在多个(这也是两个 Index 的写法分单数复数的原因)。 在 InnoDB 中所有的 Secondary Indexes 都是基于 Clustered Index 的,每一个 Secondary Index 都包含 Primary Keys, 也就是 Clustered Index 中的索引列。

Q: Secondary Index 也是直接指向 page 中的记录吗?

A: Secondary Index 并不直接指向 page, 它先指向 Clustered Index 然后再指向 page 内数据 (这种情况也称为 回表)。

Index-selective

  • Leftmost Prefix

    当有多列索引(multiple-column,也叫联合索引) 时,索引形式如(c1, c2, c3), 查询条件如 (c1), (c1, c2) 以及 (c1, c2, c3) 均可利用到该索引。

  • Index-Extensions

    InnoDB 自动把 Primary Key 附加到 Secondary Index 后面,如 PK(c1, c2), 同时也定义了 Secondary Index 其包含 (c3), InnoDB 内部优化器 (optimizer) 会把其扩展为 (c3, c1, c2) 的形式, 从而提高查询效率。该功能需要通过指令 SET optimizer_switch = 'use_index_extensions=off' 进行启动和停用,InnoDB 默认属于开启状态。

  • Index-Merge

    所谓索引融合,就是对多个索引进行条件扫描,然后再把它们的结果合并。

  • Index-Cover

    所谓索引覆盖,就是查询的列通过索引就能够取得,这种情况下不需要再定位到page去取数据。避免了磁盘读写、回表查询等操作,所以这种情况比一般的索引查询效率更高。

pros and cons

  当了解了索引的功效之后,我们是不是为表建立越多的索引越好呢? 并不如此,索引也有它的适用范围,和代价,所以并不是索引越多越好。

Pros

  • 减少数据量的扫描;
  • 当索引列有排序操作时,可以避免服务端的排序与临时表的产生;
  • 可以把随机的磁盘 I/O 变为顺序的 I/O;

Cons

  当数据量少的时候建立索引其实并没有什么用,如果数据量还不足一个 page, 而 page 又是数据处理的最小单位,所以这个时候有没有索引都一样;那当数据量很大的时候呢,这个时候如果数据量非常大,那么索引文件相应的也变得很大,如果 MySQL 需要多次才能扫描完索引的内容,那么这时候索引也没有用了,因为索引的读取也得造成磁盘的 I/O, 且也不能短时间定位到对应的数据。 而且索引的建立也是有一定的代价的,如每次插入数据时,相比于无索引情况需要另外的磁盘写操作,对应的也占用更多的磁盘空间,读取的时候相应的也占用更多的内存容量,数据更新随之而来的也是索引更新成本。

Summary

  索引并不适用于所有的情况,只有当索引的成本小于它的代价时,才是有效的索引。所以建立索引时需要综合考虑实际的业务场景,再结合 MySQL 查询优化器以及索引选择原理才能够建立适合自己业务的索引。

Appendix

  • B+tree 为什么快 ?
  • 查询到底什么了什么索引 ?
  • Page 详细结构 ?
  • Record 详细结构 ?
微信公众号
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343