今天有网友问我一个MySQL恢复的问题。下面提供了屏幕截图。对于这个问题,在一些停电场景下还是有可能出现的。首先我要确认是线上业务还是测试环境。对于在线业务,这具有很大的影响。如果数据库无法启动,首先要做的是启动数据库,然后在此基础上检查丢失数据的范围,并安排数据恢复。当然,从我的角度来看,如何快速重现这个问题。我用自己写的脚本快速搭建了一个测试主从环境(https://github.com/jeanron100/mysql_slaves,后来有大牛建议用Python来做,最近也在考虑),可以在几分钟内完成。我们创建一个表test并指定两个字段:id和name。然后开始显式事务。createtabletest(idintprimarykey,namevarchar(30)notnull);显式开启一个事务:begin;insertintotestvalues(1,'a');insertintotestvalues(2,'b');insertintotestvalues(3,'c');不提交,我们直接查看mysql的服务进程,直接杀掉。默认情况下,启用双1指示器。Let'sdirectlysimulateapowerfailureandrestarttoseethebackgroundprocessing:2017-09-1315:05:1135556[Note]InnoDB:HighestsupportedfileformatisBarracuda.2017-09-1315:05:1135556[Note]InnoDB:Thelogsequencenumbers1625987and1625987inibdatafilesdonotmatchthelogsequencenumber1640654intheib_logfiles!2017-09-1315:05:1135556[注意]InnoDB:Databasewasnotshutdownnormally!2017-09-1315:05:1135556[注意]InnoDB:Startingcrashrecovery.2017-09-1315:05:1135556[注意]InnoDB:从.ibd文件中读取表空间信息。..2017-09-1315:05:1135556[Note]InnoDB:Restoringpossiblehalf-writtendatapages2017-09-1315:05:1135556[Note]InnoDB:fromthedoublewritebackbuffer...intotal3rowoperationstoundoInnoDB:Trxidcounteris23042017-09-1315:05:1135556[注意]InnoDB:128rollbacksegment(s)areactive.InnoDB:Startinginbackgroundtherollbackofuncommittedtransactions2017-09-1315:05:117f5ccc3d1700InnoDB:Rollingbacktrxwithid1806,3rowstoundo2017-09-1315:05:113InnoDBRollbackofr5:05:113withID1806Completed2017-09-1315:05:117F5CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC3D1700INDB:rollbackofnon-preparedtransactionscompleted2017-09-1315:05:05:05:1135556[注意]-09-1315:05:1135556[Note]Recoveringafteracrashusingbinlog2017-09-1315:05:1135556[Note]Startingcrashrecovery...2017-09-1315:05:1135[Note]5崩溃恢复完成。可以看到,后台检测到上次异常宕机,然后开始崩溃恢复。InnoDB检测到日志LSN为1625987,系统数据文件ibd的LSN为1625987,与ib_logfiles中的LSN不匹配,随后进行了一系列的recovery,rollforward,resume,rollback。***表中数据为空,证明之前的事务已经回滚。所以根据上面的情况,我们理解为事务启动了,基本情况下不会出现这个问题。什么时候会抛出初始错误。我们继续测试,开启显式事务,不commit。开始;插入测试值(1,'a');插入测试值(2,'b');插入测试值(3,'c');然后杀掉mysql服务进程,找到mysql数据目录,删除redo文件。完成后我们重新启动数据库。这时候抛出了类似截图的错误。2017-09-1316:05:1436896[Note]InnoDB:HighestsupportedfileformatisBarracuda.2017-09-1316:05:147f73450a97e0InnoDB:Error:page7logsequencenumber1627722InnoDB:isinthefuture!Currentsystemlogsequencenumber1626124.InnoDB:YourdatabasemaybecorruptoryoumayhavecopiedtheInnoDBInnoDB:tablespacebutnottheInnoDBlogfiles.SeeInnoDB:http://dev.mysql.com/doc/refman/5.6/en/forcing-innodb-recovery.htmlInnoDB:更多信息。目前这个问题的范围其实并不明显,因为尽管如此,我们仍然可以写入数据。mysql>insertintotestvalues(1,'a');QueryOK,1rowaffected(0.04sec)mysql>select*fromtest;+----+-----+|id|name|+----+------+|1|a|+----+------+1rowinset(0.00sec)关于crashrecovery,有一个数据参数需要特别注意,就是innodb_force_recovery,默认值为该参数为0,若为非零值(范围1-6),将有如下影响范围。1(SRV_FORCE_IGNORE_CORRUPT):忽略检测到的损坏页面。2(SRV_FORCE_NO_BACKGROUND):阻止主线程运行。如果主线程需要执行全清除操作,就会导致崩溃。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服务。mysql>setglobalinnodb_force_recovery=2;ERROR1238(HY000):Variable'innodb_force_recovery'isareadonlyvariable假设我们设置为2,再次重现这个问题,你会发现数据库可以临时启动,但是只能查询数据,DML操作会抛出一个错误。mysql>select*fromtest;Emptyset(0.00sec)mysql>mysql>insertintotestvalues(1,'a');ERROR1030(HY000):Goterror-1fromstorageengine根据这个影响的范围评估force_recovery的值,我们可以做出相应的取舍起来。如果MySQL服务无法正常启动,可以修改该参数的值,使其先满足服务持续性的基本问题。然后在评估后导出重要数据。
