Partial Data Recovery(restoring .ibd file)
Mövzumuz advance olub fiziki olaraq data-nın recover olunması haqqındadır. MySQL version: 5.6.12
Biraz teorik olaraq məsələni izah edək. MySQL-də cədvəllər database daxilində yerləşir. Cədvəlin öz strukturu .frm faylında, cədvəlin index və data-ları isə .ibd faylında yerləşir.
.ibd cədvəlin tablespace-dir. Və bu hər cədvəl üçün ayrılıqda yaradılır(Əgər MySQL innodb_file_per_table=1 olaraq start olunubsa)
Qeyd edək ki, əgər MySQL innodb_file_per_table=1 şəklində start OLUNMAYIBSA, cədvəlin bütün index və dataları system tablepspace-də yer alır. system tablespace isə shared tablespace olduğu üçün biz onun daxilindən yalnız 1 cədvəlin taplespace-ini çıxardıb restore edə bilmərik. Yəni partial(ayrıca cədvəl) recovery üçün ilkin şərt innodb_file_per_table=1-dir.
system tablespace haqqında qısa məlumat:
A small set of data files (the ibdata files) containing the metadata for InnoDB-related objects (the data dictionary), and the storage areas for the undo log, the change buffer, and the doublewrite buffer. Depending on the setting of the innodb_file_per_table, when tables are created, it might also contain table and index data for some or all InnoDB tables. The data and metadata in the system tablespace apply to all the databases in a MySQL instance.
Bütün bunları nəzərə alıb MySQL-imizi bütün proyektlərimizdə innodb_file_per_table=1 ilə start edirik.
Bunu əyani olaraq göstərək. Database və cədvəl yaradaq:
mysql> create database blog; Query OK, 1 row affected (0.09 sec) mysql> use blog; Database changed mysql> create table partial_test(id int not null); Query OK, 0 rows affected (0.36 sec) mysql> insert into partial_test() values(1),(2),(3),(4),(5); Query OK, 5 rows affected (0.27 sec) Records: 5 Duplicates: 0 Warnings: 0
Və yoxlayaq:
[root@localhost ~]# cd /var/lib/mysql/blog [root@localhost blog]# ls db.opt partial_test.frm partial_test.ibd
Gördüyümüz kimi .frm və .idb faylları yaradılmışdır. Və bu da onu göstərir ki biz partial restore edə bilərik.
Bizim cədvəlimizdə olan məlumatlar:
mysql> select * from partial_test; +----+ | id | +----+ | 1 | | 2 | | 3 | | 4 | | 5 | +----+ 5 rows in set (0.00 sec)
Və biz çox ənənəvi hal kimi .ibd faylını başqa bir direktoriyaya copy edək:
[root@localhost blog]# cp partial_test.ibd /home/sh [root@localhost sh]# ls | grep partial* partial_test.ibd
İndi də cədvəlimizi səhvən, çox kədərli hal kimi truncate edək:
mysql> truncate table partial_test; Query OK, 0 rows affected (0.35 sec) mysql> select * from partial_test; Empty set (0.00 sec)
İddia edirik ki, əgər kopyaladığımız və içində datası olan .ibd file-ı geri qaytardıqda bizim cədvəlimizdə itirilmiş məlumatlar da geri dönəcək. Sınayaq.
Shutting down MySQL:
root@localhost sh]# service mysql stop Shutting down MySQL.... [ OK ]
Geri kopyalama:
[root@localhost sh]# cp partial_test.ibd /var/lib/mysql/blog cp: overwrite `/var/lib/mysql/blog/partial_test.ibd'? y
MySQL start:
[root@localhost sh]# service mysql start Starting MySQL. [ OK ]
Connect oluruq və yoxlayırıq və nə pis ki, xəyallar boşa çıxdı:
mysql> select * from partial_test; ERROR 1146 (42S02): Table 'blog.partial_test' doesn't exist
Və ERROR log-a baxdıqda biz görürük:
InnoDB: Error: table ‘blog/partial_test’
InnoDB: in InnoDB data dictionary has tablespace id 14,
InnoDB: but a tablespace with that id does not exist. There is
InnoDB: a tablespace of name blog/partial_test and id 13, though. Have
InnoDB: you deleted or moved .ibd files?
.
.
.
[ERROR] InnoDB: Failed to find tablespace for table ‘”blog”.”partial_test”‘ in the cache. Attempting to load the tablespace with space id 14.
.
.
.
InnoDB: cannot calculate statistics for table “blog”.”partial_test” because the .ibd file is missing.
Dolayısı ilə artıq bizim cədvəl itirilmişdir(çox təəssüf). Bu da physical copy alıb backup ümidi ilə yaşayanlara kiçik bir dərs olsun.
Bir daha etdiklərimizi ümumiləşdirsək, biz cədvəl yaratdıq, məlumat daxil etdik, daxil edilmiş məlumatlı .ibd faylımızı başqa bir direktoriyaya kopyaladıq. Daha sonra cədvəlimizi truncate etdik və .idb faylını geri kopyaladıq o niyyətlə ki, data-lar geri qayıtsın. NƏTİCƏ= alınmadı.
Davam edək cədvəlimizi drop edək:
mysql> drop table partial_test; Query OK, 0 rows affected (0.17 sec)
Lakin nə möcüzədirsə partial_test cədvəli-nin tablespace-i silinmir(discard olunmur):
[root@localhost blog]# ls db.opt partial_test.ibd
ERROR log-a baxdıqda:
2013-08-10 13:39:27 7fc7c69b6700 InnoDB: Operating system error number 2 in a file operation.
InnoDB: The error means the system cannot find the path specified.
İnnoDB: cannot calculate statistics for table “blog”.”partial_test” because the .ibd file is missing.
Hətta düzgün drop da getmədi. Məcburən:
[root@localhost blog]# rm partial_test.ibd rm: remove regular file `partial_test.ibd'? y [root@localhost sh]# service mysql stop Shutting down MySQL.... [ OK ] [root@localhost sh]# service mysql start Starting MySQL. [ OK ]
2-ci ssenarimizi yaradaq. Cədvəl truncate etmək əvəzinə gəlin .ibd faylın özünü bir-başa silək və geri kopyalayaq:
mysql> create table remove_test(id int not null); Query OK, 0 rows affected (0.32 sec) mysql> show tables; +----------------+ | Tables_in_blog | +----------------+ | remove_test | +----------------+ 1 row in set (0.00 sec) mysql> insert into remove_test() values(1),(2),(3),(4),(5); Query OK, 5 rows affected (0.09 sec) Records: 5 Duplicates: 0 Warnings: 0
Və kopyalayaq:
[root@localhost blog]# ls db.opt remove_test.frm remove_test.ibd [root@localhost blog]# cp remove_test.ibd /home/sh
Daha sonra təsəvvür edək ki, yeni məlumatlar daxil edilib və index əlavə olunub:
mysql> alter table remove_test add index(id); mysql> insert into remove_test() values(6),(7),(8),(9),(10); Query OK, 5 rows affected (0.10 sec) Records: 5 Duplicates: 0 Warnings: 0
Düşünün ki, kimsə server işlək vəziyyətdə ola-ola remove_test.ibd faylını datadir-dən remove edir:
[root@localhost blog]# rm remove_test.ibd rm: remove regular file `remove_test.ibd'? y
Bizim bundan dərhal xəbərimiz olmayacaq. Lakin bir müddət sonra server-ə restart verdikdə və daha sonra baxdıqda:
mysql> select * from remove_test; ERROR 1146 (42S02): Table 'blog.remove_test' doesn't exist
ERROR log-a nəzər yetirdikdə:
[ERROR] InnoDB: Could not find a valid tablespace file for ‘blog/remove_test’
[ERROR] InnoDB: Tablespace open failed for ‘”blog”.”remove_test”‘, ignored.
İndi isə server-i shut edib kopyaladığımız .ibd faylını geri gətirək. Məqsəd heç olmasa data-larımızın yarısını geri qaytarmaqdır:
[root@localhost sh]# cp remove_test.ibd /var/lib/mysql/blog [root@localhost sh]# chown -R mysql:mysql /var/lib/mysql [root@localhost blog]# ls db.opt remove_test.frm remove_test.ibd
Start edirik və hətta MySQL-imiz crash olur…
Bunu mən BUG kimi report etdim artıq:
http://bugs.mysql.com/bug.php?id=69980
Bunun səbəbi dokumentasiyaya əks getməyimizdir 🙂
You cannot freely move .ibd files between database directories as you can with MyISAM table files. The table definition stored in the InnoDB shared tablespace includes the database name. The transaction IDs and log sequence numbers stored in the tablespace files also differ between databases.
Dolayısı ilə yenidən fiziki olaraq cədvəlləri kopyalayanların kədərini artıracaq bir hadisə baş verdi ki, bu tip “backup” backup deyildir.
Ümumi yuxarıda qeyd olunanlardan çıxan nəticələr:
1)
Heç bir şəkildə əməliyyat sisteminin köməyi ilə cədvəllərə dəyişiklik etməyin, onları silməyin.
Yəni rm qəbul olunmazdır(yuxarıda əyani nümunə var)
2)
Fiziki olaraq .ibd fayllarının kopyalanması sizə recover edə biləcəyiniz bir backup vermir.Yenə də yuxarıda göstərdiyimiz 2 hal buna sübutdur.
Bəs sual olunsun ki, partial restore-u necə edə bilərik?
Bunun üçün yeni cədvəl yaradaq onun düzgün backup-ını alaq və düzgün restore-unu göstərək.
Ardıcıllığa riayət etməyiniz mütləqdir:
mysql> create database blog; Query OK, 1 row affected (0.06 sec) mysql> use blog; Database changed mysql> create table restore_it(id int not null); Query OK, 0 rows affected (0.46 sec) mysql> insert into restore_it() values(1),(2),(3),(4),(5); Query OK, 5 rows affected (0.33 sec) Records: 5 Duplicates: 0 Warnings: 0
Düzgün physical backup-ı biz XtraBackup(innobackupex) [online hot backup tool] ilə alırıq. Bu bizə “safe”=”clean” .ibd file verəcək. Yəni tam dokumentasiyada göstərildiyi kimi:
In this context, a “clean” .ibd file backup is one for which the following requirements are satisfied:
- There are no uncommitted modifications by transactions in the .ibd file.
- There are no unmerged insert buffer entries in the .ibd file.
- Purge has removed all delete-marked index records from the .ibd file.
- mysqld has flushed all modified pages of the .ibd file from the buffer pool to the file.
Backup alaq:
[root@localhost ~]# innobackupex --user=root --password=12345 /home/sh/backup5 --no-timestamp --defaults-file=/usr/my.cnf . . . 130811 00:54:21 innobackupex: Connection to database server closed 130811 00:54:21 innobackupex: completed OK!
Backup-dan sonra işimizi çətinləşdirmək məqsədilə index əlavə edək:
mysql> alter table restore_it add index(id); Query OK, 0 rows affected (0.62 sec) Records: 0 Duplicates: 0 Warnings: 0
Daha sonra cədvəlimizi truncate edək ki, restore-a səbəb olsun 😉
mysql> truncate table restore_it; Query OK, 0 rows affected (0.50 sec)
Restore üçün physical backup-ımızı prepare etməliyik:
[root@localhost ~]# innobackupex --apply-log --redo-only /home/sh/backup5 . . . InnoDB: Starting shutdown... InnoDB: Shutdown completed; log sequence number 1679817 130811 01:07:06 innobackupex: completed OK! Və: [root@localhost ~]# innobackupex --apply-log /home/sh/backup5 . . InnoDB: Shutdown completed; log sequence number 1679904 130811 01:07:29 innobackupex: completed OK!
Bizə full restore gərəkli olmadığına görə. Yəni biz bütün database-i yox, məhz 1 cədvəli geri qaytarmaq istədiyimiz üçün. İnnobackupex-in copy-back funksiyasından yox, məhz MySQL-in öz imkanlarından istifadə edəcik. Xahiş olunur ən əhəmiyyətli hissəyə diqqət yetirək ani səhv yenə də cədvəlimizin itirilməsinə gətirib çıxardacaq:
1-ci addım köhnə tablespace-i discard edirik:
mysql> alter table restore_it discard tablespace; Query OK, 0 rows affected (0.08 sec)
2-ci addım İnnobackupex-lə hazırladımız backup olan direktory-dən .ibd faylını kopyalamaq:
[root@localhost ~]# cd /home/sh/backup5 [root@localhost blog]# cp restore_it.ibd /var/lib/mysql/blog [root@localhost blog]# chown -R mysql:mysql /var/lib/mysql
3-cü addım yeni tablespace-i import edirik:
mysql> alter table restore_it import tablespace; Query OK, 0 rows affected, 2 warnings (0.38 sec)
Bu zaman warning-lər çıxır:
mysql> show warnings; +---------+------+-------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------------------------------------------------------------------------------------------------------------+ | Warning | 1810 | InnoDB: IO Read error: (2, No such file or directory) Error opening './blog/restore_it.cfg', will attempt to import without schema verification | | Warning | 1817 | InnoDB: Index corrupt: Index '"id"' not found or corrupt, you should recreate this index.
restore_it.cfg faylına hələ ki nəzər yetirmirik. Bu haqda ayrıca yazım olacaq.
2-ci Warning index-in tapılmadığını ve bizim onu yenidən yaratmalı olduğumuzu bildirir.
4-cü addım data-nın qayıdıb-qayıtmadığını yoxlamaqdır:
mysql> select * from restore_it; ERROR 1712 (HY000): Index restore_it is corrupted
Köhnə index-i drop edək:
mysql> alter table restore_it drop index id; Query OK, 0 rows affected (0.28 sec) Records: 0 Duplicates: 0 Warnings: 0
Və mutlu sonu görək:
mysql> select * from restore_it; +----+ | id | +----+ | 1 | | 2 | | 3 | | 4 | | 5 | +----+ 5 rows in set (0.00 sec)
🙂 Məlumatlarımız geri döndü.
Partial restore üçün lazım olanları ümumiləşdirsək:
*) innodb_file_per_table=1 ilə start olmuş server.
*) Xtrabackup-la alınmış clean physical backup
*) 1 ədəd MySQL-i sevən beyin
Təşəkkürlər.