如何在 MySQL 中收缩/清除 ibdata1 文件
我在localhost中使用MySQL作为R中执行统计的“查询工具”,即每次运行R脚本时,我创建一个新数据库(A),创建一个新表(B),将数据导入B ,提交一个查询来获取我需要的内容,然后我删除 B 并删除 A。
这对我来说工作正常,但我意识到 ibdata 文件大小正在快速增加,我在 MySQL 中没有存储任何内容,但 ibdata1 文件已经超过 100 MB。
我或多或少使用默认 MySQL 设置进行设置,有没有办法可以在固定时间段后自动收缩/清除 ibdata1 文件?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
当你删除innodb表时,MySQL不会释放ibdata文件内的空间,这就是它不断增长的原因。这些文件几乎不会缩小。
如何缩小现有的 ibdata 文件:
https://dev.mysql.com/doc/refman/5.6/en/innodb-system-tablespace.html#innodb-resize-system-tablespace
您可以编写此脚本并将脚本安排到在固定的时间段后运行,但对于上述设置,多个表空间似乎是一个更简单的解决方案。
如果您使用配置选项
innodb_file_per_table
,您将创建多个表空间。也就是说,MySQL 为每个表创建单独的文件,而不是一个共享文件。这些单独的文件存储在数据库的目录中,当您删除该数据库时,它们也会被删除。这应该消除在您的情况下收缩/清除 ibdata 文件的需要。有关多个表空间的更多信息:
https: //dev.mysql.com/doc/refman/5.6/en/innodb-file-per-table-tablespaces.html
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
在 bash 中快速编写已接受答案的过程脚本:
另存为
purge_binlogs.sh
并以root
身份运行。不包括
mysql
、information_schema
、performance_schema
(和binlog
目录)。假设您在
/root/.my.cnf
中有管理员凭据,并且您的数据库位于默认的/var/lib/mysql
目录中。您还可以在运行此脚本后清除二进制日志以重新获得更多磁盘空间:
Quickly scripted the accepted answer's procedure in bash:
Save as
purge_binlogs.sh
and run asroot
.Excludes
mysql
,information_schema
,performance_schema
(andbinlog
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:
似乎没有人提到 innodb_undo_log_truncate 设置可能产生的影响。
阅读后Percona 的博客文章 关于该主题,我在 MariaDB 10.6 中启用了
UNDO LOG
条目的截断,该条目填充了 95% 的ibdata1
,并且在完全删除和恢复,从那一刻起,我的 ibdata1 就不再增长了。使用默认的
innodb_undo_log_truncate = 0
我的ibdata1
很容易达到 10% 的数据库空间占用,也就是几十 GB。当
innodb_undo_log_truncate = 1
时,ibdata1
的大小固定为 76 Mb。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% ofibdata1
, and, after a complete drop and restore, from that moment on myibdata1
never grew anymore.With the default
innodb_undo_log_truncate = 0
myibdata1
easily reached 10% of databases space occupation, aka tens of Gigabytes.With
innodb_undo_log_truncate = 1
,ibdata1
it's firm at 76 Mb.如果您的目标是监视 MySQL 可用空间,并且无法阻止 MySQL 收缩 ibdata 文件,那么可以通过表状态命令获取它。示例:
MySQL> 5.1.24:
MySQL < 5.1.24:
然后将此值与您的 ibdata 文件进行比较:
来源:http:// /dev.mysql.com/doc/refman/5.1/en/show-table-status.html
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:
MySQL < 5.1.24:
Then compare this value to your ibdata file:
Source: http://dev.mysql.com/doc/refman/5.1/en/show-table-status.html
在新版本的 mysql-server 中,上述配方将粉碎“mysql”数据库。
在旧版本中它可以工作。在新版本中,一些表切换到表类型 INNODB,这样做会损坏它们。
最简单的方法是:
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:
我们最近在 MariaDB 10.6.13 中遇到了一个错误(已在 10.6.14 中修复)导致我们的 ibdata1 文件每天增长 5+GB。当磁盘空间使用情况警报开始发送时,该文件已增长到超过 400GB。
我们的数据库总共约为 865GB,因此使用
mysqldump
进行逻辑备份/恢复至少需要几天时间才能完成。如果您有一个大型数据库,您可以使用 mydumper 更快地执行逻辑转储/恢复,因为显然这仍然是缩小 ibdata1 文件的唯一方法。以下是我在重建数据库服务器以使 ibdata1 恢复到合理大小时遵循的一些注释/过程:
我已经使用了
innodb_file_per_table=1
。 p>我使用此工具来确定
ibdata1
中的空间使用情况确实撤消日志页面(运行了几个小时,这是在我意识到 10.6.13 中存在错误并且不确定是什么导致 ibdata1 增长如此之多之前)。我们有一个主副本和三个副本。
我首先从生产中取出一个副本,使用
mydumper
转储/恢复它,然后使用mariabackup
备份重建的副本。我使用该备份来恢复其余的数据库服务器(速度快得多)。我们已经使用
mariabackup
建立了完整的备份例程。 在做任何事情之前,请确保您有一个已知良好的完整备份!重建第一台服务器
停止对数据库的所有写入。
我会刷新表;设置 global read_only = 1; 以 100% 确保备份期间没有任何内容写入数据库(当然,在副本上,
stopreplica;
,read_only=1
已在我们的配置中的副本上设置)。使用 mydumper 转储数据库。我使用了这个脚本,它会在完成后发送一封电子邮件(如果您设置了电子邮件):
这花了 12 分钟将价值 865GB 的数据库转储到一台相当强大的企业服务器上,该服务器在硬件 RAID 10 中配备了 4 个 SSD。旧服务器这需要不到一小时。
Grep 日志中是否有任何错误。
我尝试简单地删除我们的数据库,关闭 MariaDB,并删除 ibdata1 但这引起了问题,所以我关闭了 MariaDB,擦除了整个数据目录(
rm -rf /var/lib/mysql/ *
),然后运行 mysql_install_db
重新开始(确保您有良好的备份!)。然后我添加了此配置以将撤消日志移出 ibdata1 。当然,如果您想这样做,请根据您的环境进行调整。在这一点上,我认为这样做是个好主意,主要是因为无法截断 ibdata1 ,而且我不想很快再次重建所有服务器。
将这些临时添加到配置中以进行恢复:
现在启动 MySQL / MariaDB 并恢复:
这大约需要 12 小时才能完成。
请务必检查
restore.log
是否有错误 (grep -i error restore.log
)。由于我没有发现错误,我的第一次恢复完全失败了。删除临时配置并重新启动 MySQL/MariaDB。
在执行其他操作之前,我运行了 pt-table-checksum 以确保主副本和所有副本具有相同的数据(尤其是新恢复的副本)。
如果您需要重建其他服务器(其他副本/主服务器),您可以使用
mariabackup
(或 Percona XtraBackup),速度要快得多。像这样的事情(我在副本上运行这个,所以我添加了所有从选项):每台服务器大约需要两个小时才能完成(数据库865GB,企业服务器,10gig网络,RAID 10中的4个SSD)。
撤消日志截断现在肯定可以工作。我最初将 innodb_max_undo_log_size 设置为 10MB,MariaDB 不断地截断它们。当它这样做时它会记录。
ibdata1
现在在我们高度活跃的服务器上稳定保持在 12MB。We recently experienced a bug in MariaDB 10.6.13 (fixed in 10.6.14) that caused our
ibdata1
file to grow 5+GB/day. The file grew to over 400GB by the time alerts on disk space usage started to get sent out.Our databases in total are about 865GB so a logical backup / restore with
mysqldump
would have taken days at least to do. If you have a large database you can usemydumper
to do a logical dump/restore much more quickly since apparently that is still the only way to shrink theibdata1
file.Here are some notes / procedures I followed when I rebuilt our DB servers to get
ibdata1
back down to a reasonable size:I use
innodb_file_per_table=1
already.I used this tool to determine the space usage in
ibdata1
was indeed undo log pages (it took hours to run, this was before I was aware that there was a bug in 10.6.13 and didn't know for sure what was causingibdata1
to grow so much).We have one primary and three replicas.
I first took one replica out of production, used
mydumper
to dump/restore it, then I usedmariabackup
to back up the rebuilt replica. I used that backup to restore the rest of the DB servers (which is much faster).We already have a full backup routine using
mariabackup
. Before you do anything, make sure you have a known good full backup!Rebuilding the first server
Stop all writes to the database.
I do
flush tables; set global read_only = 1;
to make 100% sure nothing is writing to the DB during backup (andstop replica;
on replicas of course,read_only=1
is already set on replicas in our config).Use
mydumper
to dump databases. I used this script which will send an email when it's done (if you've got email set up):This took 12 minutes to dump 865GB worth of DBs on a pretty beefy enterprise server with 4 x SSDs in hardware RAID 10. On the older server this takes less than one hour.
Grep the log for any errors.
I tried simply dropping our databases, shutting down MariaDB, and removing
ibdata1
but that caused problems so I shut down MariaDB, wiped the entire data dir (rm -rf /var/lib/mysql/*
), and ranmysql_install_db
to start fresh (make sure you have good backups!).Then I added this config to move the undo logs out of
ibdata1
. Adjust for your environment, of course, if you want to do this. At this point I think it's a good idea to do so mainly because there's no way to truncateibdata1
and I don't want to have to rebuild all of our servers again any time soon.Add these to the config temporarily for the restore:
Now start up MySQL / MariaDB and restore:
This took around 12 hours to complete.
Make sure to check
restore.log
for errors (grep -i error restore.log
). My first restore was totally botched because of errors I didn't catch.Remove the temp config and restart MySQL/MariaDB.
Before I did anything else, I ran
pt-table-checksum
to make sure the primary and all replicas had identical data (esp. the newly restored replica).If you need to rebuild other servers (other replicas/the primary) you can use
mariabackup
(or Percona XtraBackup) which is much faster. Something like this (I ran this on a replica so I added all the slave options):This all took about two hours per server to complete (865GB in databases, enterprise servers, 10gig network, 4 x SSD in RAID 10).
Undo log truncation is definitely working now. I had set
innodb_max_undo_log_size
to 10MB initially and MariaDB was constantly truncating them. It'll log when it does so.ibdata1
is now holding steady on our highly active servers at just 12MB.正如已经指出的,您无法缩小 ibdata1(为此,您需要转储和重建),但通常也没有真正的需要。
使用自动扩展(可能是最常见的大小设置),ibdata1 预分配存储空间,每次接近满时都会增长。由于空间已经分配,这使得写入速度更快。
当您删除数据时,它不会缩小,但文件内的空间被标记为未使用。现在,当您插入新数据时,它会在进一步增长文件之前重用文件中的空白空间。
因此,只有当您确实需要这些数据时,它才会继续增长。除非您确实需要其他应用程序的空间,否则可能没有理由缩小它。
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.
ibdata1
不收缩是 MySQL 的一个特别烦人的特性。除非删除所有数据库、删除文件并重新加载转储,否则 ibdata1 文件实际上无法缩小。但是您可以配置 MySQL,以便将每个表(包括其索引)存储为单独的文件。这样
ibdata1
就不会增长得那么大。根据 Bill Karwin 的评论,默认情况下启用此功能从 MySQL 5.6.6 版本开始。不久前我做了这件事。但是,要将服务器设置为每个表使用单独的文件,您需要更改
my.cnf
才能启用此功能:https://dev.mysql.com/doc/refman/5.6/en/innodb-file-per-table -tablespaces.html
当您想从 ibdata1 回收空间时,您实际上必须删除该文件:
mysqldump
mysql
和performance_schema
数据库除外。mysql
和performance_schema
)ibdata1
和ib_log
文件当您在步骤 5 中启动 MySQL 时,将重新创建 ibdata1 和 ib_log 文件。
现在你可以出发了。当您创建新数据库进行分析时,这些表将位于单独的 ibd* 文件中,而不是位于 ibdata1 中。由于您通常会在不久后删除数据库,因此
ibd*
文件将被删除。http://dev.mysql.com/doc/refman/ 5.1/en/drop-database.html
您可能已经看过这个:
http://bugs.mysql.com/bug.php?id=1341
通过使用命令
ALTER TABLEENGINE=innodb
或OPTIMIZE TABLE
可以从 ibdata1 中提取数据和索引页到单独的文件中。但是,除非执行上述步骤,否则 ibdata1 不会缩小。关于
information_schema
,没有必要也不可能放弃。它实际上只是一堆只读视图,而不是表。并且没有与它们关联的文件,甚至没有数据库目录。 informations_schema 正在使用内存数据库引擎,并在 mysqld 停止/重新启动时被删除并重新生成。请参阅 https://dev.mysql.com/doc/refman /5.7/en/information-schema.html。That
ibdata1
isn't shrinking is a particularly annoying feature of MySQL. Theibdata1
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: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:mysqldump
of all databases, procedures, triggers etc except themysql
andperformance_schema
databases.mysql
andperformance_schema
)ibdata1
andib_log
filesWhen you start MySQL in step 5 the
ibdata1
andib_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 inibdata1
. As you usually drop the database soon after, theibd*
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
orOPTIMIZE 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. Theinformations_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.添加到 John P 的回答,
对于 Linux 系统,可以使用以下命令完成步骤 1-6:
mysqldump -u [用户名] -p[root_password] [数据库名] > dumpfilename.sql
mysqladmin -u [用户名] -p[root_password] drop [数据库名称]
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 [用户名] -p[root_password] create [database_name]
mysql -u [用户名] -p[root_password] [database_name]
mysql -u [用户名] -p[root_password] [数据库名] < dumpfilename.sql
警告:如果您在此 mysql 实例上有其他数据库,这些说明将导致您丢失其他数据库。确保修改步骤 1,2 和 6,7 以涵盖您希望保留的所有数据库。
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.