Статьи

Проверка согласованности списка листовых узлов дерева B + в InnoDB

Это сообщение от  Александра Кузьминского из MySQL Performance Blog.

Если у нас есть страницы InnoDB, есть два способа узнать, сколько записей они содержат:

  • Поле PAGE_N_RECS в заголовке страницы
  • Подсчет записей при ходьбе по списку записей от минимума до супремума

В какой — то предыдущей версии из инструмента восстановления краткого резюме было добавлено на свалку , которая производится с помощью constraints_parser.

Но если страница потеряна и page_parser не нашел ее, все записи с этой страницы будут потеряны. Другими словами, статистика восстановления на странице дает нам мало представления о том, является ли восстановленная таблица полной.

Чтобы устранить этот недостаток, в ревизии 80 введен новый инструмент index_check.

Как вы знаете, InnoDB хранит таблицу в кластерном индексе с именем PRIMARY.

ПЕРВИЧНЫЙ индекс — это структура дерева B + . Приятно, что все конечные узлы имеют указатели на следующий конечный узел, поэтому следуя указателям, мы можем прочитать весь индекс в порядке первичного ключа. InnoDB расширяет структуру и сохраняет также указатель на предыдущий узел. Предыдущий узел головы равен NULL, а также следующий узел хвоста.

Основываясь на этих знаниях, мы можем проверить, есть ли у нас все элементы списка. Если мы сможем перейти с любой страницы InnoDB в начало и конец списка, то наш набор страниц завершен.

Это именно то, что делает index_chk — он читает файлы из каталога со страницами InnoDB (который создается page_parser) и пытается просмотреть список страниц вперед и назад.

Давайте рассмотрим пример. Я взял поврежденное табличное пространство InnoDB и разделил его с помощью page_parser:

# ./page_parser -f /var/lib/mysql/ibdata1
Opening file: /var/lib/mysql/ibdata1
File information:
ID of device containing file:        64512
inode number:                     27037954
protection:                         100660 (regular file)
number of hard links:                   1
user ID of owner:                      107
group ID of owner:                     116
device ID (if special file):             0
blocksize for filesystem I/O:         4096
number of blocks allocated:          86016
time of last access:            1374662334 Wed Jul 24 06:38:54 2013
time of last modification:      1374233938 Fri Jul 19 07:38:58 2013
time of last status change:     1374233938 Fri Jul 19 07:38:58 2013
total size, in bytes:             44040192 (42.000 MiB)
Size to process:                  44040192 (42.000 MiB)
8.26% done. 2013-07-24 08:39:58 ETA(in 00:00 hours). Processing speed: 3637248 B/sec
...
95.70% done. 2013-07-24 08:40:09 ETA(in 00:00 hours). Processing speed: 4399053 B/sec
#

Теперь давайте возьмем некоторый индекс и проверим, есть ли на нем все страницы:

# ls pages-1374669586/FIL_PAGE_INDEX/0-410/
00000000-00000145.page  00000000-00000235.page  00000000-00000241.page  00000000-00000247.page  00000000-00000254.page
00000000-00000147.page  00000000-00000236.page  00000000-00000243.page  00000000-00000249.page  00000000-00000255.page
00000000-00000148.page  00000000-00000239.page  00000000-00000244.page  00000000-00000251.page
# ./index_chk -f pages-1374669586/FIL_PAGE_INDEX/0-410
Couldn't open file pages-1374669586/FIL_PAGE_INDEX/0-410/00000000-00000140.page
#

Плохие новости, страница с идентификатором 140 отсутствует!

Действительно, предыдущая страница перед страницей № 145 — это страница № 140, но она отсутствует.

# hexdump -C pages-1374669586/FIL_PAGE_INDEX/0-410/00000000-00000145.page | head -2
00000000  d4 cd 68 41 00 00 00 91  00 00 00 8c ff ff ff ff  |..hA............|
00000010  00 00 00 00 2b 6c ea 90  45 bf 00 00 00 00 00 00  |....+l..E.......|
#

Для веселого окончания давайте проверим индекс со всеми страницами:

# ls pages-1374669586/FIL_PAGE_INDEX/0-2/* # this is SYS_COLUMNS table
pages-1374669586/FIL_PAGE_INDEX/0-2/00000000-00000010.page  pages-1374669586/FIL_PAGE_INDEX/0-2/00000000-00002253.page
pages-1374669586/FIL_PAGE_INDEX/0-2/00000000-00002244.page
# ./index_chk -f pages-1374669586/FIL_PAGE_INDEX/0-2
OK
#

Таким образом, таблица полностью восстанавливается, если и только если выполняются два условия:

  • Команда grep «Потерянные записи: ДА» table.dump | grep -v «Страница листа: НЕТ» ничего не выводит
  • ./index_chk -f pages-1374669586 / FIL_PAGE_INDEX / <inde_id of = ”» my = ”» table = »»> сообщает о ОК