Jak znaleźć i naprawić pofragmentowane tabele MySQL

27

Użyłem MySQLTuner, który wskazał, że niektóre tabele są podzielone. użyłem

mysqlcheck --optimize -A

aby zoptymalizować wszystkie tabele. Naprawiono niektóre tabele, ale MySQLTuner nadal znajduje fragmenty 19 tabel. jak mogę zobaczyć, które tabele wymagają defragmentacji? Może TABELA OPTYMALIZACJI będzie działać tam, gdzie nie działa mysqlcheck? A co jeszcze powinienem spróbować?

ciekawy kot
źródło
1
Mam podobny problem. Konfiguruję nowy DB z MySQL 5.5, a niektóre tabele InnoDB nigdy się nie rozpakowują. Zastanawiam się, czy czek bez danych (pokazany w odpowiedzi KayakJim) jest nieprawidłowy w tabelach InnoDB.
docwhat

Odpowiedzi:

38

krótka odpowiedź:

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;

Odpowiedź „musisz wiedzieć”

najpierw musisz zrozumieć, że tabele MySQL ulegają fragmentacji, gdy wiersz jest aktualizowany, więc jest to normalna sytuacja. Gdy tworzona jest tabela, powiedzmy importowana przy użyciu zrzutu z danymi, wszystkie wiersze są przechowywane bez fragmentacji na wielu stronach o stałym rozmiarze. Po zaktualizowaniu wiersza o zmiennej długości strona zawierająca ten wiersz jest dzielona na dwie lub więcej stron w celu przechowywania zmian, a te dwie nowe (lub więcej) strony zawierają puste miejsca wypełniające nieużywane miejsce.

Nie wpływa to na wydajność, chyba że fragmentacja oczywiście wzrośnie za bardzo. Co to jest zbyt duża fragmentacja, zobaczmy zapytanie, którego szukasz:

  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 i INDEX_LENGTH to przestrzeń zajmowana przez twoje dane i indeksy, a DATA_FREE to całkowita ilość bajtów nieużywanych na wszystkich stronach tabeli (fragmentacja).

Oto przykład prawdziwego stołu produkcyjnego

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

W tym przypadku mamy tabelę używającą (896 + 316) = 1212 MB, a dane mają wolne miejsce 5 MB. Oznacza to „współczynnik fragmentacji”:

5/1212 = 0.0041

... co jest naprawdę niskim „współczynnikiem fragmentacji”.

Pracuję z tabelami o współczynniku bliskim 0,2 (co oznacza 20% pustych miejsc) i nigdy nie zauważam spowolnienia zapytań, nawet jeśli zoptymalizuję tabelę, wydajność jest taka sama. Ale zastosowanie stołu optymalizacyjnego na stole 800 MB zajmuje dużo czasu i blokuje go na kilka minut, co jest niewykonalne w produkcji.

Więc jeśli weźmiesz pod uwagę to, co wygrywasz w wydajności i czas zmarnowany na optymalizację stołu, wolę NIE OPTYMALIZOWAĆ.

Jeśli uważasz, że lepiej jest do przechowywania, sprawdź swój stosunek i zobacz, ile miejsca można zaoszczędzić podczas optymalizacji. Zwykle nie jest to zbyt wiele, więc wolę NIE OPTYMALIZOWAĆ.

A jeśli zoptymalizujesz, następna aktualizacja utworzy puste miejsca, dzieląc stronę na dwie lub więcej. Ale szybsza jest aktualizacja pofragmentowanej tabeli niż niepodzielonej, ponieważ jeśli tabela jest pofragmentowana, aktualizacja w wierszu niekoniecznie podzieli stronę.

Mam nadzieję, że to Ci pomoże.

Felipe Rojas
źródło
1
Chociaż jest to odpowiedź sprzed kilku lat, pomyślałem, że zwrócę uwagę, że data_free to statystyka dla całego obszaru tabel, a nie dla odpowiedniej tabeli. Jeśli przechowujesz wiele tabel razem w jednym obszarze tabel, data_free może wprowadzić cię w błąd, sądząc, że tabela wymaga defragmentacji, co oznacza po prostu, że w obszarze tabel są wolne zakresy. Uruchomienie tabeli optymalizacji nie zmniejszy wolnych zakresów. Defragmentacja tabeli może nawet zwiększyć liczbę wolnych zakresów.
Bill Karwin
14

Aby dodać do odpowiedzi Felipe-Rojasa , możesz obliczyć współczynnik fragmentów jako część zapytania:

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;

Jeśli tabela jest podzielona na małe fragmenty (mniej niż 5%?), Prawdopodobnie możesz zostawić ją w spokoju.

Cokolwiek większego i będziesz musiał ocenić na podstawie użycia bazy danych, blokowania tabel itp., Jak ważne jest defragmentowanie tabeli.

sysadmiral
źródło
2

Optymalizacja tabeli rzeczywiście rozwiąże problem, który masz.

Jeśli masz tylko kilka baz danych, możesz użyć PHPMyAdmin, aby przejrzeć wszystkie swoje bazy danych. Wybierz tabele z narzutem, a następnie wybierz, aby zoptymalizować.

Jeśli masz wiele baz danych, prawdopodobnie preferowana byłaby inna metoda.

Korzystam z następującej konfiguracji skryptu PHP w cronie, aby uruchamiał się co godzinę.

$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
źródło
3
Jestem prawie pewien, że mysqlcheck --optimize -Ajest to to samo, co SQLOPTIMIZE TABLE <tablename>;
docwhat
2

Natknąłem się na tę stronę i uznałem zapytania Felipe-Rojasa i sysadmirala za bardzo pomocne. Ale w moim przypadku uruchomiłem zapytanie w phpMyAdmin WHM i uzyskanie tylko TABLE_NAME nie było tak pomocne, ponieważ baza danych nie była wymieniona, a kilka baz danych ma te same nazwy tabel. Tak więc zwykłe dodanie TABLE_SCHEMAzapewni również tę kolumnę.

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

Pokazuje DB

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

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

Aby to naprawić, użyłem linku Defragmentacja tabeli w phpMyAdmin dla każdej z tabel, co spowodowało wysoki „frag_ratio”, dla którego phpMyAdmin wykonuje:

ALTER TABLE `table_name` ENGINE = InnoDB;
Chris
źródło
0

Tabela wykorzystująca silnik InnoDB MySQL zasadniczo nigdy nie musi być OPTIMIZEd.

Wartość Data_freez jednego information_schema.tableslub SHOW TABLE STATUSbardzo często jest niezerowa, nawet jeśli myślisz, że zrobiłeś wszystko, co możesz zrobić, aby zdefragmentować swoje tabele. Co więcej, ta metryka jest tylko jedną z kilku fragmentacji, które mogą się zdarzyć. (Również zmarnowane miejsce w blokach, cofanie list, indeks BTrees vs BTrees danych itp. Itp.

I innodb_file_per_tablekomplikuje użycie Data_free. Jeśli tabela jest w środku ibdata1, Data_freeoznacza to cały obszar tabel; raczej bezużyteczna liczba. Jeśli tabela znajduje się we własnym .ibdpliku, prawdopodobnie będzie to kilka MB lub kilka procent wielkości tabeli, w zależności od tego, która wartość jest większa.

Może warto uruchomić tylko wtedy, gdy usunąłeś wiele wierszy i nie zamierzasz uzupełniać tabeli .OPTIMIZE TABLE

PARTITIONspokazują również niepokojącą ilość Data_free, ponieważ każda partycja zwykle pokazuje 4-7 MB „za darmo”. I to nie zniknie.

Dlaczego Defragmentacja?

  • Aby zwrócić miejsce w systemie operacyjnym? No, może osiągnąć to krótko, jeśli miał innodb_file_per_table=1. Ale dodając wiersze, zabierzesz go z powrotem z systemu operacyjnego.
  • Aby przyspieszyć dostęp? Zapomnij o tym. Układ bloków na dysku jest stosunkowo losowy i ma miejsce przez kilka ostatnich dziesięcioleci. Pół wieku temu dość istotna była zmiana układu bloków.
  • Aby przywrócić równowagę BTrees? Więc? Natychmiast staną się ponownie niezrównoważone. Stan ustalony dla losowo wstawianych BTree wynosi 69%. I to nawet nie jest uwzględnione Data_free.
  • MySQLTuner mówi do? Ten produkt musi się wyluzować.

Notatka historyczna. Kiedy pomagałem DBA w większości tabel MyISAM, odkryłem może 2 na 1000 tabel, którym pomagał miesięcznie OPTIMIZE . Od tego czasu pracowałem z tysiącami tabel InnoDB, jeszcze znalazłem problem z wydajnością, któremu prawdopodobnie można by pomóc OPTIMIZE. (Jasne, wystąpiły problemy z miejscem na dysku, które OPTIMIZEmogą pomóc, ale staje się to trudne - zwykle DBA nie ma wystarczającej ilości miejsca na dysku do uruchomienia OPTIMIZE!)

Rick James
źródło