一、问题情况
由于公司有台docker服务器的mysql实例在升级表结构时docker崩溃造成mysql不停重启,报错信息如下:
1 2 3 4 5 6 |
mysqld got signal 11 ; Most likely, you have hit a bug, but this error can also be caused by malfunctioning hardware. Thread pointer: 0x7f853c000f80 Attempting backtrace. You can use the following information to find out where mysqld died. If you see no messages after this, something went terribly wrong... |
初步怀疑可能是内存不足造成了docker崩溃,造成在写入数据时中断,让mysql崩溃不停重启。
二、解决办法
1.停止mysql
1 |
service mysql stop |
2.使用myisamchk先检查和修复
命令如果找不到找一下mysql命令所在位置,一般都在同级目录下
1 2 3 4 5 6 |
#myisamchk默认是检查表 myisamchk tbl_name #myisamchk -r是修复表,-o是优化表 myisamchk -r tbl_name myisamchk -o tbl_name |
由于myisamchk要按表检查修复而且只支持MYISAM数据引擎表,使用起来效率低,等于检查mysql的指定表文件,而且要停库所以一般不用,myisamchk还带一个自动修复参数可以写在my.cnf里启动执行
1 2 3 |
[mysqld] myisam-recover = [options] options参数有DEFAULT,BACKUP,FORCE,QUICK,可以设定为BACKUP,FORCE,万万不可单独设置成FORCE,否则数据文件丢了都没处哭去。 |
3.由于myisamchk是最老的修复方法了,我们一般还是习惯使用mysqlcheck,而且mysqlcheck必须在库运行时执行
1 2 3 4 5 6 7 8 9 10 11 |
#检查所有库 mysqlcheck -A -c -p #修复所有库 mysqlcheck -A -r -p #优化所有库 mysqlcheck -A -o -p # -a -c -r -o 互相不能同时写,会报错: Error: mysqlcheck doesn't support multiple contradicting commands. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
mysqlcheck实际相当于在mysql里执行CHECK TABLE、REPAIR TABLE、ANALYZE TABLE、OPTIMIZE TABLE语句。 --analyze 缩写 -a 选项:实际上是执行了ANALYZE TABLE(支持InnoDB,MyISAM,NDB) --check 缩写 -c 选项:实际上是执行了CHECK TABLE(支持InnoDB,MyISAM,ARCHIVE,CSV) --optimize 缩写 -o 选项:实际上执行了OPTIMIZE TABLE(支持InnoDB,MyISAM,ARCHIVE) --repair 缩写 -r 选项:实际上执行REPAIR TABLE(支持MyISAM,ARCHIVE,CSV) --all-databases 缩写 -A 选项:选择所有数据库 -p 使用密码登录 其他修复选项: --repair --quick 尝试快速修复 --repair --force 强行修复 在mysqlcheck时,每张表会被加上READ LOCK。执行时,尤其是大表,将会变得十分耗时 #还可以按表/按库检测[options]为上面提到的选项 mysqlcheck [options] db_name [tbl_name ...] mysqlcheck [options] --databases db_name [...] |
4.如果mysqlcheck检测出InnoDB的错误会提示出来,但是无法修复,所以下来要介绍一下InnoDB数据引擎表的修复方法:
遇到innodb的错误,首先要停止mysql,然后my.cnf里加一下innodb_force_recovery,可以有效停止mysql因为innodb表损坏启动不了或者不停重启,也可以保护错误表不被写入造成更多问题,如果1不行就依次往上加最大为6
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[mysqld] innodb_force_recovery = 1 innodb_force_recovery影响整个InnoDB存储引擎的恢复状况。默认为0,表示当需要恢复时执行所有的 innodb_force_recovery可以设置为1-6,大的数字包含前面所有数字的影响。当设置参数值大于0后,可以对表进行select,create,drop操作,但insert,update或者delete这类操作是不允许的。 1(SRV_FORCE_IGNORE_CORRUPT):忽略检查到的corrupt页。 2(SRV_FORCE_NO_BACKGROUND):阻止主线程的运行,如主线程需要执行full purge操作,会导致crash。 3(SRV_FORCE_NO_TRX_UNDO):不执行事务回滚操作。 4(SRV_FORCE_NO_IBUF_MERGE):不执行插入缓冲的合并操作。 5(SRV_FORCE_NO_UNDO_LOG_SCAN):不查看重做日志,InnoDB存储引擎会将未提交的事务视为已提交。 6(SRV_FORCE_NO_LOG_REDO):不执行前滚的操作。 |
mysql正常启动以后,先找到错误表所在,错误提示:
1 2 3 |
Warning : InnoDB: The B-tree of index PRIMARY is corrupted. Warning : InnoDB: Index index_name is marked as corrupted error : Corrupt |
然后尝试修复表:
1 2 3 4 5 6 |
#首先复制错误表的表结构和数据到新表 CREATE TABLE 新表 SELECT * FROM 旧表; #如果成功了,检查一下数据是否完整,完整的话,就可以删除旧表,重命名新表到旧表,检测正常后使用.如果不放心,直接重命名2次,把旧表先改一下名,新表改名成旧表. DROP TABLE 旧表; ALTER TABLE 新表 RENAME 旧表; |
如果上面复制表失败,可以尝试只复制结构,然后再尝试恢复数据
1 2 3 4 5 6 7 8 9 10 11 |
#复制错误表的表结构到新表 CREATE TABLE 新表 SELECT * FROM 旧表 WHERE 1=2; #mysql5以上版本可以直接执行复制表结构: CREATE TABLE 新表 LIKE 旧表; #然后再尝试复制数据 INSERT INTO 新表 SELECT * FROM 旧表; #如果只有部分字段数据重要可以尝试只恢复部分字段 INSERT INTO 新表(字段1,字段2,…….) SELECT 字段1,字段2,…… FROM 旧表; |
如果一上来的复制表结构和数据没能成功,还可以尝试使用.ibd恢复数据
1 2 3 4 5 6 7 8 9 10 |
#先准备一个结构相同的表,直接复制错误表或者从备份里找到表结构创建一个都行 #清空数据 TRUNCATE TABLE 新表; #清除表空间 ALTER TABLE 新表 DISCARD TABLESPACE; #复制旧表.ibd到数据库的新表.ibd(找到mysql数据库文件的目录复制),复制的时候停一下库最好,然后重导入表数据,如果数据完整重命名就行 cp -a 库名/旧表.ibd 库名/新表.ibd ALTER TABLE 新表 IMPORT TABLESPACE; |
修复操作时如果提示写入失败或者其他问题,可以尝试降低innodb_force_recovery后重启数据库修复,修复完成以后记得去掉innodb_force_recovery
上面这种InnoDB修复对索引损坏比较有效,对数据损坏可能很难修复,再专业的可能需要用到专业DBA软件和工具了,而且.ibd还需要mysql开了innodb_file_per_table = 1,每个InnoDB表会单独生成.frm和.ibd文件,如果没开只有.frm文件,数据统一写在ibdata*里,更难恢复.所以如果有备份,直接从备份里恢复损坏的表是最方便的,前提是增量数据不重要.