如何在 MySQL innoDB 中重建索引和更新统计信息?

How can I rebuild indexes and update stats in MySQL innoDB?

提问人:Tomas Kubes 提问时间:5/5/2015 更新时间:6/1/2022 访问量:203494

问:

我有使用 MS SQL Server 的经验,其中更新统计信息重建索引是可能且有用的。我在MySQL innoDB中找不到这样的选项,有这样的选项吗?如果没有,MySQL数据库如何创建执行计划?MySQL是否在每次UPDATE和INSERT时更新索引和统计信息?

mysql innodb

评论


答:

97赞 fancyPants 5/5/2015 #1

这是通过

ANALYZE TABLE table_name;

在这里阅读更多关于它的信息。

ANALYZE TABLE 分析并存储表的键分布。在分析过程中,该表被 MyISAM、BDB 和 InnoDB 的读锁锁定。此语句适用于 MyISAM、BDB、InnoDB 和 NDB 表。

评论

4赞 Rick James 10/25/2016
对于MyISAM,它涉及全表扫描;可能很慢。对于 InnoDB,它是一些快速探测。
2赞 John 1/26/2019
analyze tabe 不会重新生成索引。如果索引有问题,我认为它需要更换。
3赞 John 1/17/2020
不知道这怎么会是公认的答案。它不是重建索引,并且已知 mysql 存在随着时间的推移会降低索引性能的问题。可悲的是,由于性能低下,OPTIMIZE表通常没有解决方案
1赞 John 1/17/2020
@fancyPants 你错了。您的答案是“ANALYZE TABLE”是重建索引的解决方案。这实际上是不正确的,它不应该是答案。innodb 上的 OPTIMIZE TABLE 进行了完全的重新创建,其中包括索引的紧凑变体,它可以在某些边界情况下将索引性能提高几千倍。说到mysql 8.0.16,情况可能会随着时间的推移而改变。
0赞 conor 11/30/2021
ALTER TABLE t1 ENGINE = InnoDB;查看 dev.mysql.com/doc/refman/8.0/en/rebuilding-tables.html
51赞 Rick James 5/6/2015 #2

为什么?人们几乎从不需要更新统计数据。重建索引的情况就更少了。

OPTIMIZE TABLE tbl;将重建索引并执行;这需要时间。ANALYZE

ANALYZE TABLE tbl;InnoDB 可以快速重建统计数据。在 5.6.6 中,它甚至更少需要。

(注:以上适用于常规;InnoDB确实需要定期重建。INDEXesFULLTEXT

评论

12赞 Adergaard 5/12/2016
我不得不不同意这个答案。在浏览一个大约 300k 行的旧表时,我更新了索引中的几列,索引仍然包含更新前的旧值。我删除了索引并重新创建了它,然后它工作正常。MySQL的5.7.10
3赞 Rick James 5/24/2016
@Adergaard - 您如何“知道”索引仍然包含旧值?(这可能会导致错误报告。
1赞 Franc Drobnič 10/24/2016
和@Adergaard一样,我也不得不不同意。就我而言,使用某些全文索引的查询非常慢(看起来像使用了全索引搜索)。只有在删除并重新创建索引后,性能才有所提高。MySQL的 5.6.31。我希望只对索引进行某种重建,就像在 Oracle 中一样:.ALTER INDEX ... REBUILD;
4赞 Rick James 2/21/2017
(我正在修改我的立场。InnoDB似乎是MySQL中唯一一个重建可能会提供一些改进的索引。FULLTEXT
4赞 fgwaller 7/1/2018
它可能很少需要,因为许多数据库的数据量有限,而且没有那么多更改,但是如果你有一个非常活跃的大型数据库,有很多插入、更新和删除,我已经看到使用夜间优化在性能上有巨大的改进,在优化前后的复杂选择中,因子的幅度大于 10。
10赞 bbrown 11/9/2017 #3

您还可以使用提供的 CLI 工具运行优化。它有很多开关,但最基本的是,你只需传入数据库、用户名和密码。mysqlcheck

将其添加到 cron 或 Windows 计划程序可以使此过程自动化。(MariaDB,但基本上是一样的。

评论

6赞 Gabriel Belingueres 3/23/2018
要对数据库的所有表执行优化,请执行以下操作:mysqlcheck yourdatabase -p --optimize
16赞 John 1/17/2020 #4

迄今为止(mysql 8.0.18),mysql中没有合适的函数来重新创建索引。
由于 mysql 8.0 myisam 正在慢慢进入弃用状态,因此 innodb 是当前的主要存储引擎。
在大多数实际情况下,innodb 是最佳选择,它应该保持索引正常工作。
在大多数实际情况下,innodb 也做得很好,你不需要重新创建索引。几乎总是如此。

当涉及到具有数百 GB 数据和行的大型表和大量写入情况发生变化时,索引的性能可能会下降。
就我个人而言,我看到性能从使用二级索引的计数 (*) 的 ~15 分钟下降到写入表的 2 个月后的 4300 分钟,时间线性增加。
重新创建索引后,性能将恢复到 15 分钟。

到目前为止,我们有两种选择:
1) OPTIMIZE TABLE(或 ALTER TABLE)
Innodb 不支持优化,因此在这两种情况下,整个表都会被读取并重新创建。
这意味着您需要存储临时文件,并且需要花费大量时间(我的情况是优化需要一周才能完成)。 这将压缩数据并重新生成所有索引。
尽管没有正式推荐,但我强烈建议在最大 100GB 的写入密集型表上使用 OPTIMIZE 过程。

2) ALTER TABLE DROP KEY -> ALTER TABLE ADD KEY
按名称手动删除密钥,然后再次手动创建它。在生产环境中,您需要先创建它,然后删除旧版本。
好处是:这可能比优化快得多。缺点:您需要手动创建语法。
“SHOW CREATE TABLE”可用于快速查看哪些索引可用以及它们是如何调用的。

附录:
1) 要更新统计数据,您可以使用已经提到的“分析表”。
2)如果您在写入密集型服务器上遇到性能下降,则可能需要重新启动mysql。当前 mysql (8.0) 中有几个错误可能会导致显着的减速,而不会显示在错误日志中。最终,这些减速会导致服务器崩溃,但可能需要数周甚至数月才能累积到崩溃,在此过程中,服务器的响应速度越来越慢。
3)如果您希望重新创建一个需要数周时间才能完成的大型表,或者由于内部数据完整性问题而在数小时后失败,则应执行CREATE TABLE,LIKE INSERT-INTO SELECT *。然后对表进行“原子重命名”。
4) 如果在大型表上完成 INSERT INTO SELECT * 需要数小时到数天才能完成,则可以使用多线程方法将该过程加快约 20-30 倍。您将表“分区”为块,并并行插入 INTO SELECT *。

3赞 Jason 4/23/2020 #5

对于基本的清理和重新分析,您可以运行“OPTIMIZE TABLE ...”,它将压缩索引中的开销并运行ANALYZE TABLE,但它不会重新排序并使它们尽可能小和高效。

https://dev.mysql.com/doc/refman/8.0/en/optimize-table.html

但是,如果希望完全重新生成索引以获得最佳性能,则可以:

  1. 删除/重新添加索引(显然)
  2. 转储/重新加载表
  3. ALTER TABLE 并使用相同的存储引擎进行“更改”
  4. REPAIR TABLE(仅适用于 MyISAM、ARCHIVE 和 CSV)

https://dev.mysql.com/doc/refman/8.0/en/rebuilding-tables.html

如果对字段(作为索引的一部分)执行 ALTER TABLE 并更改其类型,则它还将完全重建相关索引。

-1赞 MatFiz 12/10/2021 #6

MySQL手册中所述,有多种方法可以重建表。如果您不更改MySQL服务器的版本,并且希望支持具有各种引擎(MyISAM,InnoDB)的表,那么这样的存储过程可能会派上用场:

CREATE PROCEDURE rebuildTables(in dbName VARCHAR(100))
BEGIN
    -- flag marking cursor end 
    DECLARE done INT DEFAULT FALSE;
    DECLARE tableName VARCHAR(255) DEFAULT "";
    DECLARE tableEngine VARCHAR(100) DEFAULT "";

    -- declare cursor for table names and engine
    DEClARE curTables
        CURSOR FOR
        SELECT TABLE_NAME, ENGINE FROM information_schema.tables where table_schema = dbName and table_type = 'BASE TABLE';

    -- declare NOT FOUND handler
    DECLARE CONTINUE HANDLER
        FOR NOT FOUND SET done = TRUE;

    OPEN curTables;

    rebuildTables: LOOP
        FETCH curTables INTO tableName, tableEngine;
        SELECT concat('Processing ', tableName, ' engine ', tableEngine);
        IF done THEN
            LEAVE rebuildTables;
        END IF;
        -- rebuild table as adviced in https://dev.mysql.com/doc/refman/5.7/en/rebuilding-tables.html
        SET @query = CONCAT('ALTER TABLE ', tableName, ' ENGINE=', tableEngine);
        PREPARE stmt FROM @query;
        EXECUTE stmt;
        DEALLOCATE PREPARE stmt;
    END LOOP;
    CLOSE curTables;
END;

为了调用它,只需:

CALL rebuildTables('yourDbName');

请注意,此过程可能需要大量时间,尤其是对于大型表。

4赞 Jason 2/9/2022 #7

使用 CLI,

mysqlcheck -u root -p --auto-repair --optimize --all-databases

评论

0赞 matiaslauriti 4/9/2022
CLI的命令通常不是最好的解决方案,因为如果您在 AWS 或 Google 等云环境中运行数据库,则根本没有 CLI/SSH
2赞 John C 4/20/2022
@matiaslauriti 这就是参数的用途......--host
1赞 John C 4/20/2022
@matiaslauriti是的,这就是你的桌面。他们都有一个控制台。除了安装MySQL客户端外,没有什么特别需要的。
2赞 John C 4/21/2022
@matiaslauriti MySQL客户端不需要安装...您可以简单地下载它。任何维护MySQL的人,如果需要运行表优化,就会将MySQL客户端从他们的环境中分离出来。对表运行优化是一项 DBA 任务,而不是最终用户任务。你现在什么都不做,只是无缘无故地制造荒谬的场景。
2赞 John C 4/21/2022
@matiaslauriti 即使有所有这些虚构的场景,它仍然没有改变MySQL客户端能够很好地为云托管PAS数据库运行这些类型的任务的事实。