MySQL 高级 —— 深入理解 InnoDB 与 MyISAM

引言

InnoDB作为事务型数据的首选存储引擎,是中高级程序员必须掌握的知识,与之经常一同提起的MyISAM,也是在应用场景中频繁会接触的典型存储引擎。

在《高性能MySQL》第五章中,有关于这两种引擎的索引描述,本篇博客将结合书中内容进行总结和概括,帮助更好地理解其内部的存储方式。

一、查看数据库存储引擎的SQL语句

SHOW ENGINES;

SHOW VARIABLES LIKE '%storage_engine%';

 

二、InnoDB 和 MyISAM 存储引擎的比较

关于InnoDB和MyISAM的常规比较,下表是重点。

三、InnoDB 和 MyISAM 的数据分布

在《高性能MySQL》第五章,作者围绕着数据在两种截然不同的存储引擎中是如何存储的进行了细致的分析。

先来说说MyISAM存储引擎。它对表中数据有单独的存储文件,所谓“单独的” 指的是数据和主键是分开存储的。这一点与InnoDB有着本质的区别。这在数据库领域,叫做——非聚簇索引

我思考了一下,如果让我去设计一个存储引擎,根据我的知识水平,多半就是会设计成MyISAM这样的数据存储结构。我们先来看一下它是如何来存储数据和主键的:

首先,不论在InnoDB还是在MyISAM中,索引都是以B树的形式来存储的,这没什么好说的(参考《MySQL 高级 —— 索引实现的思考》),然后我们看到,主键索引树中的叶子节点都会指向具体的数据行。

也就是说,MyISAM分开存储了主键列和数据行,然后通过在主键索引的叶子节点中同时保存列值(主键值)和指向数据行的指针,从而实现关联。这在计算机领域是一种非常典型的键值关联的方式。这也是为什么我说,如果要我来设计存储引擎,可能多半也是这样做的原因,可以说MyISAM的数据存储方式是非常简单的。

MyISAM的二级索引的叶子节点同样保存了指向数据行的指针。因此本质上,MyISAM的主键索引和普通的二级索引(或者叫辅助索引)没有太大的区别。从上图中也可以看出。

什么是二级索引?

二级索引也叫辅助索引,是除主键索引以外的其他类型的索引。

InnoDB存储引擎,相对于MyISAM就要复杂许多。

首先,它以聚簇索引的形式来组织数据,其次作为聚簇索引的主键索引与二级索引也是有许多不同点:

InnoDB的聚簇索引就是主键索引,其叶子节点包含:主键的列值、事务ID、回滚指针、以及所有数据列

可以说,InnoDB整个表的逻辑结构就是通过主键的聚簇索引方式来存储的,在InnoDB中,聚簇索引就是表

所谓“聚簇”,意思就是数据与主键存储在一起。

另外,如果InnoDB的主键是一个列前缀索引,InnoDB还是会包含完整的主键列和剩下的其他列。这里的列前缀,我的理解是主键列并不是完整的作为索引列,而是“前缀”作为索引列。比如,主键列值是123456,那么这里的列前缀可以是123,即仅取主键列的前缀作为索引。

InnoDB的二级索引与MyISAM的二级索引有所不同,它不是类似于MyISAM那样在叶子节点中保存“行指针”,而是保存主键值,以此来作为“指针”。这是因为当出现行移动或数据页分裂时,可以避免对二级索引的维护操作。但这样的代价可能是会让二级索引占用更多的空间。

对于非叶子节点,它包含了索引列和一个指向下级节点的指针,这对所有的 B树索引都适用。

四、InnoDB为什么更推荐顺序递增id?

InnoDB更推荐使用自增id作为聚簇索引的主键。

我们知道,B树索引是按照索引列递增的顺序进行存储的,InnoDB的主键索引也不例外。

在向InnoDB插入数据时,自增的 id 可以更快速地直接在数据末尾追加。MySQL数据的存储以页为单位,当页被插满(达到页的最大填充因子,默认15/16),下一条记录就会写入新的页

而如果使用随机值,如UUID作为主键,因为新行的UUID不一定比之前插入的记录大,所以InnoDB无法简单的把新行插入到索引的最后,而是需要为新行寻找合适的位置通常是已有数据的中间位置。那么之前已经写满的,并且已经刷到磁盘上的页可能会被重新读取。这会增加很多额外工作,并会导致数据分布不够优化。

随机主键的缺点如下
1、写入的目标页可能已经刷到磁盘上,并且从缓存中移除,或者还没有被加载到缓存中,就必须要先从磁盘中读取目标页,导致大量的随机IO

2、因为写入是随机的,InnoDB不得不频繁的做页分裂操作,以便为新的行分配空间。页分裂会移动大量的数据,一次插入最少需要修改三个页面而不是一个。

3、由于频繁的页分裂,页会变得稀疏并被不规则地填充,所以最终数据会有碎片。因此可能还需要做一次OPTIMIZE TABLE 来重建表并优化页的填充。

什么是 OPTIMIZE TABLE? 

语法:OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name

简单的说,由于大量修改数据,如删除、移动等,造成的存储空间利用不均,导致的数据碎片。那么就可以使用OPTIMIZE TABLE 来优化数据表,从而更好的利用未使用的空间,整理数据文件的碎片

一般情况下,根本不需要运行 OPTIMIZE TABLE,即使对可变长度的行进行了大量的更新,也不需要频繁运行,每周一次或每月一次即可。只对MyISAM、BDB、InnoDB表有效。OPTIMIZE TABLE时,MySQL会锁表。

另外,顺序主键也不一定是完全无害的,在高并发场景,顺序插入可能会造成明显的争用,主键的上界会成为"热点",这可能会使并发插入导致间隙锁竞争。

还有另一个热点可能是AUTO_INCREMENT锁机制。这些问题可能需要重新设计表或应用,或者更改 innodb_autoinc_lock_mode设置。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章