【发布时间】:2015-07-15 02:25:09
【问题描述】:
我有使用 MS SQL 服务器的经验,这对 update statistic 和 rebuild indexes 来说是可能和有用的。我在 MySQL innoDB 中找不到这样的选项,有这样的选项吗?如果没有,MySQL数据库如何创建执行计划? MySQL 是否会在每次 UPDATE 和 INSERT 时更新索引和统计信息?
【问题讨论】:
我有使用 MS SQL 服务器的经验,这对 update statistic 和 rebuild indexes 来说是可能和有用的。我在 MySQL innoDB 中找不到这样的选项,有这样的选项吗?如果没有,MySQL数据库如何创建执行计划? MySQL 是否会在每次 UPDATE 和 INSERT 时更新索引和统计信息?
【问题讨论】:
这是用
完成的ANALYZE TABLE table_name;
阅读更多相关信息here。
ANALYZE TABLE 分析并存储表的键分布。在分析过程中,表被 MyISAM、BDB 和 InnoDB 的读锁锁定。此语句适用于 MyISAM、BDB、InnoDB 和 NDB 表。
【讨论】:
为什么?几乎从不需要更新统计数据。很少需要重建索引。
OPTIMIZE TABLE tbl; 将重建索引并执行ANALYZE;这需要时间。
ANALYZE TABLE tbl; 对于 InnoDB 来说重建统计数据的速度很快。在 5.6.6 中,它的需求就更少了。
【讨论】:
ALTER INDEX ... REBUILD;。
FULLTEXT 似乎是 MySQL 中唯一一个重建可能提供一些改进的索引。
您还可以使用提供的 CLI 工具 mysqlcheck 运行优化。它有一个ton of switches,但在最基本的情况下,您只需传入数据库、用户名和密码。
将此添加到 cron 或 Windows 调度程序可以使其成为 automated process。 (MariaDB 但基本相同。)
【讨论】:
mysqlcheck yourdatabase -p --optimize
迄今为止(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 *。
【讨论】:
对于基本的清理和重新分析,您可以运行“OPTIMIZE TABLE ...”,它会压缩索引中的开销并运行 ANALYZE TABLE,但它不会重新排序并使它们变小尽可能高效。
https://dev.mysql.com/doc/refman/8.0/en/optimize-table.html
但是,如果您希望完全重建索引以获得最佳性能,您可以:
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');
请注意,此过程可能需要很长时间,尤其是对于大型表。
【讨论】: