MySQL InnoDBに対してどうやってUndropでInnoDBデータをリカバリできるでしょうか

 

この記事で

ターゲット

ソリューション

リファレンス

 

適用範囲:
5.6 MySQLサーババージョン4.1[5.6に4.1をリリース]
この資料の情報は、すべてのプラットフォームに適用されます。

 

目標

Undropforinnodbでこわれたテーブルからデータを抽出する。

 

解決策

オープンソースUsing the opensource UnDROP tool for InnoDB from twindb.comで、時々innodb_force_recoveryで読み取ったテーブルからリカバリできる。

 

Undropツールは使われていないDBサーバが使っているibdataファイル。

 

一般的に、そのツールはibdataファイルあるいは独立したInnoDB tablespaceファイル(innodb_file_per_table使っている.ibdファイル)でインディクスページを抽出する。Blobページが利用できるサブディレクトリへ抽出する。

 

もしデータがインディクスページに抽出されたら、次のステップはデータディレクトリから主キー、一般的なクラスタリング索引IDをリカバリする。そして、データをLOAD DATA INFILEが使えるファイルに抽出する。

 

もしよければ、リカバリしたいデータベースのschema dumpで起動して、必要なときに、innodb_force_recoveryを使ってください。たとえ古いバックアップであっても、何もないよりまだましである。時にUnDROPでibdataファイルから有効なテーブル定義を抽出できる。何のバックアップもなければ、.frmファイルでテーブル定義を再作成できる。.frmもなければ、最後の手はUnDROPでidbataからテーブル定義を抽出して、せめて一部だけのデータをリカバリできる。

 

UnDROPツールは以下の通り。データを抽出する順で配列する: stream_parser

 

stream_parserはbdataからページを抽出するツールである。使い方もすごく便利である:

./stream_parser f

<path_to_ibdata>

 

ページはディフォルトで”pages-<ibdata_file_name>”に抽出する。インディクスページはサブディスプレイに格納される。

FIL_PAGE_INDEX,且blob页被储存在子目录FIL_PAGE_TYPE_BLOB。

 

テーブルにすべてのデータを抽出したいであれば、テーブルの主キーのデータディレクトリインディクスIDを識別する必要がある。これはUnDROPツールの”recover_dictionary.sh”スクリプトで、抽出されたインディクスページに抽出されたディクショナリーデータはサーバの’test’ schemaに移す。このように:

$ ./recover_dictionary.sh

Generating dictionary tables dumps… OK

Creating test database … OK

Creating dictionary tables in database test:

SYS_TABLES … OK

SYS_COLUMNS … OK

SYS_INDEXES … OK

SYS_FIELDS … OK

All OK

Loading dictionary tables data:

SYS_TABLES … 1845 recs OK

SYS_COLUMNS … 22029 recs OK

SYS_INDEXES … 4994 recs OK

SYS_FIELDS … 6070 recs OK

All OK

 

どんなインディクスIDもディクショナリーを検索して探し出せる。

例はmoodle2 schemaのテーブルmdl2_user:

mysql> SELECT SYS_TABLES.NAME TABLE_NAME, SYS_TABLES.ID TABLE_ID,

SYS_INDEXES.NAME INDEX_NAME, SYS_INDEXES.ID INDEX_ID FROM SYS_TABLES LEFT JOIN

SYS_INDEXES ON SYS_TABLES.ID = SYS_INDEXES.TABLE_ID WHERE SYS_INDEXES.NAME LIKE

‘%PRIMARY%’ AND SYS_TABLES.NAME LIKE ‘moodle2/mdl2_user’ AND SYS_INDEXES.NAME IN

(‘PRIMARY’, ‘GENERAL_CLUSTERED_INDEX’);

+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐ ‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐+

| TABLE_NAME | TABLE_ID | INDEX_NAME | INDEX_ID |

+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐ ‐‐‐‐‐‐+

| moodle2/mdl2_user | 646 | PRIMARY | 1867 |

+‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐+‐‐‐‐‐‐‐‐‐‐‐‐ +‐‐‐‐‐‐‐‐‐‐+

1 row in set (0.00 sec)

 

INDEX_IDは名1に該当する抽出ページファイルを返す:

$ ls pages‐ibdata1/FIL_PAGE_INDEX/*1867.page

2015/12/14 Document 2056049.1

https://support.oracle.com/epmos/faces/DocumentDisplay?_adf.ctrlstate=

scibdopz6_382&id=2056049.1 3/6

pages‐ibdata1/FIL_PAGE_INDEX/0000000000001867.page

 

c_parser

もし、テーブル定義があれば、データがc_parserでリカバリできる。このように、mdl2_user.sqlにはテーブル定義を含んでいる:

$ ./c_parser ‐b “./pages‐ibdata1/FIL_PAGE_TYPE_BLOB” ‐p “dumps/moodle2″ ‐l

dumps/moodle/mdl2_user.load ‐5f pagesibdata1/

FIL_PAGE_INDEX/0000000000001867.page ‐t mdl2_user.sql

 

各schemaに対して、以下のスクリプトとダンプディレクトリのテーブル定義で、データディレクトリで見つけ出したテーブルを抽出して、LOAD DATA INFILEにふさわしいファイルにロードする。もし、もとのテーブル定義がないあるいは.frmファイルで抽出出来ない場合に、最後の手として、sys_parser行をアンチノートしてください。元のテーブル定義があれば

分けられたsqlファイルとしてdumps/<schema>/<table>.sqlに格納してください:

#!/bin/bash

RECOVERY_DB=”test”

USER=”root”

PASS=”somepass”

DUMPS=”dumps”

# Create schema

echo > ${DUMPS}/schema.sql

for DB in `mysql ‐‐user=${USER} ‐‐password=${PASS} ‐NBe “select name from

${RECOVERY_DB}.sys_tables” | sed ‐r “s/^(.*)\/.*$/\1/” | grep ‐v SYS_ | sort ‐u `

do

mkdir ‐p ${DUMPS}/${DB}

echo “Creating schema for $DB…”

echo >> ${DUMPS}/schema.sql

echo “CREATE DATABASE IF NOT EXISTS $DB;” >> ${DUMPS}/schema.sql

for TABLE in `mysql ${RECOVERY_DB} ‐‐user=${USER} ‐‐password=${PASS} ‐NBe

“SELECT NAME FROM SYS_TABLES WHERE NAME LIKE ‘${DB}/%'”`

do

echo $TABLE

# ./sys_parser ‐u${USER} ‐p${PASS} ‐d ${RECOVERY_DB} ${TABLE} | tee

${DUMPS}/${TABLE}.sql >> ${DUMPS}/schema.sql

PKEY=`mysql ${RECOVERY_DB} ‐BNe “SELECT SYS_INDEXES.ID FROM SYS_TABLES

LEFT JOIN SYS_INDEXES ON (SYS_TABLES.ID = SYS_INDEXES.TABLE_ID) WHERE

SYS_TABLES.NAME = \”${TABLE}\” AND SYS_INDEXES.NAME=\”PRIMARY\””`

echo “pkey = $PKEY”

PAGE=”pages‐ibdata1/FIL_PAGE_INDEX/`printf ‘%016u’ ${PKEY}`.page”

echo “PAGE = $PAGE”

./c_parser ‐b “./pages‐ibdata1/FIL_PAGE_TYPE_BLOB” ‐p “./${DUMPS}/${DB}” ‐

l ${DUMPS}/${TABLE}.load ‐5f ${PAGE} ‐t ${DUMPS}/${TABLE}.sql > ${DUMPS}/${TABLE}

2015/12/14 Document 2056049.1

https://support.oracle.com/epmos/faces/DocumentDisplay?_adf.ctrlstate=

scibdopz6_382&id=2056049.1 4/6

done

done

 

sys_parser

前に言ってたとおりに、sys_parserはibdataファイルからテーブル定義を抽出するときに使える。けどこれはせいぜい最後の手段で、テーブル定義がなければ、ibdataから使用可能データを抽出することはありえない。

.frmファイルから有効なテーブル定義を抽出する

MySQL Utilitiesパッケージのmysqlfrmで壊れていない.frmファイルから有効な定義を抽出できる。診断モードで、mysqlfrmを使うと、sys_parserでibdataからデータを抽出するときに、同じようなトラブルになるから、mysqlfrmを使うこととサーバ標識は大切である。インポートは調整されて、c_parserと運用する。

所以使用mysqlfrm与服务器标识是很重要的。输出必须被调整来与c_parser运作,コマンドライン、警告、またはデフォルトの文字セット情報は、テーブル定義で発見された場合ので、c_parserが死ぬ。これはテーブル定義を抽出し、一つのschema.sqlがディレクトリから見つけ出した.frmファイルでschema(s)を作成する方法である:

#!/bin/bash

for FRM in `find ../datadir/ ‐type f ‐wholename “*frm” | sort`

do

mysqlfrm ‐‐server=root:somepass@localhost:../datadir/mysql.sock ‐‐port=33307

$FRM | extract_schema.pl

if [ ${PIPESTATUS[0]} ‐ne 0 ]; then

echo “$FRM is corrupt”

fi

done

 

extract_schema.plこのように:

!/usr/bin/perl

open (SCHEMAFILE, ‘>>’, “schema.sql”) or die “Can not write to schema.sql $!”;

$schema = “”;

$table = “”;

2015/12/14 Document 2056049.1

https://support.oracle.com/epmos/faces/DocumentDisplay?_adf.ctrlstate=

scibdopz6_382&id=2056049.1 5/6

while (<STDIN>) {

$origline = $ _;

chomp;

if (/^ CREATE TABLE.*$/) {

m/.*CREATE TABLE \`(. *)\`\.\`(.*)\`[[:space:]]\(/;

$schema = $1;

$table = $2;

print “Creat ing $schema.$table\n”;

unless (‐e $schema or mkdir $schem a) {

die “Unable to create dir for sche ma $schema”;

}

p rint SCHEMAFILE “CREATE DATABASE IF NOT EXISTS $schema;\n”;

print SCHEMAFILE “USE $schema;\n”;

print SCHEMAFILE “$origline”;

open (TABLEFILE, ‘>’, “$schem a/$table.sql”) or die “Can not write to

$schema/$table.sql”;

print TABLEF ILE “CREATE TABLE $table (\n”;

} else {

s/ENGINE=(.*?)[[:space:]].*/ENGINE=\1;/;

s/`PRIMARY`//;

s/^#.*$//;

s/^WARNING.*$//;

$origline =~ s/(.*ENGINE.*$)/\1;/;

$origline =~ s/^#.*$//;

$origline =~ s/^WARNING.*$//;

print SCHEMAFILE “$origline” if (!/^\s*$/);

print TABLEFILE “$_\n” if (!/^\s*$/);

}

}

 

幸い、UnDROPはまだ使用可能だが、これを使うこととは状況はとってもひどくなった。データベースがこわれたときに、どんなデータをリカバリする保障もない。このような状況を避けるために、MySQL Enterprise Backupで定期的に、バックアップを作成してください。そして、せめて一つのslaveをコピしてください。

Comment

*

沪ICP备14014813号-2

沪公网安备 31010802001379号