【问题标题】:How can I rebuild indexes and update stats in MySQL innoDB?如何在 MySQL innoDB 中重建索引和更新统计信息?
【发布时间】:2015-07-15 02:25:09
【问题描述】:

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

【问题讨论】:

    标签: mysql innodb


    【解决方案1】:

    这是用

    完成的
    ANALYZE TABLE table_name;
    

    阅读更多相关信息here

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

    【讨论】:

    • 完美,这正是我想要的。
    • 对于MyISAM,它涉及全表扫描;可能很慢。对于 InnoDB,这是一些快速探测。
    • Analyze tabe 不会重建索引。如果索引有问题,我认为需要更换。
    • 不知道这怎么可能是公认的答案。它不是重建索引,并且已知 mysql 会随着时间的推移降低索引性能的问题。遗憾的是 OPTIMIZE 表由于性能低而通常没有解决方案
    • @fancyPants 你错了。您的回答说“分析表”是重建索引的解决方案。这实际上是不正确的,它不应该是答案。 innodb 上的 OPTIMIZE TABLE 进行了完整的重新创建,其中包括索引的紧凑变体,它可以在某些边界情况下将索引性能提高几千倍。说到 mysql 8.0.16,事情可能会随着时间而改变。
    【解决方案2】:

    为什么?几乎从不需要更新统计数据。很少需要重建索引。

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

    ANALYZE TABLE tbl; 对于 InnoDB 来说重建统计数据的速度很快。在 5.6.6 中,它的需求就更少了。

    【讨论】:

    • 我不同意这个答案。在查看大约 300k 行的旧表时,我更新了索引中的几列,并且索引仍然包含更新前的旧值。我删除了索引并重新创建了它,然后它工作正常。 MySQL 5.7.10
    • @Adergaard - 您如何“知道”索引仍包含旧值? (这可能会导致错误报告。)
    • 像@Adergaard 一样,我也不得不反对。在我的情况下,使用某些全文索引的查询非常慢(看起来像是使用了完整索引搜索)。只有在删除并重新创建索引后,性能才会有所提高。 MySQL 5.6.31。我希望仅对索引进行某种重建,例如在 Oracle 中:ALTER INDEX ... REBUILD;
    • (我正在修改我的立场。)InnoDB 的FULLTEXT 似乎是 MySQL 中唯一一个重建可能提供一些改进的索引。
    • 它可能很少需要,因为许多数据库的数据量有限且没有那么多更改,但是如果您有一个非常活跃的大型数据库,其中包含大量的插入、更新和删除,我已经看到了巨大的使用夜间优化可提高性能,在优化前后的复杂选择中最多可提高 10 倍以上的系数。
    【解决方案3】:

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

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

    【讨论】:

    • 对一个数据库的所有表进行优化:mysqlcheck yourdatabase -p --optimize
    【解决方案4】:

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

    当涉及到具有数百 GB 数据和行并且大量写入情况发生变化的大型表时,索引可能会降低性能。
    在我个人的情况下,我看到性能从使用二级索引的 count(*) 大约 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 倍。您将表“分区”成块并并行 INSERT INTO SELECT *。

    【讨论】:

      【解决方案5】:

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

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

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

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

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

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

      【讨论】:

        【解决方案6】:

        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');
        

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

        【讨论】:

          猜你喜欢
          • 2017-11-14
          • 1970-01-01
          • 1970-01-01
          • 2017-10-23
          • 2010-10-24
          • 1970-01-01
          • 2013-08-19
          • 1970-01-01
          • 2012-12-18
          相关资源
          最近更新 更多