假设你在使用MySQL中的InnoDB驱动,由于遇到了驱动程序错误,内核错误,电源故障或某些罕见的MySQL错误,而在InnoDB ibdata1文件损坏,实例不能启动。你该怎么办呢? 某门户mysql innodb数据库实例损坏,数据库服务无法启动,使用文件系统上的数据库frm及bid文件恢复数据库内的业务数据。 Mysql innodb数据库将实例的基础字典信息存储在data目录下的ibdata1文件以及mysql实例中,可以将其理解为oracle数据库的system表空间以及数据字典,如果损坏数据实例将无法启动。 Mysql innodb引擎打开innodb_file_per_table后,每个表都会单独存放在一个表空间中,包含了tablename.frm和tablename.ibd文件,其中.frm后缀的文件记录表的结构信息,.ibd后缀的文件记录表的数据。在mysql实例无法启动的情况下使用这两个文件可以恢复数据库内的业务数据。 恢复过程中需要获得每个数据表的字段个数和每个表空间文件的table id(5.5版本),其中字段个数记录在frm文件中。 1、分析frm文件格式获得表字段个数 分析mysql源代码中位于table.cc文件中的open_binary_frm函数获得frm文件中记录字段数的位置: open_binary_frm函数中位于table.cc文件第766行调用了get_from_pos函数,该函数负责计算frm文件内字段信息集合的起始偏移量: 该函数中首先seek到file head+64,然后读取“(head+4的dword)+(head+8的dword)*4”的数据,最后pos=buf+length,这里之所以不直接读取,是因为读到的位置可能不是双数,因此要从第64字节读取后整个buffer进行小头转换再读取最后的目标pos。 对该段代码进行解释,pos的值就是frm文件从第64字节开始,跳过head+4的dword内记录的字节数后读取(head+8的dword)*4个字节内的值。 举例说明: 第4、5字节为03 00,小头转换后为0003,第8、9字节为01 00,转换后为00 01。按上述算法,从第64字节开始读取3+1*4=7个字节,读取后进行小头转换为“2F 2F 00 00 00 20 00”,最后截取第3到结束为“00 00 20 00”等于10进制的8192,这就是frm文件中字段信息的保存起始偏移量。 再次观察open_binary_frm函数,我们需要的表字段数实际上是记录在字段信息的保存起始偏移量+258位置: 在本例中为8192+258=8450=0x2102的位置 04 00小头转换后为0004,表示该表有4个字段。 至此字段数量信息读取完成。 2、获得表对应的space_id space_id 可以从ibd文件的第38~39字节、42~43字节两处处获得: 两处完全一样,读取其中一个即可。 1、模拟数据库字典信息损坏 将数据库data目录下ibdata文件改名为ibdata.1,此时启动数据库服务失败,err日志中出现以下信息: 2、在辅助实例生成数据表ddl语句 在目标库建立与原库名称相同字段数量相同的表(字段名任意),将原库frm文件复制到辅助实例,并设置innodb_force_recovery==6 3、在目标实例创建空表并分离数据 执行上述create table命令,对齐space_id,并执行表空间分离 4、使用原frm、ibd文件覆盖目标库同名文件 停止目标实例服务,将需要恢复的原数据库内的frm、ibd文件覆盖目标实例的同名文件,并import表空间: 恢复过程中重要点如下: 1、批量快速获得表的字段数 生产系统包含大量数据表,人工逐个分析frm文件速度慢,需要开发工具批量自动获取。 2、批量获得space_id并生成对齐语句 Space_id的对齐是指在目标库某个表在全实例被创建的顺位必须相同(5.5版本,5.6及以上版本不需要),比如某个表在原库的space_id=100,在目标库上必须先建立99个空表,再建立这个表就能使该表的space_id相同。核心逻辑如下: 3、原地恢复 生产系统数据表数量多、体积大,全部复制到目标实例data目录下时间太长,因此需要将目标实例的data目录指向原实例,同时需要批量将原实例data目录下的所有文件改名。部分代码如下: 1、建立辅助实例、目标实例(人工) 2、批量获得原库表字段个数、space_id(工具) 3、辅助实例批量生成create table语句并自动生成space_id对齐(建空表)语句(工具) 4、目标库文件改名、目标库使用生成的createtable语句建立空表(工具) 5、目标库覆盖(工具) |
|