Como encontrar e corrigir tabelas MySQL fragmentadas

27

Eu usei o MySQLTuner, que apontou que algumas tabelas estavam fragmentadas. eu usei

mysqlcheck --optimize -A

otimizar todas as tabelas. Consertou algumas tabelas, mas o MySQLTuner ainda encontra 19 tabelas fragmentadas. como posso ver quais tabelas precisam de desfragmentar? Talvez OPTIMIZE TABLE funcione onde o mysqlcheck não funcionou? Ou o que mais devo tentar?

curiouscat
fonte
1
Eu tenho um problema similar. Estou configurando um novo banco de dados com o MySQL 5.5 e certas tabelas do InnoDB nunca desfragmentam. Gostaria de saber se a verificação Data_free (mostrada na resposta de KayakJim) está incorreta nas tabelas do InnoDB.
docwhat

Respostas:

38

a resposta curta:

select  ENGINE, TABLE_NAME,Round( DATA_LENGTH/1024/1024) as data_length , round(INDEX_LENGTH/1024/1024) as index_length, round(DATA_FREE/ 1024/1024) as data_free from information_schema.tables  where  DATA_FREE > 0;

A resposta "Você deve saber"

Em primeiro lugar, você deve entender que as tabelas Mysql são fragmentadas quando uma linha é atualizada, portanto é uma situação normal. Quando uma tabela é criada, digamos importada usando um dump com dados, todas as linhas são armazenadas sem fragmentação em muitas páginas de tamanho fixo. Quando você atualiza uma linha de comprimento variável, a página que contém esta linha é dividida em duas ou mais páginas para armazenar as alterações, e essas novas duas (ou mais) páginas contêm espaços em branco preenchendo o espaço não utilizado.

Isso não afeta o desempenho, a menos que a fragmentação cresça demais. O que é muita fragmentação, bem, vamos ver a consulta que você está procurando:

  select  ENGINE, TABLE_NAME,Round( DATA_LENGTH/1024/1024) as data_length , round(INDEX_LENGTH/1024/1024) as index_length, round(DATA_FREE/ 1024/1024) as data_free from information_schema.tables  where  DATA_FREE > 0;

DATA_LENGTH e INDEX_LENGTH são o espaço que seus dados e índices estão usando, e DATA_FREE é a quantidade total de bytes não utilizados em todas as páginas da tabela (fragmentação).

Aqui está um exemplo de uma tabela de produção real

| ENGINE | TABLE_NAME               | data_length | index_length | data_free |
| InnoDB | comments                 |         896 |          316 |         5 |

Nesse caso, temos uma tabela usando (896 + 316) = 1212 MB e temos dados em um espaço livre de 5 MB. Isso significa uma "taxa de fragmentação" de:

5/1212 = 0.0041

... Qual é uma "taxa de fragmentação" realmente baixa.

Eu tenho trabalhado com tabelas com uma proporção próxima de 0,2 (ou seja, 20% dos espaços em branco) e nunca percebi uma lentidão nas consultas, mesmo se eu otimizar a tabela, o desempenho será o mesmo. Mas aplicar uma tabela de otimização em uma tabela de 800 MB leva muito tempo e bloqueia a tabela por vários minutos, o que é impraticável na produção.

Portanto, se você considerar o que ganha em desempenho e o tempo perdido em otimizar uma tabela, prefiro NÃO OTIMIZAR.

Se você acha que é melhor para armazenamento, veja sua proporção e quanto espaço você pode economizar ao otimizar. Geralmente não é muito, então eu prefiro NÃO OTIMIZAR.

E se você otimizar, a próxima atualização criará espaços em branco dividindo uma página em duas ou mais. Mas é mais rápido atualizar uma tabela fragmentada do que uma não fragmentada, porque se a tabela estiver fragmentada, uma atualização em uma linha não necessariamente dividirá uma página.

Espero que isso ajude você.

Felipe Rojas
fonte
1
Embora seja uma resposta de vários anos atrás, pensei em apontar que o data_free é uma estatística para todo o espaço de tabela, não para a respectiva tabela. Se você armazenar várias tabelas juntas em um espaço de tabela, o data_free pode induzir você a acreditar que a tabela precisa de desfragmentação, quando isso significa apenas que existem extensões livres no espaço de tabela. A execução da tabela de otimização não reduzirá as extensões livres. Desfragmentar a tabela pode até aumentar as extensões livres.
Bill Karwin
14

Apenas para adicionar à resposta de Felipe-Rojas, você pode calcular a proporção de fragmentos como parte da consulta:

select ENGINE,
  concat(TABLE_SCHEMA, '.', TABLE_NAME) as table_name,
  round(DATA_LENGTH/1024/1024, 2) as data_length,
  round(INDEX_LENGTH/1024/1024, 2) as index_length,
  round(DATA_FREE/1024/1024, 2) as data_free,
  (data_free/(index_length+data_length)) as frag_ratio
FROM information_schema.tables
WHERE DATA_FREE > 0
ORDER BY frag_ratio DESC;

Se uma tabela estiver fragmentada com uma pequena porcentagem (menos de 5%?), Provavelmente você poderá deixá-la em paz.

Qualquer coisa maior e você precisará avaliar com base no uso do banco de dados, nas tabelas de bloqueio, etc., quanto à importância de desfragmentar a tabela.

sysadmiral
fonte
2

A tabela Otimizar, de fato, resolverá o problema que você está tendo.

Se você tiver apenas alguns bancos de dados, poderá usar o PHPMyAdmin para percorrer todos os seus bancos de dados. Selecione as tabelas com sobrecarga e, em seguida, selecione para otimizar.

Se você tiver muitos bancos de dados, provavelmente será preferível outro método.

Eu uso a seguinte configuração de script PHP no cron para executar a cada hora.

$DB = new mysqli ('localhost', 'DbUser', 'DbPassword');
$results = $DB->query('show databases');
$allDbs = array();
while ($row = $results->fetch_array(MYSQLI_NUM))
{
    $allDbs[] = $row[0];
}
$results->close();
foreach ($allDbs as $dbName)
{
    if ($dbName != 'information_schema' && $dbName != 'mysql')
    {
        $DB->select_db($dbName);
        $results = $DB->query('SHOW TABLE STATUS WHERE Data_free > 0');
        if ($results->num_rows > 0)
        {
            while ($row = $results->fetch_assoc())
            {
                $DB->query('optimize table ' . $row['Name']);
            }
        }
        $results->close();
    }
}
$DB->close();
Daemon of Chaos
fonte
3
Eu tenho certeza que mysqlcheck --optimize -Aé o mesmo que o SQLOPTIMIZE TABLE <tablename>;
docwhat
2

Me deparei com esta página e achei as perguntas de Felipe-Rojas e sysadmiral muito úteis. Mas, no meu caso, eu estava executando a consulta no phpMyAdmin do WHM e obter apenas TABLE_NAME não foi tão útil, pois o banco de dados não estava listado e vários bancos de dados têm os mesmos nomes de tabela. Portanto, simplesmente adicionar TABLE_SCHEMAtambém fornecerá essa coluna.

select  ENGINE, TABLE_SCHEMA, TABLE_NAME, Round( DATA_LENGTH/1024/1024) as data_length , round(INDEX_LENGTH/1024/1024) as index_length, round(DATA_FREE/ 1024/1024) as data_free, (data_free/(index_length+data_length)) as frag_ratio from information_schema.tables  where  DATA_FREE > 0 order by frag_ratio desc

Mostra DB

ENGINE  | TABLE_SCHEMA  | TABLE_NAME    | data_length   | index_length  | data_free | frag_ratio

InnoDB  | db_name       | db_table      | 0             | 0             | 8         | 170.6667

Para "consertar", usei o link da tabela Defragment no phpMyAdmin para cada uma das tabelas que resultaram em "frag_ratio" alto para o qual o phpMyAdmin executa:

ALTER TABLE `table_name` ENGINE = InnoDB;
Chris
fonte
0

Uma tabela usando o InnoDB Engine do MySQL basicamente nunca precisa ser OPTIMIZEd.

O valor Data_freeda partir de qualquer information_schema.tablesou SHOW TABLE STATUSé muitas vezes diferente de zero, mesmo quando você acha que tem feito tudo o que você pode fazer desfragmentação sua mesa (s). Além disso, essa métrica é apenas uma das várias fragmentações que podem e ocorrem. (Além disso, espaço desperdiçado em blocos, desfazer listas, indexar BTrees versus dados BTrees, etc., etc.

E innodb_file_per_tablecomplica o uso de Data_free. Se a tabela estiver dentro ibdata1, Data_freerefere-se a todo o espaço de tabela; um número bastante inútil. Se a tabela estiver em seu próprio .ibdarquivo, é provável que tenha alguns MB ou alguns por cento do tamanho da tabela, o que for maior.

Somente se você excluiu muitas linhas e não pretende recarregar a tabela, pode valer a pena executar OPTIMIZE TABLE.

PARTITIONstambém mostra uma quantidade perturbadora de Data_free, uma vez que cada partição normalmente mostra 4-7 MB "livre". E isso não vai embora.

Por que desfragmentar?

  • Para devolver espaço ao sistema operacional? Bem, você pode conseguir isso brevemente, se tivesse innodb_file_per_table=1. Mas, à medida que você adiciona linhas, você as recupera do sistema operacional.
  • Para acelerar o acesso? Esqueça. O layout dos blocos no disco é relativamente aleatório e existe nas últimas décadas. Há meio século, era um pouco importante reorganizar os blocos.
  • Reequilibrar BTrees? Tão? Eles ficarão prontamente desequilibrados novamente. O estado estacionário para BTrees que são inseridos aleatoriamente é de 69%. E isso nem é levado em consideração Data_free.
  • MySQLTuner diz para? Esse produto precisa relaxar.

Uma nota de história. Quando eu estava ajudando DBAs com principalmente tabelas MyISAM, descobri talvez 2 entre 1.000 tabelas que eram ajudadas mensalmente OPTIMIZE . Desde então, trabalhei com milhares de tabelas do InnoDB e ainda encontrei um problema de desempenho que provavelmente seria ajudado OPTIMIZE. (Claro, houve problemas de espaço em disco que OPTIMIZEpodem ajudar, mas isso é complicado - geralmente o DBA não tem espaço em disco suficiente para executar OPTIMIZE!)

Rick James
fonte