Comment trouver et réparer des tables MySQL fragmentées

27

J'ai utilisé MySQLTuner qui a souligné que certaines tables étaient fragmentées. j'ai utilisé

mysqlcheck --optimize -A

pour optimiser toutes les tables. Il a corrigé certaines tables mais MySQLTuner trouve toujours 19 tables fragmentées. comment savoir quelles tables ont besoin d'être défragmentées? Peut-être qu'OPTIMIZE TABLE fonctionnera là où mysqlcheck n'a pas fonctionné? Ou quoi d'autre devrais-je essayer?

curieux
la source
1
J'ai le même problème. J'installe une nouvelle base de données avec MySQL 5.5 et certaines tables InnoDB ne défragmentent jamais. Je me demande si la vérification Data_free (montrée dans la réponse de KayakJim) est incorrecte avec les tables InnoDB.
docwhat

Réponses:

38

la réponse courte:

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;

La réponse "Vous devez savoir"

tout d'abord, vous devez comprendre que les tables Mysql sont fragmentées lorsqu'une ligne est mise à jour, c'est donc une situation normale. Lorsqu'une table est créée, disons importée à l'aide d'un vidage avec des données, toutes les lignes sont stockées sans fragmentation dans de nombreuses pages de taille fixe. Lorsque vous mettez à jour une ligne de longueur variable, la page contenant cette ligne est divisée en deux ou plusieurs pages pour stocker les modifications et ces deux nouvelles pages (ou plus) contiennent des espaces vides remplissant l'espace inutilisé.

Cela n'affecte pas les performances, sauf bien sûr si la fragmentation augmente trop. Ce qui est trop de fragmentation, voyons bien la requête que vous recherchez:

  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 et INDEX_LENGTH sont l'espace utilisé par vos données et index, et DATA_FREE est la quantité totale d'octets inutilisés dans toutes les pages de table (fragmentation).

Voici un exemple d'une vraie table de production

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

Dans ce cas, nous avons une table utilisant (896 + 316) = 1212 Mo, et avons des données sur un espace libre de 5 Mo. Cela signifie un "rapport de fragmentation" de:

5/1212 = 0.0041

... Ce qui est un "taux de fragmentation" vraiment faible.

J'ai travaillé avec des tables avec un ratio proche de 0,2 (soit 20% des espaces vides) et je ne remarque jamais de ralentissement des requêtes, même si j'optimise la table, les performances sont les mêmes. Mais appliquer une table d'optimisation sur une table de 800 Mo prend beaucoup de temps et bloque la table pendant plusieurs minutes, ce qui est impraticable en production.

Donc, si vous considérez ce que vous gagnez en performances et le temps perdu à optimiser une table, je préfère NE PAS OPTIMISER.

Si vous pensez que c'est mieux pour le stockage, voyez votre ratio et voyez combien d'espace pouvez-vous économiser en optimisant. Ce n'est généralement pas trop, donc je préfère NE PAS OPTIMISER.

Et si vous optimisez, la prochaine mise à jour créera des espaces vides en divisant une page en deux ou plus. Mais il est plus rapide de mettre à jour une table fragmentée qu'une table non fragmentée, car si la table est fragmentée, une mise à jour sur une ligne ne divisera pas nécessairement une page.

J'espère que ceci vous aide.

Felipe Rojas
la source
1
Bien que ce soit une réponse d'il y a plusieurs années, j'ai pensé que je ferais remarquer que le data_free est une statistique pour tout le tablespace, pas pour la table respective. Si vous stockez plusieurs tables ensemble dans un espace de table, le data_free peut vous induire en erreur pour croire que la table doit être défragmentée, quand cela signifie simplement qu'il y a des extensions libres dans l'espace de table. L'exécution de la table d'optimisation ne réduira pas les extensions libres. La défragmentation de la table peut même augmenter les étendues libres.
Bill Karwin
14

Pour ajouter à la réponse de Felipe-Rojas, vous pouvez calculer le taux de fragmentation dans le cadre de la requête:

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;

Si une table est fragmentée à un faible pourcentage (moins de 5%?), Vous pouvez probablement la laisser seule.

Tout ce qui est plus grand et que vous devrez évaluer en fonction de votre utilisation de la base de données, du verrouillage des tables, etc. quant à l'importance de la défragmentation de la table.

amiral
la source
2

Optimiser la table résoudra en effet le problème que vous rencontrez.

Si vous n'avez que quelques bases de données, vous pouvez utiliser PHPMyAdmin pour parcourir toutes vos bases de données. Sélectionnez les tables avec des frais généraux, puis sélectionnez pour optimiser.

Si vous avez beaucoup de bases de données, une autre méthode serait probablement préférable.

J'utilise la configuration de script PHP suivante dans cron pour exécuter toutes les heures.

$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();
Démon du chaos
la source
3
Je suis presque sûr que mysqlcheck --optimize -Ac'est la même chose que le SQLOPTIMIZE TABLE <tablename>;
docwhat
2

Je suis tombé sur cette page et j'ai trouvé les requêtes de Felipe-Rojas et sysadmiral très utiles. Mais dans mon cas, j'exécutais la requête dans phpMyAdmin de WHM et obtenir uniquement TABLE_NAME n'était pas aussi utile car la base de données n'était pas répertoriée et plusieurs bases de données ont les mêmes noms de table. Donc, simplement l'ajout TABLE_SCHEMAfournira également cette colonne.

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

Affiche DB

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

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

Pour "corriger", j'ai utilisé le lien de la table de défragmentation dans phpMyAdmin pour chacune des tables qui ont abouti à un "frag_ratio" élevé pour lequel phpMyAdmin s'exécute:

ALTER TABLE `table_name` ENGINE = InnoDB;
Chris
la source
0

Une table utilisant le moteur InnoDB de MySQL n'a pratiquement jamais besoin de l'être OPTIMIZEd.

La valeur de Data_freefrom information_schema.tablesou SHOW TABLE STATUSest très souvent non nulle, même si vous pensez avoir fait tout ce que vous pouvez faire pour défragmenter vos tables. De plus, cette métrique n'est qu'une des nombreuses fragmentations qui peuvent se produire et se produisent. (En outre, l'espace perdu dans les blocs, les listes d'annulation, les BTrees d'index vs les BTrees de données, etc., etc.

Et innodb_file_per_tablecomplique l'utilisation de Data_free. Si la table est dedans ibdata1, se Data_freeréfère alors à l'espace de table entier; un nombre plutôt inutile. Si la table se trouve dans son propre .ibdfichier, il est probable qu'elle représente quelques Mo ou quelques pour cent de la taille de la table, la valeur la plus élevée étant retenue.

Ce n'est que si vous avez supprimé de nombreuses lignes et que vous n'avez pas l'intention de recharger la table que cela peut valoir la peine d'être exécuté OPTIMIZE TABLE.

PARTITIONsmontrent également une quantité inquiétante de Data_free, car chaque partition affiche généralement 4-7 Mo "libre". Et cela ne disparaîtra pas.

Pourquoi défragmenter?

  • Pour retourner de l'espace au système d'exploitation? Eh bien, vous pourriez y parvenir brièvement si vous l'aviez fait innodb_file_per_table=1. Mais lorsque vous ajoutez des lignes, vous les récupérez du système d'exploitation.
  • Pour accélérer l'accès? Oublie. La disposition des blocs sur le disque est relativement aléatoire, et ce depuis les dernières décennies. Il y a un demi-siècle, il était quelque peu important de réorganiser les blocs.
  • Pour rééquilibrer BTrees? Alors? Ils redeviendront rapidement déséquilibrés. L'état stable pour les BTrees qui sont insérés de manière aléatoire dans est de 69%. Et cela n'est même pas pris en compte Data_free.
  • MySQLTuner le dit? Ce produit doit refroidir.

Une note d'histoire. Lorsque j'aidais les administrateurs de bases de données avec principalement des tables MyISAM, j'ai découvert peut-être 2 des 1000 tables aidées par un mensuel OPTIMIZE . Depuis lors, j'ai travaillé avec des milliers de tables InnoDB, mais je n'ai pas encore trouvé de problème de performance qui pourrait être résolu OPTIMIZE. (Bien sûr, il y a eu des problèmes d'espace disque pour lesquels cela OPTIMIZEpourrait aider, mais cela devient délicat - généralement le DBA n'a pas assez d'espace disque pour fonctionner OPTIMIZE!)

Rick James
la source