在 MySQL/MariaDB 中,索引的性能会随着索引变得越来越大而降低吗?

Nun*_*uno 9 mysql innodb mariadb index partitioning

PARTITION我目前正在探索, 对于我的特定用例的使用。
\n我使用 InnoDB,每个表一个文件。玛丽亚数据库 10.8。

\n

我正在阅读 Rick 的PARTITION Maintenance in MySQL网页。

\n

我想强调这一点:

\n
\n

WHERE X = 1234-- 这使得“分区修剪”仅在该一个分区中查找。但这并不比INDEX(x)在非分区表上好。无论如何,您可能都需要该索引;在第一次“修剪”到所需的分区后,您仍然需要索引。没有更快。
\n一个常见的谬误:“分区将使我的查询运行得更快”。不会的。思考“点查询”需要什么。没有分区,但有适当的索引,有一个 BTree(索引)可以向下钻取以找到所需的行。对于 10 亿行,这可能是 5 层深。通过分区,首先选择并“打开”分区,然后向下钻取较小的 BTree(例如 4 层)。嗯,较浅 BTree 的节省被必须打开分区所消耗。同样,如果您查看需要访问的磁盘块,以及其中哪些块可能会被缓存,您会得出结论:可能有大约相同数量的磁盘命中。由于磁盘命中是查询中的主要成本,因此分区不会获得任何性能(至少对于这种典型情况)。二维情况(如下)给出了该讨论的主要矛盾。

\n
\n

我完全明白这意味着什么,但我有一个问题:

\n

在 MySQL/MariaDB 中,索引的性能会随着索引变得越来越大而降低吗?

\n

对于 10 亿行或 1000 亿行,就性能而言,好的索引总是优于分区吗?

\n

--

\n

还有一点最接近我想要受益的:

\n
\n

用例#3——热点。这个解释起来有点复杂。给定以下组合:
\n\xe2\x9a\x88 表的索引太大而无法缓存,但一个分区的索引是可缓存的,并且
\n\xe2\x9a\x88 索引是随机访问的,并且
\n\xe2\x9a\x88 由于更新索引,数据摄取通常会受到 I/O 限制
\n分区可以将所有索引保持在 RAM 中“热”,从而避免大量 I/O。

\n

案例 3 的重大胜利:改进缓存以减少 I/O,从而加快操作速度。

\n
\n

“索引缓存”对 InnoDB 也有效吗?我的理解CACHE INDEX只适用于MyISAM。
\n或者这与它在 InnoDB 缓冲池中有关吗?

\n

关于减少 I/O,这是否适用于 NVMe 服务器?我的%iowait是 0.00,而我的应用程序是写入密集型的。

\n

Bil*_*win 10

对于 10 亿行或 1000 亿行,就性能而言,好的索引总是优于分区吗?

对此我可以说几件事。

  • 我们不能进行这种概括,因为这取决于查询。一般来说,每种优化都对正确类型的查询有很大帮助,但会牺牲其他类型的查询。因此,在选择优化方法之前,您必须非常具体地了解要优化的查询。

  • 这不是一个非此即彼的选择。您可以对表进行分区,还可以定义索引,以便在给定分区中优化搜索。

  • 我不认为你有 1000 亿行。如果你这样做了,你就不会在 Stack Exchange 上问这个问题,你会分配给你的全职数据库架构师团队优化它的任务。毫无疑问,他们会带着使用许多服务器的设计回来。在单个表中存储 1000 亿行是不切实际的。你会如何备份它?您将如何添加列?

InnoDB 使用 B 树索引(也是全文索引和空间索引,但在本讨论中我们假设索引的默认类型)。

B 树索引的插入和搜索复杂度为O(log 2 n ),其中n是数据结构中的条目数。因此,随着索引变大,插入或搜索的成本会变得更高。

索引搜索所需的 I/O 是B 树深度的函数。即要遍历多少层非终端节点才能到达叶子节点。深度取决于有多少索引条目,还取决于给定条目的数据类型有多大,因为InnoDB页面大小是固定的,所以一个页面上只能容纳这么多索引节点。请参阅: https ://www.percona.com/blog/2009/04/28/the_depth_of_a_b_tree/

通过将索引页的子集保留在 RAM 和 InnoDB 缓冲池中,可以降低 I/O 成本。但是,如果索引增长得比 RAM 大得多,则没有足够的缓冲池来容纳整个索引,因此如果您在整个索引上随机搜索,InnoDB 可能会逐出您很快将再次需要的页面。当您需要这些页面时,它们会从存储中重新加载,但这可能会导致页面在 RAM 中换入和换出时产生额外的开销。

缓存索引仅适用于MyISAM。InnoDB 按需缓存页面,其中可能包括给定索引的子集。忘记任何将索引加载到缓存中的手动命令。老实说,我建议出于任何目的忘记 MyISAM。自 2000 年代以来,我还没有看到它得到适当的使用。

您询问了 NVMe 存储。NVMe 当然比旧的 SATA 接口更快,但它与 RAM 相比如何?这取决于您测量的内容,但就访问时间和吞吐量(MB/秒)而言,您可以信赖 RAM 比最新一代 NVMe 快数倍。此外,编写 InnoDB 代码时假设页面必须位于 RAM 中才能读取。将数据和索引页缓存在 RAM 中仍然是一个胜利。

我同意 Rick 的总体说法,即分区通常不会像您想象的那样对性能有帮助。它在正确的场景中很有用,但它并不是一个神奇的“一切都很快”的解决方案。对于所有其他类型的优化也是如此!

  • @JD 好吧,SQL Server 如此强大,真是太棒了。MySQL 不是。我支持的数据库有 1-50 亿行的表,在这种规模下,项目需要重构,以便它们可以扩展到多个数据库服务器,这是非常紧迫的。我同意备份时间是表大小的函数,表大小各不相同。但超过 500GB 的表开始变得难以使用。我们实际上实施了一个警报,敦促开发人员要么存档一些数据,要么拆分这些表。 (2认同)

Ric*_*mes 9

(除了比尔的评论......)

另一种分析性能的方法——“计算磁盘命中数”。

  • InnoDB 在其 buffer_pool 中缓存 16KB 块(数据和索引)。
  • 缓存是按需的并且大约是最近最少使用的。
  • 所有活动(读、写、查找等)都在 buffer_pool 中完成,而不是直接在磁盘上完成。
  • 一个简单的经验法则是,任何 InnoDB BTree 的扇出约为 100。(Percona 使用 128——本质上相同。)十亿行(数据或索引)将有 5 个级别。这意味着必须获取 5 个块(如果尚未缓存)才能执行简单的点查询。或者通过辅助键查找 10 个块。(6 和 12 代表 1000 亿行。)
  • 如果您要从该 BTree 中获取 101 个“连续”行,则需要 2 个叶节点(开始的那个加上下一个。) 另一条经验法则:所有非叶节点可能都被缓存。
  • 如果您的活动是随机的(如使用 UUID 或 PK 的哈希值)并且buffer_pool 无法容纳整个 BTree,则缓存几乎变得毫无用处。这导致每个点查询至少有 1 次磁盘命中。在这种情况下,“太大对性能来说很糟糕”。
  • 另一方面,如果您只接触“最近”的行(并且 PK 是 AUTO_INCRMENT 或基于时间的),则缓存 buffer_pool 在 100 次中有 99 次具有所需数据。也就是说,本质上没有降级,因为即使比 buffer_pool 大得多,表也会增长。

(在最后两句话中我做了很多“挥手”。)

我希望我已经为您提供了一些线索,帮助您自行判断您的桌子是否会随着其增长而受到影响。如果您想进一步讨论,请提供CREATE TABLE。当有人谈论十亿行表时,我喜欢缩小数据类型、重构架构、规范化、添加汇总表、考虑分片等。但我很少推荐分区。有时,我建议“保留汇总表,但扔掉事实表”。这消除了各种扩展和性能问题。


bob*_*lux 5

在 MySQL/MariaDB 中,索引的性能会随着索引变得越来越大而降低吗?

这取决于你所说的“性能”是什么意思......

如果您的意思是“根据索引键查找一行或一系列行”,那么答案将是“一点点”。正如其他人所解释的,只要索引的工作集保留在缓存中,“大”可能会比“小”慢一点,但这很可能会被物流、网络使用的其余查询时间淹没如果叶页没有被缓存,那么就会添加一个随机 IO,所以你必须询问你的 IO 系统这需要多长时间。

但是您询问索引与分区,因此在这种情况下,如果“分区”情况和“单表”情况下的数据相同,则分区上索引的总大小将与索引几乎相同在单人桌子上。在相同的查询负载下,没有理由认为其中一个会比另一个更好地缓存,因此两者之间可能没有区别。如果您只访问最新的行,那么这两种情况都会以同样的方式受益,因为只需要缓存索引的相应部分。

但是,如果您从全局角度考虑性能并添加诸如“删除超过 12 个月的所有行”之类的内容,并且在运行此归档操作时要删除 10 亿行,那么巨大的表和巨大的索引将成为一个极其糟糕的主意(TM )。如果它是日期索引,也许它是可以容忍的,因为删除将触及它的连续块。然而,如果它是一个相当随机的列上的索引,那么每个删除的行都会触发索引中某个地方的随机写入,整个地方,这将永远令人烦恼。

然而,如果您使用分区,“DROP PARTITION”几乎是瞬时的,因为在幕后,它只是删除相应的文件。除非要触发 ON DELETE 触发器,否则如果数据库知道我们要删除整个分区,则读取要删除的行是没有意义的。

如果没有对旧分区进行写入,如果备份工具可以利用不需要备份未更改的分区这一事实,则备份速度可能会快得多。

我正在编辑添加分区可以使查询更快的其他情况:

您没有足够的钱将整个表放在 SSD 上,因此您将很少访问的旧分区(和索引)放在笨重、缓慢旋转的 RAID 上,以及查看大部分数据的最新分区(和索引)。在一些非常快的 SSD 上的操作。这是一个很好的“现金与性能”优化,但您将不得不偶尔移动分区。也许您甚至可以只复制最近的分区,或者为每个服务器放置一个分区并并行运行它们(如果数据库支持的话),诸如此类。

此外,如果查询优化器搞砸了并决定进行全表扫描或类似的操作,也许如果您的表已分区并且查询对分区键有一个条件,则搞砸的大小可以限制为仅一个几个分区而不是整个表。