ChatGPT解决这个技术问题 Extra ChatGPT

How to shrink/purge ibdata1 file in MySQL

I am using MySQL in localhost as a "query tool" for performing statistics in R, that is, everytime I run a R script, I create a new database (A), create a new table (B), import the data into B, submit a query to get what I need, and then I drop B and drop A.

It's working fine for me, but I realize that the ibdata file size is increasing rapidly, I stored nothing in MySQL, but the ibdata1 file already exceeded 100 MB.

I am using more or less default MySQL setting for the setup, is there a way for I can automatically shrink/purge the ibdata1 file after a fixed period of time?


K
Kar.ma

That ibdata1 isn't shrinking is a particularly annoying feature of MySQL. The ibdata1 file can't actually be shrunk unless you delete all databases, remove the files and reload a dump.

But you can configure MySQL so that each table, including its indexes, is stored as a separate file. In that way ibdata1 will not grow as large. According to Bill Karwin's comment this is enabled by default as of version 5.6.6 of MySQL.

It was a while ago I did this. However, to setup your server to use separate files for each table you need to change my.cnf in order to enable this:

[mysqld]
innodb_file_per_table=1

https://dev.mysql.com/doc/refman/5.6/en/innodb-file-per-table-tablespaces.html

As you want to reclaim the space from ibdata1 you actually have to delete the file:

Do a mysqldump of all databases, procedures, triggers etc except the mysql and performance_schema databases Drop all databases except the above 2 databases Stop mysql Delete ibdata1 and ib_log files Start mysql Restore from dump

When you start MySQL in step 5 the ibdata1 and ib_log files will be recreated.

Now you're fit to go. When you create a new database for analysis, the tables will be located in separate ibd* files, not in ibdata1. As you usually drop the database soon after, the ibd* files will be deleted.

http://dev.mysql.com/doc/refman/5.1/en/drop-database.html

You have probably seen this:
http://bugs.mysql.com/bug.php?id=1341

By using the command ALTER TABLE <tablename> ENGINE=innodb or OPTIMIZE TABLE <tablename> one can extract data and index pages from ibdata1 to separate files. However, ibdata1 will not shrink unless you do the steps above.

Regarding the information_schema, that is not necessary nor possible to drop. It is in fact just a bunch of read-only views, not tables. And there are no files associated with the them, not even a database directory. The informations_schema is using the memory db-engine and is dropped and regenerated upon stop/restart of mysqld. See https://dev.mysql.com/doc/refman/5.7/en/information-schema.html.


@JordanMagnuson Don't bother to drop information_schema. It is in fact just a bunch of read-only views, not tables. And there are no files associated with the them. There isn't even a directory for the database. The informations_schema is using the memory db-engine and is dropped and regenerated upon stop/restart of mysqld. See dev.mysql.com/doc/refman/5.5/en/information-schema.html. Regarding performance_schema I haven't used that schema myself.
I don't know if this is a recent thing but once the innodb_file_per_table option is enabled you can simply run "ALTER TABLE ENGINE=InnoDB" (even if it's already InnoDB) and it will move the table in to its individual file. No need to drop databases and such.
+1 FWIW, MySQL 5.6 enables innodb_file_per_table by default.
Yes, ibdata1 is expected to be present along with the other files. The ibdata1 file will still hold metadata about tables, the undo log and buffers.
I've run out of space in my server because of the ibdata1 file, so I cannot even dump the databases. Would it be the same to just move the files at /var/lib/mysql (except "mysql", "ibdata1", "ib_logfile0" and "ib_logfile1") and then follow the steps? See stackoverflow.com/questions/2482491/…
K
Kar.ma

Adding to John P's answer,

For a linux system, steps 1-6 can be accomplished with these commands:

mysqldump -u [username] -p[root_password] [database_name] > dumpfilename.sql mysqladmin -u [username] -p[root_password] drop [database_name] sudo /etc/init.d/mysqld stop sudo rm /var/lib/mysql/ibdata1 sudo rm /var/lib/mysql/ib_logfile* sudo /etc/init.d/mysqld start mysqladmin -u [username] -p[root_password] create [database_name] mysql -u [username] -p[root_password] [database_name] < dumpfilename.sql

Warning: these instructions will cause you to lose other databases if you have other databases on this mysql instance. Make sure that steps 1,2 and 6,7 are modified to cover all databases you wish to keep.


You need to repeat 1,2, and 6 for every database that has InnoDB tables.
You need a couple more steps in between #5 and #6. You have to recreate the database and re-assign permissions. So from mysql client command promptcreate database database_name; and then grant all privileges on database_name.* to 'username'@'localhost' identified by 'password';
@fred I didn't need to grant privileges when doing this. Possibly because I recreated the database with the same name?
To type the password at a Password: prompt (which is a safer practice), just put -p without any actual password.
Now this causes the error InnoDB: File ./ibdata1: 'open' returned OS error 71. Cannot continue operation, so cannot restart the server!
C
Clément Moulin - SimpleRezo

When you delete innodb tables, MySQL does not free the space inside the ibdata file, that's why it keeps growing. These files hardly ever shrink.

How to shrink an existing ibdata file:

https://dev.mysql.com/doc/refman/5.6/en/innodb-system-tablespace.html#innodb-resize-system-tablespace

You can script this and schedule the script to run after a fixed period of time, but for the setup described above it seems that multiple tablespaces are an easier solution.

If you use the configuration option innodb_file_per_table, you create multiple tablespaces. That is, MySQL creates separate files for each table instead of one shared file. These separate files a stored in the directory of the database, and they are deleted when you delete this database. This should remove the need to shrink/purge ibdata files in your case.

More information about multiple tablespaces:

https://dev.mysql.com/doc/refman/5.6/en/innodb-file-per-table-tablespaces.html


first link broken, closest match I could find: dev.mysql.com/doc/refman/5.5/en/…
m
miken32

Quickly scripted the accepted answer's procedure in bash:

#!/usr/bin/env bash
dbs=$(mysql -BNe 'show databases' | grep -vE '^mysql$|^(performance|information)_schema$')
mysqldump --events --triggers --databases $dbs > alldatabases.sql && \
    echo "$dbs" | while read -r db; do
        mysqladmin drop "$db"
    done && \
    mysql -e 'SET GLOBAL innodb_fast_shutdown = 0' && \
    /etc/init.d/mysql stop && \
    rm -f /var/lib/mysql/ib{data1,_logfile*} && \
    /etc/init.d/mysql start && \
    mysql < alldatabases.sql

Save as purge_binlogs.sh and run as root.

Excludes mysql, information_schema, performance_schema (and binlog directory).

Assumes you have administrator credendials in /root/.my.cnf and that your database lives in default /var/lib/mysql directory.

You can also purge binary logs after running this script to regain more disk space with:

PURGE BINARY LOGS BEFORE CURRENT_TIMESTAMP;

Still not sure why, but today some of my InnoDB tables were corrupted during a similar process, so I wouldn't remove alldatabases.sql before double checking if all tables are healthy. As for some improvements: set innodb_fast_shutdown=0 before shutdown, set autocommit=0 before importing SQL file, execute COMMIT and set autocommit=1 after importing SQL file, use mysqlcheck --all-databases before deleting backup.
A
A.L

If you use the InnoDB storage engine for (some of) your MySQL tables, you’ve probably already came across a problem with its default configuration. As you may have noticed in your MySQL’s data directory (in Debian/Ubuntu – /var/lib/mysql) lies a file called ‘ibdata1′. It holds almost all the InnoDB data (it’s not a transaction log) of the MySQL instance and could get quite big. By default this file has a initial size of 10Mb and it automatically extends. Unfortunately, by design InnoDB data files cannot be shrinked. That’s why DELETEs, TRUNCATEs, DROPs, etc. will not reclaim the space used by the file.

I think you can find good explanation and solution there :

http://vdachev.net/2007/02/22/mysql-reducing-ibdata1/


C
Cyno

If your goal is to monitor MySQL free space and you can't stop MySQL to shrink your ibdata file, then get it through table status commands. Example:

MySQL > 5.1.24:

mysqlshow --status myInnodbDatabase myTable | awk '{print $20}'

MySQL < 5.1.24:

mysqlshow --status myInnodbDatabase myTable | awk '{print $35}'

Then compare this value to your ibdata file:

du -b ibdata1

Source: http://dev.mysql.com/doc/refman/5.1/en/show-table-status.html


B
Bahram Ardalan

In a new version of mysql-server recipes above will crush "mysql" database. In old version it works. In new some tables switches to table type INNODB, and by doing so you will damage them. The easiest way is to:

dump all you databases

uninstall mysql-server,

add in remained my.cnf:

    [mysqld]
    innodb_file_per_table=1

erase all in /var/lib/mysql

install mysql-server

restore users and databases


s
steveayre

As already noted you can't shrink ibdata1 (to do so you need to dump and rebuild), but there's also often no real need to.

Using autoextend (probably the most common size setting) ibdata1 preallocates storage, growing each time it is nearly full. That makes writes faster as space is already allocated.

When you delete data it doesn't shrink but the space inside the file is marked as unused. Now when you insert new data it'll reuse empty space in the file before growing the file any further.

So it'll only continue to grow if you're actually needing that data. Unless you actually need the space for another application there's probably no reason to shrink it.


I think you're a little too dismissive of the need to free up the space.
I have a 60Gig Solid State partition. I run out of space fast, since I work with 4+gig databases. I'm looking to move mysql to another partition soon, but this question and it's answers will help me in the meantime
Thank you for this answer, it's very helpful. I have cleared out some tables from legacy data... it's good to know that the size on disk won't grow again any time soon.
I have a 500G ibdata1 file - but almost all of the data that was stored in it is now stored in per-database files. I very much need to shrink this collossal waste of space!
Complete nonsense! A file that keeps bloating up needs to be trimmed whether you're running out of space or not. I would call it a storage leak.
S
Slam

What nobody seems to mention is the impact innodb_undo_log_truncate setting can have.

After reading Percona's blog post about the topic, I've enabled in my MariaDB 10.6 the truncation of UNDO LOG entries which filled 95% of ibdata1, and, after a complete drop and restore, from that moment on my ibdata1 never grew anymore.

With the default innodb_undo_log_truncate = 0 my ibdata1 easily reached 10% of databases space occupation, aka tens of Gigabytes.

With innodb_undo_log_truncate = 1, ibdata1 it's firm at 76 Mb.