A criação do índice MySQL falhando na tabela está cheia

10

UPDATE: tl; dr: O problema foi que o MySQL usa TMPDIRao criar índices. E o meu TMPDIRfoi ficar sem espaço em disco.

Q original:

Estou tentando adicionar um índice a uma tabela do InnoDB e obtendo um table is full error. Eu tenho espaço em disco suficiente e a configuração do MySQL possui um arquivo por tabela = 1. Os dados da tabela são de 85 GB e presumo que o índice esteja entre 20 e 30 GB e tenho muito mais espaço em disco do que isso. Também estou usando ext3, então não sinto nenhum problema com o limite de tamanho do arquivo do ponto de vista do SO.

O erro registrado é assim:

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

O que está causando isso e como posso resolver?

A tabela de criação:

`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`

Como os dados são 87,4 GB e eu estimo que haja cerca de 1,5B linhas.

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% /
Noam
fonte
11
"InnoDB: Verifique se o seu sistema operacional e sistema de arquivos suportam arquivos desse tamanho. InnoDB: Verifique também se o disco não está cheio ou se uma cota de disco foi excedida." - você verificou isso? Você está usando ext2? ext3? xfs? Verificou as cotas para o usuário?
Philᵀᴹ
@ Phil Verificado ambos. Estou usando o ext3 e tenho quase certeza de que não há problema com o espaço em disco. O palpite é que isso tem algo a ver com um log atingindo seu limite ou algo assim, mas nenhuma idéia realmente de como verificar e corrigir.
Noam
Gostaria de saber o que é o arquivo "(mesclagem)"?
akuzminsky
@akuzminsky hmm não faço ideia. Eu apenas executei uma nova criação de índice do PHPMyAdmin.
Noam
+1 para TMPDIR, esse foi o problema no meu servidor também.
Chad E.

Respostas:

8

Por favor, não se deixe enganar pela mensagem de erro The table 'my_table' is full. Esse cenário raro não tem absolutamente nada a ver com espaço em disco. Esta condição de tabela completa tem a ver com o encanamento interno do InnoDB.

Primeiro, dê uma olhada neste diagrama da arquitetura InnoDB

Arquitetura InnoDB

Observe que o espaço de tabela do sistema (o arquivo ibdata) possui apenas 128 segmentos de reversão e 1023 slots de reversão por segmento de reversão. Isso limita o tamanho da capacidade de reversão de uma transação. Em outras palavras, se um único segmento de reversão precisar de mais de 1023 slots para suportar uma transação, a transação atingirá essa table is fullcondição.

Pense no restaurante Red Lobster, em Nova Jersey . Pode ter capacidade para 200 pessoas. Se o restaurante estiver cheio, uma fila de pessoas poderá sair para esperar. Se as pessoas na fila ficarem impacientes, elas poderão sair porque o restaurante está cheio. Obviamente, a solução não seria aumentar Nova Jersey (ou obter mais espaço em disco). A solução seria aumentar o restaurante Red Lobster. Dessa forma, você pode aumentar a capacidade de assentos para, digamos, 240. Mesmo assim, uma fila pode se formar do lado de fora se mais de 240 pessoas decidirem vir para a Red Lobster.

Só para dar um exemplo, eu tinha um cliente com 2TB de espaço de tabela do sistema e innodb_file_per_table foi desativado. (346G para ibdata1, a redefinição para ibdata2). Eu executei esta consulta

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

Em seguida, subtraí InnoDBDataIndexSpace da soma dos tamanhos de arquivo para ibdata1 e ibdata2. Eu tenho duas coisas que me chocaram

  • Restavam 106 GB dentro do espaço de tabela do sistema.
  • Eu tenho essa mesma Table is Fullcondição

Isso significa que os 106 GB estavam sendo usados ​​para o encanamento interno do InnoDB. O cliente estava usando ext3 no momento.

A solução para mim foi adicionar ibdata3. Eu discuti isso no meu post antigo Como resolver "A tabela ... está cheia" com "innodb_file_per_table"?

Eu discuti isso em outros posts também

Lembre-se de que essa condição pode ocorrer mesmo se innodb_file_per_table estiver ativado. Como? Rollbacks e Undo Logs são a fonte de picos não controlados e o crescimento do ibdata1 .

SUA PERGUNTA REAL

Como você está adicionando um índice a uma tabela e obtendo Table is Full, a tabela deve ser enorme e não pode caber em um único segmento de reversão. Você deve fazer o seguinte:

PASSO 01

Coloque os dados em um arquivo de despejo

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

PASSO 02

Entre no MySQL e execute este

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;

PASSO 03

Carregue a tabela com o índice adicional com os dados

mysql -Dmydb < table_data.sql

Isso é tudo.

Veja, o problema é que o ALTER TABLE tentará injetar todas as linhas da sua enorme tabela como uma única transação. Usar o mysqldump irá inserir os dados na tabela (agora com um novo índice) milhares de linhas por vez, nem todas as linhas em uma única transação.

Não se preocupe. what if this doesn't work?A tabela original será nomeada mytable_oldem caso de algo. pode servir como um backup. Você pode descartar o backup quando souber que a nova tabela funciona para você.

De uma chance !!!

ATUALIZAÇÃO 16/06/2014 - 11:13

Se você está preocupado com o despejo de dados maior, basta compactá-lo.

Você pode executar as mesmas etapas, mas da seguinte maneira

PASSO 01

Coloque os dados em um arquivo de despejo

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

PASSO 02

Entre no MySQL e execute este

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;

PASSO 03

Carregue a tabela com o índice adicional com os dados

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

ou

gunzip < table_data.sql.gz | mysql -Dmydb

ATUALIZAÇÃO 16/06/2014 - 12:55

Acabei de pensar em outro aspecto em relação a esta questão.

Como você está executando DDL e não DML, é possível que isso não seja o encanamento interno do InnoDB. Como o DDL não pode reverter para o InnoDB , o problema deve ser o encanamento externo. Onde esse encanamento externo está entupido? Eu suspeito que a pasta temporária para o sistema operacional. Por quê?

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

Veja o disk quota exceeded? Onde essa cota de disco está sendo imposta?

Execute esta consulta

SHOW GLOBAL VARIABLES LIKE 'tmpdir';

Você disse que é /var/lib/mysqltmp

Agora, execute isso no sistema operacional

df -h /var/lib/mysqltmp

Algo me diz que o espaço para /var/lib/mysqltmpestava se esgotando Afinal, você só tem 14G de graça. Aos olhos do DDL (para criar o índice), ocorreu uma reversão, não no arquivo ibdata1, mas no tmpdirlocal. Se /var/lib/mysqltmpnão estiver montado em nenhum lugar, os dados temporários serão gravados na partição raiz. Se /var/lib/mysqltmpestiver montado em algum lugar, essa montagem será preenchida com dados de linha. Em ambos os casos, não há espaço suficiente para concluir o DDL.

Você tem duas opções aqui

OPÇÃO 1

Você também pode criar um disco grande (talvez com mais de 100 GB) e montar /var/lib/mysqltmpnesse disco grande.

OPÇÃO 2

Minhas sugestões em três etapas ainda devem funcionar, mesmo com o espaço em disco limitado que você possui

COMENTÁRIO

É uma pena a mensagem de erro que você postou diz

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

Isso significa que o sistema operacional está bem. Também não existe nenhum número de erro associado a essa situação.

Há outra coisa que você deve saber. A documentação do MySQL diz que a criação rápida de índice para o InnoDB ainda vai para o disco :

Durante a criação do índice, os arquivos são gravados no diretório temporário ($ TMPDIR no Unix,% TEMP% no Windows ou o valor da variável de configuração --tmpdir). Cada arquivo temporário é grande o suficiente para conter uma coluna que compõe o novo índice e cada um é removido assim que é mesclado no índice final.

Devido a uma limitação do MySQL, a tabela é copiada, em vez de usar “Fast Index Creation” quando você cria um índice em uma TEMPORARY TABLE. Isso foi relatado como Bug # 39833 do MySQL .

ATUALIZAÇÃO 16/06/2014 - 13:58 EDT

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

Certifique-se de /mnt/cbsvolume1/var/lib/mysqlter 100G ou mais grátis

RolandoMySQLDBA
fonte
Levei semanas para criar a tabela em primeiro lugar a partir do despejo (dados de 85 GB). Existe outra maneira além de fazer isso de novo? Você recomendaria talvez particionar a tabela?
Noam
O particionamento não ajudará porque a ALTER TABLE para adicionar o índice ainda tentaria carregar tudo em uma única transação. Lembre-se, você está lutando contra a arquitetura InnoDB, não o espaço em disco. Minha resposta deve ajudá-lo neste caso, já que o mysqldump injeta alguns milhares de linhas por INSERT, não uma inserção de tudo ou nada em uma tabela temporária com a possibilidade de uma reversão.
RolandoMySQLDBA 16/06
Mas a criação dessa tabela (novamente) levará uma eternidade. Alguma maneira de executar um índice de criação enquanto desabilita a opção de reversão?
Noam
Criar um índice é DDL, não DML. Você não pode alterar o comportamento do DDL. É por isso que meu post usa técnicas de DML.
RolandoMySQLDBA 16/06
Entre quantas linhas a tabela possui ???
RolandoMySQLDBA