So finden und reparieren Sie fragmentierte MySQL-Tabellen

27

Ich habe MySQLTuner verwendet, das darauf hinweist, dass einige Tabellen fragmentiert sind. ich benutzte

mysqlcheck --optimize -A

um alle Tabellen zu optimieren. Es wurden einige Tabellen repariert, aber MySQLTuner findet immer noch 19 fragmentierte Tabellen. Wie kann ich sehen, welche Tabellen defragmentiert werden müssen? Vielleicht funktioniert OPTIMIZE TABLE dort, wo mysqlcheck es nicht getan hat? Oder was soll ich sonst noch probieren?

neugierige Katze
quelle
1
Ich habe ein ähnliches Problem. Ich richte eine neue Datenbank mit MySQL 5.5 ein und bestimmte InnoDB-Tabellen werden nie unfragmentiert. Ich frage mich, ob der Data_free-Check (in KayakJims Antwort gezeigt) bei InnoDB-Tabellen falsch ist.
docwhat

Antworten:

38

die kurze antwort:

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;

Die Antwort "Sie müssen wissen"

Zunächst müssen Sie verstehen, dass MySQL-Tabellen fragmentiert werden, wenn eine Zeile aktualisiert wird. Dies ist also eine normale Situation. Wenn eine Tabelle erstellt wird, z. B. mithilfe eines Speicherauszugs mit Daten importiert, werden alle Zeilen ohne Fragmentierung in vielen Seiten mit fester Größe gespeichert. Wenn Sie eine Zeile mit variabler Länge aktualisieren, wird die Seite, die diese Zeile enthält, in zwei oder mehr Seiten unterteilt, um die Änderungen zu speichern. Diese neuen zwei (oder mehr) Seiten enthalten Leerzeichen, die den nicht verwendeten Platz ausfüllen.

Dies wirkt sich nicht auf die Leistung aus, es sei denn, die Fragmentierung wächst zu stark. Was zu viel Fragmentierung ist, sehen wir uns die gesuchte Abfrage an:

  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 und INDEX_LENGTH sind der Speicherplatz, den Ihre Daten und Indizes belegen, und DATA_FREE ist die Gesamtmenge der Bytes, die auf allen Tabellenseiten nicht verwendet werden (Fragmentierung).

Hier ist ein Beispiel einer realen Produktionstabelle

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

In diesem Fall haben wir eine Tabelle mit (896 + 316) = 1212 MB und Daten mit einem freien Speicherplatz von 5 MB. Dies bedeutet ein "Fragmentierungsverhältnis" von:

5/1212 = 0.0041

... Das ist ein wirklich niedriger "Fragmentierungsgrad".

Ich habe mit Tabellen mit einem Verhältnis nahe 0,2 gearbeitet (dh 20% der Leerzeichen) und bei Abfragen nie eine Verlangsamung festgestellt, auch wenn ich die Tabelle optimiere, ist die Leistung gleich. Das Anwenden einer Optimierungstabelle auf eine 800-MB-Tabelle nimmt jedoch viel Zeit in Anspruch und blockiert die Tabelle für einige Minuten, was für die Produktion nicht praktikabel ist.

Wenn Sie also überlegen, was Sie an Leistung und Zeit für die Optimierung eines Tisches gewinnen, bevorzuge ich NOT OPTIMIZE.

Wenn Sie der Meinung sind, dass der Speicher besser ist, überprüfen Sie Ihr Verhältnis und wie viel Speicherplatz Sie bei der Optimierung einsparen können. Es ist normalerweise nicht zu viel, deshalb ziehe ich es vor, NICHT OPTIMIEREN.

Und wenn Sie optimieren, werden beim nächsten Update Leerzeichen erstellt, indem Sie eine Seite in zwei oder mehr Seiten aufteilen. Es ist jedoch schneller, eine fragmentierte Tabelle zu aktualisieren, als eine nicht fragmentierte, da bei einer fragmentierten Tabelle eine Aktualisierung einer Zeile nicht unbedingt eine Seite aufteilt.

Ich hoffe das hilft dir.

Felipe Rojas
quelle
1
Obwohl dies eine Antwort von vor einigen Jahren ist, dachte ich, dass data_free eine Statistik für den gesamten Tablespace ist, nicht für die jeweilige Tabelle. Wenn Sie mehrere Tabellen zusammen in einem Tablespace speichern, kann Sie data_free irreführen, dass die Tabelle defragmentiert werden muss, wenn nur freie Speicherbereiche im Tablespace vorhanden sind. Durch das Ausführen von Optimize Table werden die freien Speicherbereiche nicht reduziert. Durch die Defragmentierung der Tabelle können die freien Speicherbereiche sogar vergrößert werden .
Bill Karwin
14

Um die Antwort von Felipe-Rojas zu ergänzen, können Sie das Fragmentverhältnis als Teil der Abfrage berechnen:

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;

Wenn eine Tabelle zu einem kleinen Prozentsatz fragmentiert ist (weniger als 5%?), Können Sie sie wahrscheinlich in Ruhe lassen.

Alles, was größer ist, und Sie müssen anhand Ihrer Datenbanknutzung, Sperren von Tabellen usw. beurteilen, wie wichtig es ist, die Tabelle zu defragmentieren.

Sysadmiral
quelle
2

"Tabelle optimieren" behebt tatsächlich das Problem, das Sie haben.

Wenn Sie nur wenige Datenbanken haben, können Sie PHPMyAdmin verwenden, um alle Ihre Datenbanken zu durchsuchen. Wählen Sie die Tabellen mit Overhead aus und wählen Sie dann zum Optimieren aus.

Wenn Sie viele Datenbanken haben, ist wahrscheinlich eine andere Methode vorzuziehen.

Ich verwende das folgende PHP-Skript-Setup in Cron, um es stündlich auszuführen.

$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 des Chaos
quelle
3
Ich bin mir ziemlich sicher, dass dies mysqlcheck --optimize -Adasselbe ist wie SQLOPTIMIZE TABLE <tablename>;
docwhat 16.03.11
2

Ich bin auf diese Seite gestoßen und fand die Anfragen von Felipe-Rojas und Sysadmiral sehr hilfreich. Aber in meinem Fall habe ich die Abfrage in WHMs phpMyAdmin ausgeführt und nur TABLE_NAME zu erhalten, war nicht so hilfreich, da die Datenbank nicht aufgelistet war und mehrere Datenbanken den gleichen Tabellennamen haben. Wenn Sie also einfach hinzufügen, TABLE_SCHEMAwird auch diese Spalte angezeigt.

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

Zeigt DB

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

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

Um das Problem zu beheben, habe ich den Link Defragment table in phpMyAdmin für jede der Tabellen verwendet, die zu einem hohen "frag_ratio" geführt haben, für das phpMyAdmin ausgeführt wird:

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

Eine Tabelle, die die InnoDB Engine von MySQL verwendet, muss es im Grunde genommen nie sein OPTIMIZEd.

Der Wert Data_freevon entweder information_schema.tablesoder SHOW TABLE STATUSist sehr oft ungleich Null, auch wenn Sie glauben, alles getan zu haben, was Sie tun können, um Ihre Tabelle (n) zu defragmentieren. Darüber hinaus ist diese Metrik nur eine von mehreren Fragmentierungen, die auftreten können und können. (Außerdem verschwendeter Speicherplatz in Blöcken, Undo-Listen, Index-BTrees vs. Daten-BTrees usw.)

Und innodb_file_per_tableerschwert die Verwendung von Data_free. Wenn sich die Tabelle in befindet ibdata1, Data_freebezieht sie sich auf den gesamten Tablespace. eine ziemlich nutzlose Zahl. Befindet sich die Tabelle in einer eigenen .ibdDatei, sind es wahrscheinlich einige MB oder einige Prozent der Tabellengröße, je nachdem, welcher Wert größer ist.

Nur wenn Sie viele Zeilen gelöscht haben und nicht beabsichtigen, die Tabelle erneut zu füllen, ist es möglicherweise sinnvoll, sie auszuführen OPTIMIZE TABLE.

PARTITIONszeigen auch eine störende Menge an Data_free, da jede Partition typischerweise 4-7MB "frei" zeigt. Und das wird nicht verschwinden.

Warum defragmentieren?

  • So geben Sie Speicherplatz für das Betriebssystem zurück Nun, Sie könnten dies kurz erreichen, wenn Sie es getan hätten innodb_file_per_table=1. Wenn Sie jedoch Zeilen hinzufügen, wird dies vom Betriebssystem zurückgenommen.
  • Um den Zugang zu beschleunigen? Vergiss es. Das Layout von Blöcken auf der Festplatte ist seit einigen Jahrzehnten relativ zufällig. Vor einem halben Jahrhundert war es wichtig, die Blöcke neu anzuordnen.
  • BTrees neu ausbalancieren? So? Sie werden sofort wieder aus dem Gleichgewicht gebracht. Der Steady-State für BTrees, die zufällig eingefügt werden, beträgt 69%. Und das wird nicht einmal berücksichtigt Data_free.
  • MySQLTuner sagt zu? Das Produkt muss abkühlen.

Eine Geschichtsnotiz. Als ich DBAs hauptsächlich mit MyISAM-Tabellen half, entdeckte ich vielleicht 2 von 1000 Tabellen, denen monatlich geholfen wurde OPTIMIZE. Seitdem ich mit Tausenden von InnoDB-Tabellen gearbeitet habe, habe ich noch ein Leistungsproblem gefunden, das wahrscheinlich von geholfen wurde OPTIMIZE. (Sicher, es gab Speicherplatzprobleme, bei denen OPTIMIZEmöglicherweise Abhilfe geschaffen wurde, aber das wird schwierig - normalerweise verfügt der DBA nicht über genügend Speicherplatz, um ausgeführt zu werden OPTIMIZE!)

Rick James
quelle