【发布时间】:2016-11-06 21:58:51
【问题描述】:
在 InnoDB 的 B+ 树实现中如何处理重复键的索引。
例如,如果有一个有 100 万行的表,其中有一列的基数为 10。如果我们在该列上创建索引,生成的 B+ 树会是什么样子?
它是否只有 10 个键,每个键的值是属于该键的主键列表(如果是,采用什么结构?链表?)还是有 1M 键(如果是,则 B+树必须以不同的方式处理)?
【问题讨论】:
在 InnoDB 的 B+ 树实现中如何处理重复键的索引。
例如,如果有一个有 100 万行的表,其中有一列的基数为 10。如果我们在该列上创建索引,生成的 B+ 树会是什么样子?
它是否只有 10 个键,每个键的值是属于该键的主键列表(如果是,采用什么结构?链表?)还是有 1M 键(如果是,则 B+树必须以不同的方式处理)?
【问题讨论】:
在某种意义上,InnoDB BTree 没有重复项。这是因为PRIMARY KEY 的列被附加到为辅助键指定的列中。这会导致一个完全有序的列表。
当您通过辅助键(或键的初始部分)查找时,查询将向下钻取 BTree 以找到与您提供的索引匹配的第一行,然后向前扫描以获取任何其他行。要获取其余列,它需要 PRIMARY KEY 列进行第二次 BTree 查找。
优化器很少使用“低基数”的索引。例如,不应为 yes/no 或 true/false 或 male/female 列编制索引。优化器会发现简单地扫描表比在索引和(通过 PK 列)主 BTree 之间来回跳转更快。
何时使用指数与下注的截止值约为 20%,具体取决于月相。
【讨论】:
您提出的案例对于 B+ 树来说是一个糟糕的案例。基数 10 表示 only 10 of the 1 million values are unique。实际上,它不仅对 B+ 树不利,而且通常是一个糟糕的索引。根据该指数,您平均会留下大约 1 个子集。 100,000 个值,您必须仔细查看或使用其他值进一步过滤。
关于生成树的结构,这里有一些注意事项:
- 如果叶节点已满,插入可能需要拆分
- 有时叶节点的分裂需要下一个更高节点的分裂
- 在最坏的情况下,拆分可能会一直级联到根节点
https://www.percona.com/files/presentations/percona-live/london-2011/PLUK2011-b-
- 叶子节点作为双向链表链接在一起
- […]
- 可以扫描整个树而根本不访问更高的节点
https://www.percona.com/files/presentations/percona-live/london-2011/PLUK2011-b-
如果您使用或多或少都属于同一个等价类的键插入大量数据,我会期望一棵树,这不会有太大帮助。这 10 个键可能仅存在于根节点中,并且树中更深的所有数据都将未排序(因为没有任何东西可以对其进行排序)。
由于叶子是双链表,因此您基本上只剩下我在开头所写的内容:您必须遍历值的一大子集。关于给定的索引,这是可以预料的,并且 B+ 树在这种情况下可能会做得很好(一个列表可以用于遍历所有数据)。
实际上,这是一种更深层次的抽象:叶子是双链接的,但每个叶子中有多个值(数据或链接到 PK)。尽管如此,这些也都在一个列表中,所以如果你只是遍历所有内容并没有太大区别。
请注意,您还可以调查 MySQL 真正构建的内容。有一些工具可以检查构建的索引数据结构,例如
【讨论】:
InnoDB 将表存储在内部称为 PRIMARY 的 B+ 树索引中。索引的键是你的主键字段。
如果您定义二级索引,则会有额外的 B+ 树索引(在 .ibd 或 ibdata1 中),其中键是二级索引字段,值是主键。
B+ 树本身并不要求 key 是唯一的。 PRIMARY 和所有 UNIQUE 索引的唯一性在服务器级别强制执行。
这里有一些幻灯片,介绍了 InnoDB 如何组织索引并使用它们来访问数据。 http://www.slideshare.net/akuzminsky/efficient-indexes-in-mysql#downloads-panel
【讨论】: