Mysql引擎介绍及InnoDB逻辑存储结构

对于后端开发来说,数据库是我们日常开发中几乎都会使用到的。而且对于许多大型应用来说,往往数据库就是限制其性能的瓶颈所在。在以前的大多数时间里面,对数据库的认知,始终停留在一个比较浅的层面里。遂决定翻阅相关的书籍、博客和官方文档,让自己对数据库有一个全面的了解。

MySQL架构

以下是MySQL大体的组件结构


摘自https://www.rathishkumar.in/2016/04/understanding-mysql-architecture.html

如果只关心和聚焦于客户端向MySQL发送一条sql语句大致主要会涉及到的Server层的组件,可以查看下图:


摘自MySQL实战45讲

上面这张图的箭头标识和注释也说得比较清楚了。如果想要更加详细的说明,可以自行查阅相关文献资料。需要说明的是在MySQL8.0中,已经将查询缓存整个去掉了。

MySQL内置存储引擎介绍

上图介绍的MySQL架构大致可以分成Server层和存储引擎层的。MySQL提供的基于插件式的存储引擎,使得我们可以根据不同的需要选择不同的引擎,甚至是在同一个schema中的不同表,也可以使用不同的存储引擎。而实际的数据,也是存储在存储引擎中的。不同的存储引擎的架构和对数据的组织方式也有所不同。正是这些不同,决定了这些存储引擎提供了不同的特性和功能。

内置存储引擎

我们可以使用show engines来查看当前mysql内置了哪些存储引擎

show engines

三种常见的存储引擎区别及介绍
InnoDB

InnoDB从5.1版本以后,已经取代了MyISAM,成为MySQL默认的存储引擎了。可以从上表上看到,InnoDB是唯一支持事务、XA和Savepoints的内置存储引擎。同时,它还支持行锁、外键约束等。InnoDB表基于聚簇索引建立,并且采用MVCC来支持高并发,同时实现了ANSI SQL92定义的四种隔离级别,并在引擎内部实现了redo log和undo log。这些特性组合在一起,使得InnoDB成为了一个适合处理大量数据的高性能事务引擎。对于DBA来说,结合server层的bin log组成的日志系统机制,使得使用InnoDB作为数据存储引擎的数据库具备安全的崩溃恢复能力和快速稳定的复制性能,这些都是其它存储引擎所不具备的。所以在Oracle收购MySQL以后,没有了版权问题,InnoDB就毫无争议地成为了MySQL的默认存储引擎了。

MyISAM

在MySQL5.1及之前的版本,MyISAM都是默认的存储引擎。虽然不支持事务、不支持行级锁,崩溃后无法安全恢复。但是还MyISAM还是提供了许多其它的特性,包括全文索引、压缩、空间函数(GIS)等。这些特性在某些场景下的性能是高于其它存储引擎的,比如需要存储和批量查询归档日志数据,MyISAM引擎能提供较高的处理效率。

Memory

Memory存储引擎将表中的数据存储到内存中,为查询和引用其他表数据提供快速访问。因为是存在内存中,所以数据访问的速度一般也要快于其它存储引擎,同时Memory支持Hash索引,因此在单值查找的速度非常快。同时,MySQL在执行查询的过程中需要使用临时表来保存中间结果,内部使用的临时表就是Memory表(如果结果集大小超出Memory表的限制,则会转换成MyISAM表)。

使用哪一种引擎需要灵活选择,一个数据库中多个表可以使用不同引擎以满足各种性能和实际需求,使用合适的存储引擎,将会提高整个数据库的性能

功 能 MYISAM Memory InnoDB
存储限制 256TB RAM 64TB
支持哈希索引 No Yes No
支持全文索引 Yes No No
支持数索引 Yes Yes Yes
支持数据缓存 No N/A Yes
支持外键 No No Yes

InnoDB内存/磁盘结构及存储逻辑结构

InnoDB总体架构
https://dev.mysql.com/doc/refman/5.5/en/innodb-architecture.html
https://dev.mysql.com/doc/refman/5.7/en/innodb-architecture.html

上面这张图是InnoDB存储引擎在内存和磁盘上的对应结构。
这里分别取两个最常操作的update和select操作大致描述一下内部的流转机制(在默认的可重复读级别下, 同时略去了所有有关加锁释放锁的操作):
1.select * from xxx where id=1 语句:
(1)引擎接收到执行计划,会创建一个trx_id。
(2)查询是否在这个内存中,如果在,则返回行。如果在change buffer中或者不在内存中,则从磁盘中读入内存(在change buffer中还要涉及merge操作更新内存中的数据)。引擎层拿到数据,返回给到server层的执行器。
(3)如果Innodb发现某二级索引被频繁访问,会对该索引上创建一个哈希索引。下次同样的查询过来,会直接走这个自适应哈希索引。

2.update xxx set xxx where id=1 语句:
(1)引擎接收到执行计划,会为该事务创建一个trx_id。
(2)查询是否在这个内存中,如果在,则返回行。如果在change buffer中或者不在内存中,则从磁盘中读入内存(在change buffer中还要涉及merge操作更新内存中的数据)。引擎层拿到数据,返回给到server层的执行器。
(3)执行器更新这行的相关列数据,再通过API接口调用引擎层,更新修改后的行数据更新到内存中。同时写入redo log,此时处于prepare阶段。
(4)引擎层告知执行器已经已经执行完成,随时可以提交事务。
(5)server层的执行器将该操作写入bin log
(6)执行期通过API接口告知引擎提交事务,引擎把刚刚写入的redo log标记为commit状态,更新完成。(两阶段提交)

image.png

上面就是5.5版本下的文件组织,可以看到,每个schema都有一个文件夹,文件夹里面有 db.opt和*.frm格式的文件。db.opt存放的是字符集和字符集排序规则信息(字符文件,可以打开),frm文件存的是表结构等信息,官方同时也给出说明,frm里的信息和InnoDB数据字典有所重叠,是出于历史遗留原因(参见)。另外如果你没有设置innodb_file_per_tale=ON那么所有的数据文件都将存储在ibdata1文件中。所以这个文件会随着数据的增长而增长。同时我们也可以看到,InnoDB结构图中5.7相对与5.5最大的变化,就是对于ibdata1的拆分,它把原本共享表空间,undo表空间和临时表空间从ibdata1中分了出来。好处当然就是结构更加清晰,更加方便独立管理,不会出现之前临时表空间不再使用释放了,ibdata1文件还是那么大等情况。

InnoDB的数据逻辑结构

从上面InnoDB的架构图里面的右半部分可以知道,无论是索引还是数据,InnoDB都把它们存在.idb后缀(或者ibdata1)的文件中。而在MyISAM中,索引和数据是分别存储在MYI和MYD文件中的。

下图是InnoDB的数据组织形式


高性能MySQL-InnoDB聚簇索引

从图中可以看出,InnoDB数据其实就是保存在聚簇索引的叶子节点中的,并且按照主键列顺序存储在数据文件中的。
相比之下,MyISAM的数据则是按照插入的顺序存储在磁盘上的,其索引的叶子节点存储的是可以定位到实际数据的行号(或者是可以找到其物理位置的地址,这里隐藏了页的物理细节)。
这两种数据组织形式,使得下面两种引擎有如下区别:
1.由于使用聚簇索引,所以无法同时把数据行存放在两个地方,所以一个表只能有一个聚簇索引。而InnoDB的二级索引的叶子节点也只能存储聚簇索引上的主键值,从而导致二级索引(除覆盖索引以外)在查询数据的时候需要回表。
2.也由于MyISAM的索引不存在聚簇索引,叶子节点存储的是实际数据的行号,所以对MyISAM而言,主键索引和其它索引一样,不存在定位实际数据块上的性能差异。

这里有一个有意思的问题,如果InnoDB的二级索引的叶子节点和MyISAM一样,存储的是可以直接找到实际数据的行号,那岂不是可以避免了回表的问题。我个人感觉确实是这样的。但是那样会存在一个问题,那就是行锁和间隙锁的加锁问题。我们知道,InnoDB的行锁其实质就是加在索引上面加的锁,只要访问到该索引,就会在对应的索引上面加锁(包含回表的加锁)如果不回表的话,那么不同索引上面加锁并且相互独立,那么行锁和间隙锁就毫无意义了。反过来说,InnoDB为什么会做一个回表这样的逻辑,其实是在牺牲部分二级索引定位数据页的性能,来换取更细粒度的锁带来的显著性能提升。另外,如果在二级索引上存储的是实际数据的行号,那在数据页调整的时候,也要对这些二级索引进行更新,这同时也会导致写性能的下降。

InnoDB的行锁和间隙锁

image.png

前面有提到,InnoDB的锁是加在索引上的,行锁的引入减少了锁竞争的情况,从而提高了并发度。在不同隔离级别的不同语句下,加锁情况也是不一样的。从上图可以知道,有一些查询甚至不需要加锁,通过基于MVCC实现的一致性读就可以达到对应的隔离级别,这里又进一步提高了并发度。

总结

其实当我们大概了解了InnoDB架构组件中各个组件的作用,以及其数据存储的逻辑结构。也就大概明白了为什么InnoDB提供了这么多其它存储引擎不能提供的相关特性。例如:

1.redo log及对应的两阶段提交协议的引入,使得引擎可以提供在系统崩溃的时候提供安全崩溃恢复机制。
2.undo log机制的引入,每一事务都有一个单调递增的trx_id,使得Innodb可以基于MVCC对相关的事务做一致性读(或者称为快照读)。
3.InnoDB基于聚簇索引的数据组织形式,多数情况下通过行锁,间隙锁和快照读实现了四种隔离级别。这种方式相比与直接加表锁,性能更高,更加适合高并发场景。

通过根据其作用推出它能提供的特性,反过来也加深我们对各个组件的作用的理解。同时,通过思考这些组件的设计思想来实现对应的特性,这本身就是一个很有趣的过程。

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