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

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

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

这是用

完成的
ANALYZE TABLE table_name;

了解更多信息 here

ANALYZE TABLE analyzes and stores the key distribution for a table. During the analysis, the table is locked with a read lock for MyISAM, BDB, and InnoDB. This statement works with MyISAM, BDB, InnoDB, and NDB tables.

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

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

ANALYZE TABLE tbl; InnoDB 重建统计数据的速度很快。有了 5.6.6,就更不需要了。

(注意:以上适用于常规 INDEXes;InnoDB 的 FULLTEXT 确实需要定期重建。)

您还可以使用提供的 CLI 工具 mysqlcheck 来 运行 优化。它有一个 ton of switches,但在最基本的情况下,您只需传入数据库、用户名和密码。

将此添加到 cron 或 Windows 调度程序可以使其成为 automated process。 (MariaDB 但基本相同。)

迄今为止 (mysql 8.0.18) mysql 中没有 suitable 函数来重新创建索引。
由于 mysql 8.0 myisam 正逐渐进入弃用状态,innodb 是当前的主要存储引擎。
在大多数实际情况下,innodb 是最佳选择,它应该使索引保持良好运行。
在大多数实际情况下,innodb 也做得很好,您不需要重新创建索引。几乎总是。

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

到目前为止,我们有两种选择:
1) 优化 TABLE(或更改 TABLE)
Innodb 不支持优化,所以在这两种情况下,整个 table 将被读取并重新创建。
这意味着您需要临时文件的存储空间,并且取决于 table 很多时间(我遇到过优化需要一周才能完成的情况)。 这将压缩数据并重建所有索引。
尽管没有被官方推荐,但我强烈推荐在大小高达 100GB 的重写 table 上使用 OPTIMIZE 进程。

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

附录:
1) 要更新统计数据,您可以使用已经提到的 "ANALYZE TABLE".
2) 如果您在写入繁重的服务器上遇到性能下降,您可能需要重新启动 mysql。当前 mysql (8.0) 中存在一些错误,这些错误可能会导致显着的速度下降而不会出现在错误日志中。最终,这些减速会导致服务器崩溃,但可能需要数周甚至数月的时间才能累积到崩溃,在此过程中,服务器的响应速度会越来越慢。
3) 如果你想重新创建一个大的 table,它需要数周才能完成,或者由于内部数据完整性问题在数小时后失败,你应该执行 CREATE TABLE LIKE, INSERT INTO SELECT *。然后 'atomic RENAME' tables.
4) 如果 INSERT INTO SELECT * 需要数小时到数天才能在巨大的 tables 上完成,您可以使用多线程方法将过程加快约 20-30 倍。您 "partition" 将 table 分成块并并行插入 SELECT *。

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

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

但是,如果您希望完全重建索​​引以获得最佳性能,您可以:

  1. 删除/重新添加索引(显然)
  2. 转储/重新加载 table
  3. ALTER TABLE 和 "change" 使用相同的存储引擎
  4. REPAIR TABLE(仅适用于 MyISAM、ARCHIVE 和 CSV)

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

如果您对字段(索引的一部分)执行 ALTER TABLE 并更改其类型,那么它也会完全重建相关索引。

MySQL manual所示,有多种重建表的方法。如果你不更改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');

请注意,此过程可能会花费很多时间,尤其是对于大型表。

使用 CLI,

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