Come trovare e riparare tabelle MySQL frammentate


27

Ho usato MySQLTuner che ha sottolineato che alcuni tavoli erano frammentati. ero solito

mysqlcheck --optimize -A

per ottimizzare tutte le tabelle. Ha corretto alcune tabelle ma MySQLTuner trova ancora 19 tabelle frammentate. come posso vedere quali tabelle necessitano di deframmentazione? Forse OPTIMIZE TABLE funzionerà dove mysqlcheck non ha funzionato? O cos'altro dovrei provare?


1
Ho un problema simile. Sto configurando un nuovo DB con MySQL 5.5 e alcune tabelle di InnoDB non vengono mai frammentate. Mi chiedo se il controllo Data_free (mostrato nella risposta di KayakJim) non sia corretto con le tabelle InnoDB.
docwhat

Risposte:


38

la risposta breve:

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 risposta "Devi sapere"

prima di tutto devi capire che le tabelle Mysql vengono frammentate quando una riga viene aggiornata, quindi è una situazione normale. Quando viene creata una tabella, diciamo importata usando un dump con dati, tutte le righe vengono archiviate senza frammentazione in molte pagine di dimensioni fisse. Quando si aggiorna una riga di lunghezza variabile, la pagina contenente questa riga viene divisa in due o più pagine per memorizzare le modifiche e queste nuove due (o più) pagine contengono spazi vuoti che riempiono lo spazio inutilizzato.

Ciò non influisce sulle prestazioni, a meno che ovviamente la frammentazione non cresca troppo. Cos'è troppa frammentazione, bene vediamo la query che stai cercando:

  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 sono lo spazio utilizzato dai dati e dagli indici e DATA_FREE è la quantità totale di byte non utilizzati in tutte le pagine della tabella (frammentazione).

Ecco un esempio di una vera tabella di produzione

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

In questo caso abbiamo una tabella che utilizza (896 + 316) = 1212 MB e disponiamo di uno spazio libero di dati di 5 MB. Ciò significa un "rapporto di frammentazione" di:

5/1212 = 0.0041

... Che è un "rapporto di frammentazione" davvero basso.

Ho lavorato con tabelle con un rapporto vicino a 0,2 (che significa il 20% di spazi vuoti) e non ho mai notato un rallentamento delle query, anche se ottimizzo la tabella, le prestazioni sono le stesse. Ma applicare una tabella di ottimizzazione su una tabella da 800 MB richiede molto tempo e blocca la tabella per diversi minuti, il che è impraticabile per la produzione.

Quindi, se consideri ciò che vinci in termini di prestazioni e il tempo sprecato nell'ottimizzare un tavolo, preferisco NON OTTIMIZZARE.

Se ritieni che sia meglio per l'archiviazione, vedi il tuo rapporto e vedi quanto spazio puoi risparmiare quando ottimizzi. Di solito non è troppo, quindi preferisco NON OTTIMIZZARE.

E se ottimizzi, il prossimo aggiornamento creerà spazi vuoti suddividendo una pagina in due o più. Ma è più veloce aggiornare una tabella frammentata rispetto a una non frammentata, perché se la tabella è frammentata un aggiornamento su una riga non necessariamente dividerà una pagina.

Spero che questo ti aiuta.


1
Sebbene questa sia una risposta di diversi anni fa, ho pensato di sottolineare che data_free è una stat per l'intero tablespace, non per la rispettiva tabella. Se si memorizzano più tabelle insieme in un tablespace, data_free può indurre in errore a credere che la tabella debba essere deframmentata, quando significa solo che ci sono estensioni libere nel tablespace. L'esecuzione della tabella di ottimizzazione non ridurrà le estensioni libere. La deframmentazione della tabella può persino aumentare le estensioni libere.
Bill Karwin,

14

Solo per aggiungere alla risposta di Felipe-Rojas è possibile calcolare il rapporto frammento come parte della query:

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 una tabella è frammentata in una piccola percentuale (inferiore al 5%?), Probabilmente puoi lasciarla da sola.

Qualunque cosa sia più grande e dovrai valutare in base all'utilizzo del tuo db, al blocco delle tabelle ecc. Su quanto sia importante deframmentare la tabella.


2

Optimize Table risolverà davvero il problema riscontrato.

Se hai solo pochi database, puoi usare PHPMyAdmin per esaminare tutti i tuoi database. Seleziona le tabelle con overhead e quindi seleziona per ottimizzare.

Se si dispone di molti database, sarebbe probabilmente preferibile un altro metodo.

Uso la seguente configurazione di script PHP in cron per eseguire ogni ora.

$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();

3
Sono abbastanza sicuro che mysqlcheck --optimize -Aè lo stesso di SQLOPTIMIZE TABLE <tablename>;
docwhat

2

Mi sono imbattuto in questa pagina e ho trovato molto utili le domande di Felipe-Rojas e del smiradal. Ma nel mio caso, stavo eseguendo la query in phpMyAdmin di WHM e ottenere solo TABLE_NAME non era altrettanto utile poiché il database non era elencato e diversi database hanno gli stessi nomi di tabella. Quindi, semplicemente aggiungendo TABLE_SCHEMAfornirà anche quella colonna.

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

Per "correggere" ho usato il link della tabella Deframmenta in phpMyAdmin per ciascuna delle tabelle che ha dato luogo a "frag_ratio" elevato per il quale phpMyAdmin esegue:

ALTER TABLE `table_name` ENGINE = InnoDB;

0

Una tabella che utilizza il motore InnoDB di MySQL non deve mai esserlo OPTIMIZEd.

Il valore di Data_freeda o information_schema.tableso SHOW TABLE STATUSè spesso diverso da zero, anche quando pensi di aver fatto tutto ciò che puoi fare per deframmentare le tue tabelle. Inoltre, quella metrica è solo una delle numerose frammentazioni che possono e si verificano. (Inoltre, spazio sprecato in blocchi, elenchi di annullamenti, indice BTree vs dati BTree, ecc., Ecc.

E innodb_file_per_tablecomplica l'uso di Data_free. Se la tabella è in ibdata1, si Data_freeriferisce all'intero tablespace; un numero piuttosto inutile. Se la tabella si trova nel proprio .ibdfile, è probabile che sia di qualche MB o di qualche percento della dimensione della tabella, a seconda di quale sia la dimensione più grande.

Solo se hai eliminato molte righe e non intendi riempire la tabella, potrebbe valerne la pena eseguirlo OPTIMIZE TABLE.

PARTITIONsmostra anche una quantità inquietante di Data_free, poiché ogni partizione in genere mostra 4-7 MB "liberi". E questo non andrà via.

Perché deframmentare?

  • Per restituire spazio al sistema operativo? Bene, potresti farlo brevemente se lo avessi innodb_file_per_table=1. Ma quando aggiungi le righe, le riprenderai dal sistema operativo.
  • Per accelerare l'accesso? Dimenticalo. La disposizione dei blocchi su disco è relativamente casuale, ed è stata negli ultimi decenni. Mezzo secolo fa, era in qualche modo importante riorganizzare i blocchi.
  • Per riequilibrare BTrees? Così? Presto saranno nuovamente sbilanciati. Lo stato stazionario per BTrees che viene inserito casualmente è del 69%. E questo non è nemmeno preso in considerazione Data_free.
  • MySQLTuner dice a? Quel prodotto deve raffreddarsi.

Una nota di storia. Quando stavo aiutando i DBA con principalmente le tabelle MyISAM, ho scoperto forse 2 delle 1000 tabelle che sono state aiutate da un mese OPTIMIZE . Da allora, ho lavorato con migliaia di tabelle InnoDB, ho ancora trovato un problema di prestazioni che probabilmente sarebbe stato aiutato da OPTIMIZE. (Certo, ci sono stati problemi di spazio su disco per i quali OPTIMIZEpotrebbe essere d'aiuto, ma questo diventa complicato - di solito il DBA non ha abbastanza spazio su disco per funzionare OPTIMIZE!)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.