I have experience with MS SQL server where it is possible and useful to update statistic and rebuild indexes. I can't find such option in MySQL innoDB, is there such option? If not, how MySQL database create an execution plan? Does the MySQL update indexes and statistic with every UPDATE and INSERT?
7 Answers
This is done with
ANALYZE TABLE table_name;
Read more about it 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.

- 50,732
- 33
- 89
- 96
-
4For MyISAM, it involves a full table scan; potentially slow. For InnoDB, it is a few quick probes. – Rick James Oct 24 '16 at 18:00
-
2Analyze tabe will not rebuild an index. If the index is faulty it needs a replacement I think. – John Jan 25 '19 at 19:27
-
3No idea how this can be the accepted answer. It is NOT rebuilding indexes and mysql is known to problems that degenerate index performance over time. sadly OPTIMIZE table is often no solution due to it's low performance – John Jan 17 '20 at 09:34
-
1@fancyPants You are wrong. Your answer says "ANALYZE TABLE" is the solution to rebuild indexes. That's factually not correct, it should not be the answer. OPTIMIZE TABLE on innodb does a full re-creation which includes a compact variant of the indexes and it can improve index performance in some border cases by a couple thousand times. Speaking of mysql 8.0.16, things might change over time. – John Jan 17 '20 at 11:36
-
`ALTER TABLE t1 ENGINE = InnoDB;` see https://dev.mysql.com/doc/refman/8.0/en/rebuilding-tables.html – conor Nov 29 '21 at 22:39
Why? One almost never needs to update the statistics. Rebuilding an index is even more rarely needed.
OPTIMIZE TABLE tbl;
will rebuild the indexes and do ANALYZE
; it takes time.
ANALYZE TABLE tbl;
is fast for InnoDB to rebuild the stats. With 5.6.6 it is even less needed.
(Note: The above applies to regular INDEXes
; InnoDB's FULLTEXT
does require periodic rebuilding.)

- 135,179
- 13
- 127
- 222
-
12I have to disagree with this answer. When going through an old table, of some 300k rows, I updated a couple of columns that was in an index and the index still contained the old values from before the update. I dropped the index and recreated it and then it worked fine. MySQL 5.7.10 – Adergaard May 12 '16 at 07:19
-
3@Adergaard - How did you 'know' that the index still contained the old values? (This may lead to a bug report.) – Rick James May 24 '16 at 00:05
-
1Like @Adergaard, I have to disagree, too. In my case a query that uses some fulltext index was very slow (looked like full index search was used). Performance improved only after dropping and recreating the index. MySQL 5.6.31. I wish there was some kind of rebuild only for indices, like in Oracle: `ALTER INDEX ... REBUILD;`. – Franc Drobnič Oct 24 '16 at 13:01
-
1Keep in mind that MyISAM is more prone to index corruption than InnoDB. Perhaps the two who disagreed with me were using MyISAM? – Rick James Jan 04 '17 at 18:46
-
3(I'm modifying my stand.) InnoDB's `FULLTEXT` seems to be the only index in MySQL where a rebuild _may_ provide some improvement. – Rick James Feb 20 '17 at 16:42
-
4It might be needed rarely, because many databases have limited amounts of data and not that many changes, but if you have a very active large database with lots of inserts, updates and deletes, I have seen huge improvements in performance using nightly optimizes, up to a magnitude of factors greater than 10 in complex selects between before and after optimize. – fgwaller Jul 01 '18 at 13:53
-
I would like to hear the details -- What type of data, what type of queries improved, whether it was Innodb, etc. – Rick James Jul 01 '18 at 16:25
-
@fgwaller - 10x is about what to expect in the difference between all the data cached versus not. Perhaps it was not cached for your slow `SELECT`, and the `OPTIMIZE` left it cached for you fast one. – Rick James Feb 11 '19 at 16:52
-
1@RickJames - Good theory, but I strongly doubt the cache will survive the reboot that is usually executed after the nightly optimize... I think the main problem seems to be the way (and order in) that the data is inserted/updated. I did not tinker a lot with different index options to maybe stop the indexes from becoming too "lopsided" in the first place, but the risk is that our db servers have to respond in "realtime" during operation and from my understanding some of the options may trigger a releveling of the tree which can cause undue delays. So running optimize seems to be best for now. – fgwaller Feb 28 '19 at 22:04
-
1If the question is just about `FULLTEXT`, then so state in a new Question -- There are differences between `FULLTEXT` and `INDEX`. – Rick James Feb 09 '21 at 23:32
-
For buffer pool prefetch (a type of speculative execution), an occassional reorg may be prudent, especially for large amounts of data and especially for table scans not using an index, even nowadays. Of course if you are silly and initialise index fields to a constant and do not use a hash-like synthetic key you are going to get lumps and splitting issues. – mckenzm Dec 08 '21 at 05:45
To date (mysql 8.0.18) there is no suitable function inside mysql to re-create indexes.
Since mysql 8.0 myisam is slowly phasing into deprecated status, innodb is the current main storage engine.
In most practical cases innodb is the best choice and it's supposed to keep indexes working well.
In most practical cases innodb also does a good job, you do not need to recreate indexes. Almost always.
When it comes to large tables with hundreds of GB data amd rows and a lot of writing the situation changes, indexes can degrade in performance.
In my personal case I've seen performance drop from ~15 minutes for a count(*) using a secondary index to 4300 minutes after 2 months of writing to the table with linear time increase.
After recreating the index the performance goes back to 15 minutes.
To date we have two options to do that:
1) OPTIMIZE TABLE (or ALTER TABLE)
Innodb doesn't support optimization so in both cases the entire table will be read and re-created.
This means you need the storage for the temporary file and depending on the table a lot of time (I've cases where an optimize takes a week to complete).
This will compact the data and rebuild all indexes.
Despite not being officially recommended, I highly recommend the OPTIMIZE process on write-heavy tables up to 100GB in size.
2) ALTER TABLE DROP KEY -> ALTER TABLE ADD KEY
You manually drop the key by name, you manually create it again. In a production environment you'll want to create it first, then drop the old version.
The upside: this can be a lot faster than optimize. The downside: you need to manually create the syntax.
"SHOW CREATE TABLE" can be used to quickly see which indexes are available and how they are called.
Appendix:
1) To just update statistics you can use the already mentioned "ANALYZE TABLE".
2) If you experience performance degradation on write-heavy servers you might need to restart mysql. There are a couple of bugs in current mysql (8.0) that can cause significant slowdown without showing up in error log. Eventually those slowdowns lead to a server crash but it can take weeks or even months to build up to the crash, in this process the server gets slower and slower in responses.
3) If you wish to re-create a large table that takes weeks to complete or fails after hours due to internal data integrity problems you should do a CREATE TABLE LIKE, INSERT INTO SELECT *. then 'atomic RENAME' the tables.
4) If INSERT INTO SELECT * takes hours to days to complete on huge tables you can speed up the process by about 20-30 times using a multi-threaded approach. You "partition" the table into chunks and INSERT INTO SELECT * in parallel.

- 7,507
- 3
- 52
- 52
You can also use the provided CLI tool mysqlcheck
to run the optimizations. It's got a ton of switches but at its most basic you just pass in the database, username, and password.
Adding this to cron or the Windows Scheduler can make this an automated process. (MariaDB but basically the same thing.)

- 6,370
- 5
- 37
- 43
-
6To perform the optimization on all tables of a database: `mysqlcheck yourdatabase -p --optimize` – Gabriel Belingueres Mar 23 '18 at 15:11
For basic cleanup and re-analyzing you can run "OPTIMIZE TABLE ...", it will compact out the overhead in the indexes and run ANALYZE TABLE too, but it's not going to re-sort them and make them as small & efficient as they could be.
https://dev.mysql.com/doc/refman/8.0/en/optimize-table.html
However, if you want the indexes completely rebuilt for best performance, you can:
- drop / re-add indexes (obviously)
- dump / reload the table
- ALTER TABLE and "change" using the same storage engine
- REPAIR TABLE (only works for MyISAM, ARCHIVE, and CSV)
https://dev.mysql.com/doc/refman/8.0/en/rebuilding-tables.html
If you do an ALTER TABLE on a field (that is part of an index) and change its type, then it will also fully rebuild the related index(es).

- 83
- 1
- 6
use CLI,
mysqlcheck -u root -p --auto-repair --optimize --all-databases

- 661
- 5
- 17
-
`CLI`s commands are usually not the best solutions, because if you are running a database in a cloud environment like AWS or Google, you have no CLI/SSH at all – matiaslauriti Apr 08 '22 at 18:03
-
1
-
@JohnC you still need an environment set up with `mysql` CLI... if you don't have one, you will have to set up one... If you only have a connection to the DB, and you already have a working app to connect to the database, you can just run an SQL query in that app... – matiaslauriti Apr 20 '22 at 15:26
-
@matiaslauriti yeah, that's called your desktop. All of them have a console. Nothing special needed except to install MySQL client. – John C Apr 20 '22 at 15:38
-
@JohnC no sir, if I am working with any environment and the user does not have rights to install stuff but they already had an app that allows you to run SQL (not `mysql` CLI) then you cannot use this approach... I will not reply to any more comments as this is offtopic now... – matiaslauriti Apr 20 '22 at 18:28
-
1@matiaslauriti MySQL client doesn't require to be installed... You can simply download it. And anyone maintaining MySQL that needs to run a table optimize, would have MySQL client apart of their environment already. Running a optimize on a table is a DBA task, not a end user task. You are now doing nothing but making ridiculous scenarios for no reason. – John C Apr 20 '22 at 20:49
-
@JohnC your comment is a `happy path`... the reality is mostly all devs and other users maintain DBs... I am not going to continue this discussion... – matiaslauriti Apr 21 '22 at 00:26
-
@matiaslauriti oh, I'm sorry, I thought you weren't going to continue this discussion. To indulge in your scenario of working in a company that has workstations locked down. That's actually not reality, users do not maintain databases period. And being in such a environment, developers will have what they need to develop with installed, MySQL client being one of those tools. Also in such a locked down environment, developers would almost never be allowed access to production database. Requiring to go through a change management system which almost always has a DBA doing any DB work. – John C Apr 21 '22 at 00:39
-
1@matiaslauriti Even with all this made up scenario, it still doesn't change the fact that MySQL client is very well capable of running these type of tasks for cloud managed PAS databases. – John C Apr 21 '22 at 00:42
As indicated in MySQL manual, there are multiple ways of rebuilding tables. If you do not change the version of MySQL server and you want to support tables with various engines (MyISAM, InnoDB), then such stored procedure might come in handy:
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;
In order to call it, simply:
CALL rebuildTables('yourDbName');
Mind that this process might take a lot of time, especially for large tables.

- 973
- 1
- 8
- 25