高性能索引优化策略(四):聚集索引和非聚集索引数据结构及优劣对比

聚集索引并不是一个单独的索引类型,实际上是一种数据存储的方式。聚集索引的实现细节各有不同,在InnoDB引擎中,聚集索引实际上是将索引和数据行使用同样的结构存储在一个二叉树中。

如果数据表中有聚集索引,则数据行实际上是存在所以的叶子节点。“聚集”的形式实际是指相邻键值的数据行是邻近存储的。因为一行数据不能存储在两个叶子节点上,因此在数据表中只能有一个聚集索引。由于是存储引擎负责索引的实现,因此并不是全部的存储引擎都支持聚集索引。在这里我们只讨论InnoDB,但支持聚集索引的存储引擎实现聚集索引的原理都大同小异。

下图展示了数据记录在聚集索引的存储布局。注意,叶子节点包含了完整的数据行,而其他节点仅仅只有索引。在这个图中,索引列使用的是整数。


聚集索引存储

有些数据库服务器允许我们选择对哪个索引进行聚集,但MySQL的任意内置的存储引擎都不支持这么做。InnoDB使用主键对数据进行聚集,这意味着上图的索引列实际上是主键列。

如果数据表没有定义主键,InnoDB会选择使用唯一的非空列(Not Null)索引替代。如果没有这样的索引,InnoDB会定义一个隐藏的主键去完成数据聚集(因此,数据表最好自己定义主键)。InnoDB的只能在一个数据页中进行数据聚集,因此即便是临近的索引值的数据存储页也可能间隔很远。

一个聚集主键能够提高性能,但同样也可能导致严重的性能问题。因此,你应当谨慎考虑聚集的使用,尤其是当你将一个数据表的存储引擎从InnoDB改为其他引擎时。

聚集索引具有如下的优势:

  • 可以将相关联的数据行保持邻近存储。例如,邮箱应用中,你可以使用user_id进行聚集。这种情况下,你可以从磁盘的很少的分页中获取用户的全部消息。而如果不使用聚集索引,每条消息都可能需要单独占用磁盘I/O。
  • 数据访问很快:聚集索引将索引和数据同时存储在二叉树中,因此从聚集索引中获取数据行比起非聚集索引的查询来说快很多。
  • 使用了覆盖索引的查询可以利用主键中包含在叶子节点的数据值。

如果你在设计数据表和查询时充分利用这些好处,将能显著提升性能。然而,聚集索引也有缺点:

  • 聚集能对受限于I/O负载的情况很大改善,但如果数据是在内存而与磁盘无关,那聚集实际上帮不上什么忙。
  • 插入速度严重依赖插入次序。按照主键顺序插入数据是InnoDB表中最快的方式,如果加载数据不是按照主键次序,那在完成大量数据的加载后,最好是使用优化表(OPTIMIZE TABLE)功能重新组织一下数据表。
  • 更新聚集索引列的代价很高,因为这会强制InnoDB去移动更新的数据行到一个新的位置。
  • 当新的数据行插入时或行的主键更新引起数据行移动,数据表已经构建的聚集索引会分页(page split)。分页情况发生在新的数据行需要移入一个放满数据的存储页时。存储引擎必须将存储页分成两部分去存储新的行,这会导致数据表占用更多的磁盘空间。
  • 聚集表在全表扫描时可能会更慢,尤其是数据行密集度不高或者是因为分页导致存储不连续。
  • 非聚集索引(Secondary Index)会比你预期的存储要大,这是因为叶子节点包含了主键列引用的数据行。
  • 非聚集索引访问时需要两次索引查找而不是一次。这个可能感觉有点费解,其原因在于非聚集索引存储的是数据行指针。记住,叶子节点并并不是存储引用行的实际物理位置,而是该行的主键值。这意味着,使用非聚集索引查询数据行时,存储引擎首先通过非聚集索引找到其叶子节点,然后通过叶子节点的主键再找到数据行的值。这相当于进行了两次二叉树的查找,在InnoDB中,自适应的哈希索引可以减少这种概率。

InnoDB和MyISAM数据布局比较

聚集和非聚集数据布局,以及主键和非聚集索引的差别可能让人困惑和奇怪。我们可以比较一下InnoDB和MyISAM对下面数据表的存储布局来深入了解一下。

CREATE TABLE layout_test (
  col1 int NOT NULL,
  col2 int NOT NULL,
  PRIMARY KEY(col1),
  KEY(col2)
);

假设这个表产生了主键1到10000的数据,采用的是随机顺序插入的,然后在通过OPTIMIZE TABLE进行了优化。换言之,数据在磁盘上是有序排列的,但行可能是随机顺序的。数据列col2使用1-100的随机值填充,因此存在很多的重复值。

MyISAM的数据布局更简单些,MyISAM按照插入的顺序在磁盘存储数据,如下图所示。我们以0开始展示了数据行编号,由于每一行的大小是固定的,MyISAM可以从表最开始的地方根据所需要的字节数来找到任意行(MyISAM内部并不总是使用行号,而是根据行是否固定大小或可变大小使用不同的策略)。


数据次序

这种结构使其很容易构建索引,下图绘制了一个数据系列,这个图中,物理的细节(例如存储页)被抽象隐藏,在索引中只有节点。每个索引的叶子节点可以简单地包含对应的行号,在下图是其主键。在这里隐藏了一些细节,例如在前一个节点后有少个内部的二叉树节点,但这对于理解非聚集存储引擎基础的数据布局来说并不重要。


MyISAM聚集索引结构

那对于col2列的索引怎么样。实际上,它和其他索引一样。


MyISAM非聚集索引结构

事实上,在MyISAM中,主键和其他索引并没有结构上的区别。主键只是一个简单的唯一、不为空的索引,仅仅是名字命名为主键而已。

InnoDB由于聚集索引的组织方式,存储同样数据的结构十分不同,如下图所示。


InnDB聚集索引结构

初看这张图,感觉似乎和MyISAM的并无太大不同,但是仔细再看一遍,实际上这个图展示了整张数据表,而不只是索引。由于聚集索引在InnoDB中已经是整张表了,因此这里没有像在MyISAM中的独立的行存储。

InnoDB的聚集索引每个叶子节点都包含主键值、事务ID、回滚指针以便进行事务和MVCC(Multi Version Cocurrent Control, 并发多版本控制),以及剩下的其他列(示例数据表就是col2)。如果主键作用在一个列的前缀上,InnoDB会在其他列中包含主键列的完整值。

同样,与MyISAM相比,非聚集索引和聚集索引有很大不同。相比于存储行指针,InnoDB非聚集索引的叶子节点存储的是主键值,由主键再指向数据。这个策略在行移动或数据分页需要维护非聚集索引时,减少了很多工作。使用行主键值作为指针意味着索引更大,但也意味着InnoDB可以移动行而不需要去更新非聚集索引的指针。下图展示了非聚集索引的数据布局,可以看到非聚集索引实际上存储了主键的值。


InnoDB非聚集索引结构

这些图展示了二叉树的叶子节点,但是我们隐藏了非叶子节点的细节。InnoDB中非叶子节点的二叉树的每一个节点都包含索引列,并另外附加了下一个层级的节点的指针(有可能是非叶子节点也可能是叶子节点)。这个对聚集索引和非聚集索引都一样。下图展示了InnoDB和MyISAM的索引的抽象结构对比,从中可以看出二者的不同之处。

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

推荐阅读更多精彩内容