Aller au contenu principal


BdD : Optimiser l'espace occupé (tables innodb)

Sur un des serveurs 1and1 (Ionos) hébergeant une de mes bases de données, je me suis heurté à un problème de dépassement de capacité de stockage avec pour conséquence le verrouillage des accès utilisateurs à mon site. Le manque de clarté de l'aide en ligne de mon hébergeur sur ce sujet, m'a conduit à l'écriture de ce paragraphe.
En effet, il s'agit pour optimiser l'espace occupé par les tables de la BdD, d'exécuter la requête affichée dans l'onglet " Lister table" ci-dessous, et cela de manière automatique avec le service CRON fourni par ce serveur.

Billet créé le :
24 Mai 2022

1. Les requêtes

La requête SQL de l'onglet "Lister tables" ci-dessous fournit la liste de tables dont il nous faut optimiser l'espace occupé.
L'onglet "Exemple de résultat" illustre le résultat produit par cette requête sur la base "dbs34" qu'il faut optimiser. Ce résultat est le code SQL qu'il nous faudra exécuter pour optimiser l'espace occupé par cette BdD.
L'onglet "MyCron.php" est un programme, écrit en php. La boucle "while {..}" de ce script exécute pour chaque table dépassant le seuil des 60%, la requête "ALTER TABLE .... FORCE" qui optimise l'espace.

Ce programme devant être exécuté régulièrement, il sera placé dans une tâche CRON.

Cliquez sur le bouton pour copier le code dans le presse papier
SELECT concat('ALTER TABLE `', table_schema, '`.`', table_name, '` FORCE;')
            FROM information_schema.tables
            WHERE data_free / (data_length + index_length + data_free) > 0.6
            ORDER BY data_free DESC;
                     
Cliquez sur le bouton pour copier le code dans le presse papier
<?php
                header('Content-Type: text/html; charset=UTF-8');
                $hostname = 'localhost';
                $database = 'dbs34';
                $username = 'root';
                $password = '';
             
                $mysqli = new mysqli($hostname, $username, $password, $database);
                // vérifier la connection
                if (mysqli->connect_error) {
                    die("Failed to connect to MySQL:" . mysqli->connect_error);
                }
                // Change character set to utf8
                mysqli->set_charset("utf8");
                //------------------------------------------------------------------------------------------------
                // Interrogation de la base pour créer la liste des commandes à exécuter pour optimiser les tables
                //------------------------------------------------------------------------------------------------
                $sql = "SELECT concat('ALTER TABLE `', table_schema, '`.`', table_name, '` FORCE;')
                       FROM information_schema.tables
                       WHERE data_free / (data_length + index_length + data_free) > 0.6
                       ORDER BY data_free DESC"    ;
                
                $result = $mysqli
                            ->query($sql);
                $data = array();  
                if ($result->num_rows > 0) {
                    // output data : la liste des tables à optimiser
                    while($row = $result->fetch_row()) {
                        $sql = $row[0];
                        $data[]= $row;
                        // Optimiser chaque table par exemple : ALTER TABLE `dbs34`.`83batch` FORCE;
                        $result2 = $mysqli
                            ->query($sql);
                        
                    }
                }
                else {
                    //pas de résultat
                    $data[] = "vide";
                };
                $mysqli->close();
                echo json_encode( $data );
            
Cliquez sur le bouton pour copier le code dans le presse papier
ALTER TABLE `dbs34`.`83batch` FORCE;
            ALTER TABLE `dbs34`.`83watchdog` FORCE;
            ALTER TABLE `dbs34`.`83key_value_expire` FORCE;
            ALTER TABLE `dbs34`.`83cache_default` FORCE;
            ALTER TABLE `dbs34`.`83config` FORCE;
            ALTER TABLE `dbs34`.`83search_dataset` FORCE;
            ALTER TABLE `dbs34`.`83locales_target` FORCE;
            ALTER TABLE `dbs34`.`83block_content_revision__body` FORCE;
            ALTER TABLE `dbs34`.`83cache_config` FORCE;
            ALTER TABLE `dbs34`.`83router` FORCE;
            ALTER TABLE `dbs34`.`83locales_source` FORCE;
            ALTER TABLE `dbs34`.`83queue` FORCE;
            ALTER TABLE `dbs34`.`83node__layout_builder__layout` FORCE;
            ALTER TABLE `dbs34`.`83key_value` FORCE;
            ALTER TABLE `dbs34`.`83node_revision__layout_builder__layout` FORCE;
            ALTER TABLE `dbs34`.`83config_export` FORCE;
            ALTER TABLE `dbs34`.`83block_content__body` FORCE;
                 

2.Mise en place d'une tâche CRON

Pour cette mise en place, dans le cas de mon hébergeur 1and1 et de mon abonnement (serveur linux), il me faut accéder en "mode console" à ce serveur puis en utilisant la commande "crontab", créer ou modifier la table listant les tâches automatisée.

Je dois définir la tâche : exécuter le script "MyCron.php" de l'onglet ci-dessus. Ce script étant situé à la racine de mon répertoire privé sur le serveur, la commande à saisir est :

/usr/bin/php7.4-cli $HOME/MyCron.php

Cette commande lance "php7.4-cli", un interpréteur php, avec "$HOME/MyCron.php" pour argument.

Je souhaite que cette tâche soit exécutée tous les jours. J'utilise donc la variable @daily (équivalent de 0 0 * * *). Pour que le résultat du script (produit par l'instruction "echo json.encode($data)") me parvienne, j'initialise le paramètre MAILTO à "daniel@dhumbert.info". Ce qui donne les instructions suivantes à placer dans la table "cron" :

MAILTO="daniel@dhumbert.info"
@daily /usr/bin/php7.4-cli $HOME/MyCron.php

La commande "crontab -e" ouvre la table Cron en mode "édition". La frappe de la touche "i" me met en mode "insertion". Il me reste à taper les instructions vues ci-dessus et de les enregistrer par les frappes sur les touches esc : w q du clavier.

Dans le fichier "MyCron.php" de l'onglet ci-dessus, if faut bien évidemment modifier le contenu des variables
  • $hostname  : nom du serveur hébergeant la BdD (ressemble à dbxxx.hosting-data.io chez 1and1 Ionos)

  • $database  : le nom de la BdD à optimiser

  • $username et $password : l'identifiant et le mot de passe d'un utilisateur ayant accès en modification à la BdD

L'image ci-contre montre le courriel reçu suite à l'exécution de la tâche CRON. Il montre que 3 tables ont été mise à jour :

  • cache_config

  • key_value_expire

  • cache_default

NB : toutes les tables de mon site sont préfixées.

Cliquer pour agrandir l'image

Utilisez la requête SQL suivante pour connaitre :
  • le nom des tables de la base "d15" dont le ratio "espace libre / espace alloué" dépasse les 20% ;
  • la taille des champs :
    • DATA_FREE
    • DATA_LENGTH
    • INDEX_LENGTH
  • la taille de la table en Mo ;

    SELECT
    TABLE_NAME as 'Table',
    DATA_FREE as 'Free',
    DATA_LENGTH as 'Length',
    INDEX_LENGTH as 'Index',
    (DATA_LENGTH + INDEX_LENGTH) / (1024 * 1024) as 'Taille en Mo'
    FROM information_schema.TABLES
    WHERE TABLE_SCHEMA = 'd15'and (DATA_FREE / ( DATA_LENGTH + INDEX_LENGTH + DATA_FREE))> 0.2;

Une valeur élevée de "DATA_FREE" indique que de l'espace alloué n'est pas utilisé. Une valeur excessive indique qu'il faut optimiser le stockage en reconstruisant ou défragmentant la table.

Utilisez la requête SQL suivante pour calculer la taille totale de votre BdD en Mo avec 2 décimales :
SELECT
SUM(ROUND( (( DATA_LENGTH + INDEX_LENGTH ) / 1024 / 1024),2)) AS TailleMo
FROM information_schema.TABLES

Utilisez la requête SQL suivante pour obtenir toutes les informations sur la table "83config" de la base "d11" :
SHOW TABLE STATUS FROM d11 WHERE name='83config'