Сбой при создании индекса MySQL на столе

10

ОБНОВЛЕНИЕ: tl; dr: Проблема заключалась в том, что MySQL использует TMPDIRпри создании индексов. И у меня TMPDIRне хватило места на диске.

Оригинал Q:

Я пытаюсь добавить индекс в таблицу InnoDB и получаю table is full error. У меня достаточно дискового пространства, а в конфигурации MySQL файл на таблицу = 1. Данные таблицы составляют 85 ГБ, и я предполагаю, что индекс будет около 20 ГБ - 30 ГБ, и у меня гораздо больше дискового пространства, чем это. Я также использую ext3, поэтому я не чувствую никаких проблем с ограничением размера файла с точки зрения ОС.

Зарегистрированная ошибка выглядит так:

140616 13:04:33  InnoDB: Error: Write to file (merge) failed at offset 3 1940914176.
InnoDB: 1048576 bytes should have been written, only 970752 were written.
InnoDB: Operating system error number 0.
InnoDB: Check that your OS and file system support files of this size.
InnoDB: Check also that the disk is not full or a disk quota exceeded.
InnoDB: Error number 0 means 'Success'.
InnoDB: Some operating system error numbers are described at
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/operating-system-error-codes.html
140616 13:04:33 [ERROR] /usr/libexec/mysqld: The table 'my_table' is full

Что является причиной этого и как я могу решить?

Создать таблицу:

`CREATE TABLE `my_table` (
 `uid_from` bigint(11) NOT NULL,
 `uid_to` bigint(11) NOT NULL,
 `counter` int(11) NOT NULL,
 `updated` date NOT NULL,
 PRIMARY KEY (`uid_to`,`uid_from`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8`

Данные равны 87,4 ГБ, и, по моим оценкам, около 1,5B строк.

SHOW GLOBAL VARIABLES LIKE 'tmpdir';
Variable_name   Value
tmpdir  /tmp

[root@web ~]# df -h /tmp
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       40G   24G   14G  64% /
Ноам
источник
1
«InnoDB: убедитесь, что ваша ОС и файловая система поддерживают файлы такого размера. InnoDB: проверьте также, что диск не заполнен или превышена квота диска». - Вы это проверяли? Вы используете ext2? ext3? XFS? Проверены ли квоты для пользователя?
Philᵀᴹ
@Phil Проверено оба. Я использую ext3 и почти уверен, что с дисковым пространством проблем нет. Предчувствие, это связано с тем, что журнал достигает своего предела или что-то в этом роде, но на самом деле не знаю, как это проверить и исправить.
Noam
Интересно, что это за файл (слияние)?
акузминский
@akuzminsky хмм Понятия не имею. Я только запустил создание нового индекса из PHPMyAdmin.
Noam
+1 за TMPDIR, это была проблема и на моем сервере.
Чад Э.

Ответы:

8

Пожалуйста, не обманывайте себя сообщением об ошибке The table 'my_table' is full. Этот редкий сценарий не имеет абсолютно никакого отношения к дисковому пространству. Это полное состояние таблицы связано с внутренней сантехникой InnoDB.

Во-первых, взгляните на эту диаграмму архитектуры InnoDB

InnoDB Архитектура

Обратите внимание, что системное табличное пространство (файл ibdata) имеет только 128 сегментов отката и 1023 слота отката на сегмент отката. Это накладывает ограничения на размер отката транзакции. Другими словами, если одному сегменту отката требуется более 1023 слотов для поддержки транзакции, транзакция выполнит это table is fullусловие.

Вспомните ресторан Red Lobster в Нью-Джерси . Он может вместить 200 человек. Если ресторан переполнен, ряд людей может выйти на улицу, чтобы ждать. Если люди на линии нетерпеливы, они могут уйти, потому что ресторан переполнен. Очевидно, что решение не состояло бы в том, чтобы сделать Нью-Джерси больше (или получить больше дискового пространства). Решение было бы сделать ресторан Red Lobster больше. Таким образом, вы можете увеличить вместимость до, скажем, до 240. Даже при этом, линия может образоваться снаружи, если более 240 человек решат приехать в Красный Лобстер.

Просто чтобы дать вам пример, у меня был клиент с 2 ТБ системного табличного пространства, и innodb_file_per_table был отключен. (346G для ibdata1, сброс для ibdata2). Я выполнил этот запрос

SELECT SUM(data_length+index+length) InnoDBDataIndexSpace
FROM information_schema.tables WHERE engine='InnoDB';

Затем я вычел InnoDBDataIndexSpace из суммы размеров файлов для ibdata1 и ibdata2. Я получил две вещи, которые потрясли меня

  • В системном табличном пространстве осталось 106 ГБ.
  • Я получил это же Table is Fullсостояние

Это означает, что 106 ГБ использовалось для внутренней сантехники InnoDB. В то время клиент использовал ext3.

Решением для меня было добавить ibdata3. Я обсуждал это в моем старом посте. Как решить "Таблица ... заполнена" с "innodb_file_per_table"?

Я обсуждал это и в других постах

Имейте в виду, что это может произойти, даже если была включена innodb_file_per_table . Как? Откат и журналы отмены являются источником неконтролируемых всплесков роста ibdata1 .

ВАШ АКТУАЛЬНЫЙ ВОПРОС

Поскольку вы добавляете индекс к таблице и получаете Table is Full, таблица должна быть огромной и не может поместиться в один сегмент отката. Вы должны сделать следующее:

ШАГ 01

Получить данные в файл дампа

mysqldump --no-create-info mydb mytable > table_data.sql

ШАГ 02

Войдите в MySQL и запустите этот

USE mydb
CREATE TABLE mytable_new LIKE mytable;
ALTER TABLE mytable_new ADD INDEX ... ;
ALTER TABLE mytable RENAME mytable_old;
ALTER TABLE mytable_new RENAME mytable;

ШАГ 03

Загрузите таблицу с дополнительным индексом с данными

mysql -Dmydb < table_data.sql

Это все.

Видите, проблема в том, что ALTER TABLE попытается внедрить все строки в вашей огромной таблице как одну транзакцию. Использование mysqldump вставит данные в таблицу (теперь с новым индексом) тысячи строк за раз, а не все строки в одной транзакции.

Не беспокойтесь о what if this doesn't work?Первоначальная таблица будет названа mytable_oldв случае чего-либо. это может служить резервной копией. Вы можете удалить резервную копию, когда вы знаете, что новая таблица работает для вас.

Попробуйте!

ОБНОВЛЕНИЕ 2014-06-16 11:13 EDT

Если вы беспокоитесь об огромном сбросе данных, просто распакуйте его.

Вы можете сделать те же шаги, но следующим образом

ШАГ 01

Получить данные в файл дампа

mysqldump --no-create-info mydb mytable | gzip > table_data.sql.gz

ШАГ 02

Войдите в MySQL и запустите этот

USE mydb
CREATE TABLE mytable_new LIKE mytable;
ALTER TABLE mytable_new ADD INDEX ... ;
ALTER TABLE mytable RENAME mytable_old;
ALTER TABLE mytable_new RENAME mytable;

ШАГ 03

Загрузите таблицу с дополнительным индексом с данными

gzip -d < table_data.sql.gz | mysql -Dmydb

или

gunzip < table_data.sql.gz | mysql -Dmydb

ОБНОВЛЕНИЕ 2014-06-16 12:55 ПО ВОСТОЧНОМУ ВРЕМЕНИ

Я просто подумал о другом аспекте в этом вопросе.

Поскольку вы выполняете DDL, а не DML, возможно, это не внутренняя система InnoDB. Поскольку DDL не может выполнить откат для InnoDB , проблема должна быть связана с внешним подключением. Где этот внешний водопровод засорен? Я подозреваю, что временная папка для ОС. Почему?

140616 13:04:33  InnoDB: Error: Write to file (merge) failed at offset 3 1940914176.
InnoDB: 1048576 bytes should have been written, only 970752 were written.
InnoDB: Operating system error number 0.
InnoDB: Check that your OS and file system support files of this size.
InnoDB: Check also that the disk is not full or a disk quota exceeded.
InnoDB: Error number 0 means 'Success'.
InnoDB: Some operating system error numbers are described at
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/operating-system-error-codes.html
140616 13:04:33 [ERROR] /usr/libexec/mysqld: The table 'my_table' is full

Видишь disk quota exceeded? Куда навязывается эта дисковая квота?

Запустите этот запрос

SHOW GLOBAL VARIABLES LIKE 'tmpdir';

Вы сказали, что это /var/lib/mysqltmp

Теперь запустите это в ОС

df -h /var/lib/mysqltmp

Что-то подсказывает мне, что места для /var/lib/mysqltmpне хватает, в конце концов, у вас есть только 14G бесплатно. В глазах DDL (для создания индекса) произошел откат не в файле ibdata1, а в tmpdirместоположении. Если /var/lib/mysqltmpнигде не монтируется, то временные данные записываются в корневой раздел. Если /var/lib/mysqltmpгде-то монтируется, то это монтирование заполняется данными строки. В любом случае недостаточно места для завершения DDL.

У вас есть два варианта здесь

ОПЦИЯ 1

Вы также можете создать большой диск (возможно, с объемом 100 ГБ) и смонтировать его /var/lib/mysqltmpна этом большом диске.

ВАРИАНТ № 2

Мои трехэтапные предложения должны работать, даже если у вас ограниченное дисковое пространство

КОММЕНТАРИЙ

Обидно, что сообщение об ошибке, которое вы отправили, говорит

InnoDB: Operating system error number 0.
InnoDB: Error number 0 means 'Success'.

Это означает, что ОС просто отлично. Также не существует никакого связанного номера ошибки для этой ситуации.

Есть еще одна вещь, которую вы должны знать. Документация MySQL говорит, что Быстрое Создание Индекса для InnoDB все еще идет на диск :

Во время создания индекса файлы записываются во временный каталог ($ TMPDIR в Unix,% TEMP% в Windows или значение переменной конфигурации --tmpdir). Каждый временный файл достаточно велик для размещения одного столбца, составляющего новый индекс, и каждый из них удаляется, как только он объединяется с окончательным индексом.

Из-за ограничений MySQL таблица копируется, вместо того, чтобы использовать «Быстрое создание индекса» при создании индекса в TEMPORARY TABLE. Это было сообщено как MySQL Bug # 39833 .

ОБНОВЛЕНИЕ 2014-06-16 13:58 ПО ВОСТОЧНОМУ ВРЕМЕНИ

[mysqld]
datadir                         = /mnt/cbsvolume1/var/lib/mysql
tmpdir                          = /mnt/cbsvolume1/var/lib/mysql

Пожалуйста, убедитесь, что /mnt/cbsvolume1/var/lib/mysql100G или более бесплатно

RolandoMySQLDBA
источник
Мне потребовались недели, чтобы составить таблицу в первую очередь из дампа (данные 85 ГБ). Есть ли другой способ, кроме как делать это снова и снова? Не могли бы вы порекомендовать разделить таблицу?
Noam
Разделение не поможет, потому что ALTER TABLE для добавления индекса все равно попытается загрузить все в одной транзакции. Помните, что вы боретесь с архитектурой InnoDB, а не с дисковым пространством. Мой ответ должен помочь вам в этом случае, поскольку mysqldump вставляет несколько тысяч строк на INSERT, а не вставку «все или ничего» во временную таблицу с возможностью отката.
RolandoMySQLDBA
Но создание этой таблицы (снова) займет вечность. Любой способ запустить создание индекса при отключении опции отката?
Noam
Создание индекса - это DDL, а не DML. Вы не можете изменить поведение DDL. Вот почему мой пост использует методы DML.
RolandoMySQLDBA
Кстати, сколько строк в таблице ???
RolandoMySQLDBA