MySQL 索引维护

Nic*_*las 12 mysql fragmentation index-maintenance

我对如何在 MySQL 中维护索引以防止碎片化并以某种方式优化某些查询的执行进行了大量研究。

我熟悉计算表可用的最大空间与数据和索引使用的空间之间的比率的公式。

但是,我的主要问题仍未得到解答。也许这是因为我熟悉SQL Server中的索引维护,并且我倾向于认为在MySQL中它应该在某种程度上相似。

在 SQL Server 中,您可以有多个索引,并且每个索引都可以具有不同级别的碎片。然后,您可以选择一个并在该特定索引中执行“重组”或“重建”操作,而不会影响其余部分。

据我所知,没有这样的“表碎片”,并且 SQL Server 不提供任何工具来修复“表碎片”。它提供的是检查索引碎片的工具(理解为索引使用的页面数量与该页面的完整度和连续性之间的比率),以及内部和外部碎片。

所有这些都很容易理解,至少对我来说是这样。

现在,轮到在 MySQL 中维护索引时,只存在“表碎片”的概念,如上所述。

MySQL 中的一个表可以有多个索引,但是当我用那个著名的公式检查“碎片率”时,我没有看到每个索引的碎片,而是整个表。

当我想优化 MySQL 中的索引时,我不会选择要操作的特定索引(如在 SQL Server 中)。相反,我在整个表中执行“优化”操作,这可能会影响所有索引。

当在 MySQL 中优化表时,数据 + 索引使用的空间与整体空间之间的比率减少,这表明硬盘驱动器中进行了某种物理重组,这转化为物理空间的减少。但是,索引碎片不仅与物理空间有关,还与由于插入和更新而随时间发生变化的树结构有关。

最后,我在 InnoDB/MySQL 中得到了一张表。该表有 300 万条记录、105 列和 55 个索引。它是 1.5GB,不包括索引,即 2.1GB。

该表每天都会被访问数千次以进行更新、插入(我们实际上并没有删除记录)。

该表已创建多年,我确信没有人在维护任何索引。

我期待在那里找到一个巨大的碎片,但是当我按照规定执行碎片计算时

free_space / (data_length + index_length)
Run Code Online (Sandbox Code Playgroud)

事实证明,我只有 0.2% 的碎片。恕我直言,这是非常不现实的。

所以最大的问题是:

  1. 如何检查 MySQL 中特定索引的碎片,而不是整个表
  2. OPTIMIZE TABLE 是否真的像在 SQL Server 中一样修复了索引的内部/外部碎片?
  3. 当我在 MySQL 中优化一个表时,它是否真的重建了表上的所有索引?
  4. 认为减少索引的物理空间(而不重建树本身)实际上可以转化为更好的性能是否现实?

Ric*_*mes 6

索引碎片被高估了。不用担心。

InnoDB 将两个相邻的、有点空的块合并在一起作为自然处理。

BTree 上的随机操作会使其自然地趋向于平均 69% 的满度。当然,这不是 100%,但是“修复”它的开销是不值得的。

SHOW TABLE STATUS 给你一些指标,但它们有缺陷——“Data_free”包括某些“免费”空间,但不包括其他“免费”空间。

每个块都有未使用的空间;空闲的 16KB 块;空闲“范围”(nMB 块);MVCC 行等待收割;非叶子节点有自己的分片;等等。

Percona 和 Oracle 有不同的方式来查看索引的大小(块数)。由于“免费”的定义有限,我发现它们都没有用。似乎块(每个 16KB)以块(几 MB)的形式分配,从而导致人们相信存在各种碎片。实际上,它通常只是这些多 MB 块中的大部分。并且OPTIMIZE TABLE不一定收回任何空间。

如果 SQL Server 使用 BTrees,那么说“没有碎片”就是在撒谎。想想“块分裂”会发生什么。或者想想不断进行碎片整理的开销。无论哪种方式,你都会输。

进一步注意,表和索引本质上是相同的结构:

  • B+Tree,基于一些索引
  • “数据”基于 PRIMARY KEY;每个二级索引都是一个基于其索引的 B+树。
  • “数据”的叶节点包含表的所有列。
  • 二级索引的叶节点包含该二级索引的列,以及 PRIMARY KEY 的列。

如果您有innodb_file_per_table = ON,您可以通过查看.ibd文件大小清楚地看到 OPTIMIZE TABLE 之后的收缩(如果有)。对于OFF,信息隐藏在 中ibdata1,但SHOW TABLE STATUS可能相当准确,因为所有“空闲”空间都属于每个表。好吧,除了预先分配的块。

您可能会注意到,新优化的 file-per-table 表正好有 4M、5M、6M 或 7M 的 Data_free。同样,这是预分配,并且未能向您提供详细信息。

我在 InnoDB 工作了十多年;我曾处理过数以千计的大大小小的桌子。我说只有千分之一真正需要OPTIMIZE TABLE。在其他桌子上使用它是一种浪费。

105 列很多,但也许不会太多。

一张表上有 55 个索引吗?那很不好。即每 55 个更新INSERT。让我们进一步讨论。请记住,INDEX(a)如果您也有INDEX(a,b). 并且INDEX(flag)由于基数低而无用。(但INDEX(flag, foo)可能有用。)

Q1:没有好的方法可以检查数据或二级索引中的所有形式的碎片。

Q2、Q3:OPTIMIZE TABLE通过CREATEing一个新表和INSERTing所有行重建表,然后RENAMEingDROPping。按 PK 顺序重新插入数据可确保对数据进行良好的碎片整理。索引是另一回事。

Q4:可以 DROPreCREATE每个索引一起清理。但这是一个极其缓慢的过程。5.6 有一些加速,但我不知道它们是否有助于碎片整理。

也有可能ALTER TABLE ... DISABLE KEYS,然后ENABLE他们。这可以一次更有效地重建所有二级索引。


小智 1

如何检查 MySQL 中特定索引的碎片,而不是整个表

经过。

OPTIMIZE TABLE 是否真的像 SQL Server 中那样修复索引的内部/外部碎片?

它完全重建表及其索引。

当我在 MySQL 中优化表时,它实际上会重建表上的所有索引吗?

这是同样的问题,同样的答案。

认为减少索引的物理空间(不重建树本身)实际上会转化为更好的性能是否现实?

认为可以在不重建树的情况下减少空间是不现实的。他们一起去。