记一次因磁盘块默认保留策略导致的数据库导入失败问题

近日开发人员要新测试一个功能,需要用到生产环境中的数据库(生产库版本:mysql-5.5.38-linux2.6-x86_64),因此需要我协调将生产库同步到测试库(测试库版本:mysql-5.5.38-linux2.6-x86_64)中。由于测试库中有很多从生产库同步过来的库,但这些库大都已经dirty了,所以不如重新同步一份。

由于这些数据库在我接手以前就已经存在了,但直到在我使用的过程中发现,此数据库存在问题,原来它并没有启用innodb_file_per_table选项(这个选项在MySQL 5.6.6以及更高版本中是默认启用的),如果此选项没有被启用,那么使用Innodb数据库类型的数据库的表中的数据和索引都是存放在系统表空间(the system tablespace)的,而系统表空间存放在ibdata_file文件(这个文件默认就是ibdata1)里。如果此选项没有启用就会导致文件系统的性能对数据库的性能产生巨大的影响,特别是当执行一些如DROP TABLE 和 TRUNCATE TABLE操作时。同时因为MySQL的系统表空间永远不会缩小,而且数据库中所有的数据库实例都会共享系统表空间,会导致这个文件会越来越大,以致于耗尽磁盘空间。

例如举个实际的例子:

记一次因磁盘块默认保留策略导致的数据库导入失败问题

记一次因磁盘块默认保留策略导致的数据库导入失败问题

上图就是生产库和测试库的例子,生产库的ibdata1文件已经达到了90GB,测试库的ibdata1文件也不甘示弱,达到了71GB。

以下详细说说此事情的经过。

生产库的磁盘为SSD磁盘,性能还是不错的,每日早上4点都有数据库备份脚本对生产库备份(mysqldump出sql并压缩成zip文件)。因此要将生产库的库同步到测试库的话只需要将备份文件用scp命令通过内网地址传输到测试库,将zip文件解压出来,通过nohup命令后台导入即可。

记一次因磁盘块默认保留策略导致的数据库导入失败问题

但随即发现问题不妙。原先监控系统早就报警说磁盘空间不足20%,一直没在意,后来发现磁盘空间直接利用率100%了。

记一次因磁盘块默认保留策略导致的数据库导入失败问题 

但是发现明明总空间有99GB而使用94GB,而使用率却是100%,那5GB“不翼而飞”了?经排查,先排除磁盘损坏原因,后来确定为原来是一个默认的系统保护策略。

此策略的英文描述如下:

Reserving some number of filesystem blocks for use by  privileged  processes  is done to avoid filesystem fragmentation, and to allow system daemons, such as syslogd(8), to continue to function correctly after non-privileged processes are prevented from writing to the filesystem.  Normally, the default percentage of reserved blocks is 5%.

翻译成中文大体如下:

此策略用于为一些特权进程(这个进程与root用户发起的进程不同,注意区分)正确执行完来避免造成文件系统碎片和为了允许系统后台程序如syslogd在非特权进程被阻止写入文件系统后能继续正常的工作,而采取的保留一些文件系统块用于专用的策略。默认保留的块数量是总数量的5%。

这个策略可用于ext2/ext3/ext4等系统。

记一次因磁盘块默认保留策略导致的数据库导入失败问题

dumpe2fs /dev/xvdb1 |grep -i "block coun"     
awk 'BEGIN{print 1310694 / 26213888}' ,bc工具做浮点数除法时有问题,建议使用awk。      
#0.05 --> 5% Reserved block count --> refer to 'man 8 tune2fs' and option '-m'

经图上计算恰好是5%!既然这是默认策略,不管是默认还是不默认的,都可以调整,然后用tune2fs调整一下,如下图所示:

记一次因磁盘块默认保留策略导致的数据库导入失败问题

这样那5GB空间就可以使用了,然后清除一些应用日志继续执行程序后发现还是有问题。

记一次因磁盘块默认保留策略导致的数据库导入失败问题

重新执行mysql导入命令后发现磁盘空间很快又被用尽了,赶紧取消导入命令。

记一次因磁盘块默认保留策略导致的数据库导入失败问题

记一次因磁盘块默认保留策略导致的数据库导入失败问题

此时才开始关注MySQL的ibdata问题,也就是文章刚开始提到的问题了。因此日后配置数据库时如果要使用Innodb一定要记得开启innodb_file_per_table选项。同时5%的磁盘保留策略最好不要随意更改,免得文件系统真的遭到破坏,比如碎片的产生。

再提醒一句:MySQL的系统表空间永远不会缩小,因此ibdata1这个文件会越来越大,要想降低ibdata文件的大小,决不能采用直接删除的办法,如果直接删除了,那就比较悲催了,因为Innodb引擎的原理会轻则导致数据库无法启动(意思是可以恢复),重则导致数据库数据丢失(也能找回来,但未必能100%找回来),唯一降低ibdata文件的办法就是重新安装数据库,没错,是重新安装数据库,将原先的数据库通过mysqldump导出(不要用percona-xtrabackup备份,这个工具并不是导出sql文件),数据库重新安装后,启用innodb_file_per_table选项,再用备份的SQL文件导入。上述操作显而易见还是非常麻烦的。

再额外说一些后续。

如果只有有限的机器做数据库使用,最好不同业务运行在不同的数据库服务器上。即一台机器上运行多个数据库服务器,每一个数据库服务器只运行一个数据库实例,这样做虽然繁琐,但它的好处也是显而易见的。例如一个数据库服务器损坏不会影响一批业务系统。每一个数据库实例可以很容易的对应一个从库,更好的实现HA。如果一个数据库服务器运行多个数据库实例,那么配置主从复制时会非常麻烦,因为有多个实例需要配置主从时,它的二进制文件和pos值在利用mysqldump命令逐个备份数据库时是很难确定的,除非借助percona-xtrabackup或者一些手动的办法,获取唯一的二进制文件和pos值。这种策略的设定正好在mysql的multiserver上很好的得到了体现。

tag:mysql ibdata文件,mysql innodb_file_per_table,tune2fs,磁盘保护策略,系统保留块计数

--end--




本文转自 urey_pp 51CTO博客,原文链接:http://blog.51cto.com/dgd2010/1705606,如需转载请自行联系原作者


上一篇:阿里云域名注册与域名解析步骤(绑定公网ip)


下一篇:阿里云如何注册公司?(注册公司流程)