Najszybszy sposób sprawdzenia, czy zmieniła się tabela InnoDB

22

Moja aplikacja wymaga dużej bazy danych. Obecnie korzystam z MySQL 5.5.19 i korzystam z MyISAM, ale jestem w trakcie migracji do InnoDB. Pozostaje tylko problem z wydajnością sumy kontrolnej.

Moja aplikacja wykonuje CHECKSUM TABLEw godzinach szczytu około 500–1000 instrukcji na sekundę, ponieważ GUI klienta stale przeszukuje bazę danych pod kątem zmian (jest to system monitorowania, więc musi być bardzo responsywny i szybki).

W MyISAM istnieją sumy kontrolne na żywo, które są wstępnie obliczane przy modyfikacji tabeli i są BARDZO szybkie. Jednak nie ma czegoś takiego w InnoDB. Jest więc CHECKSUM TABLEbardzo powolny.

Mam nadzieję, że będę w stanie sprawdzić czas ostatniej aktualizacji tabeli, niestety nie jest to również dostępne w InnoDB. Utknąłem teraz, ponieważ testy wykazały, że wydajność aplikacji drastycznie spada.

Jest po prostu zbyt wiele wierszy kodu, które aktualizują tabele, więc nie ma mowy o implementacji logiki w aplikacji do rejestrowania zmian w tabeli.

Czy istnieje szybka metoda wykrywania zmian w tabelach InnoDB?

Kurtka
źródło

Odpowiedzi:

15

W przypadku tabeli mydb.mytable uruchom następujące zapytanie:

SELECT update_time
FROM information_schema.tables
WHERE table_schema='mydb'
AND table_name='mytable';

Jeśli chcesz wiedzieć, które tabele zmieniły się w ciągu ostatnich 5 minut, uruchom to:

SELECT table_schema,table_name,update_time
FROM information_schema.tables
WHERE update_time > (NOW() - INTERVAL 5 MINUTE);

Spróbuj !!!

AKTUALIZACJA 21.12.2011 20:04 EDT

Mój pracodawca (firma hostingowa DB / Wweb) ma klienta z 112 000 tabel InnoDB. Bardzo trudno odczytać TABLICE INFORMACYJNE w godzinach szczytu. Mam alternatywną sugestię:

Jeśli masz włączoną opcję innodb_file_per_table, a wszystkie tabele InnoDB są przechowywane w .ibdplikach, istnieje sposób, aby ustalić czas ostatniej aktualizacji (do minuty).

W przypadku tabeli mydb.mytable wykonaj następujące czynności w systemie operacyjnym:

$ cd /var/lib/mysql/mydb
$ ls -l mytable.ibd | awk '{print $4,$5}'

Ten znacznik czasu pochodzi z systemu operacyjnego. Nie możesz się pomylić w tej sprawie.

AKTUALIZACJA 21.12.2011 22:04 EDT [mysqld] innodb_max_dirty_pages_pct = 0;

Dodaj to do my.cnf, uruchom ponownie mysql, a wszystkie tabele InnoDB doświadczą szybkich opróżnień z puli buforów.

Aby uniknąć ponownego uruchomienia, po prostu uruchom

mysql> SET GLOBAL innodb_max_dirty_pages_pct=0;

AKTUALIZACJA 27.06.2013, 07:15 EDT

Jeśli chodzi o pobieranie daty i godziny dla pliku, ls ma --time-styleopcję:

$ cd /var/lib/mysql/mydb
$ ls -l --time-style="+%s" mytable.ibd | awk '{print $6}'

Możesz porównać znacznik czasu pliku z UNIX_TIMESTAMP (NOW ()) .

RolandoMySQLDBA
źródło
Czy na pewno nie możesz się pomylić z moddate idb? Zmianą może być po prostu życie w puli buforów w pamięci i jeszcze nie upuszczenie jej na dysk.
atxdba
6
Dzięki za odpowiedź, ale jak już powiedziałem, update_time w information_schema.tables ma wartość NULL dla tabel InnoDB. Nie jestem również pewien, czy innodb_max_dirty_pages_pct = 0 to dobry pomysł, ponieważ obniży wydajność ... Myślałem o rozwiązaniu z wyzwalaczami, aby wstawić losową wartość w tabeli referencyjnej dla każdej z obserwowanych tabel, ale potem Potrzebuję tylko 3 wyzwalaczy na tabelę tylko do tego ...
Kurtka z
Również wybranie z information_schema.tables jest trochę powolne ... Zajmuje około 300ms, aby sprawdzić jedną tabelę. Dla porównania wykonanie „TABELI KONTROLNEJ” w tabeli MyISAM z milionami wierszy z włączoną aktywną sumą kontrolną zajmuje mniej niż milisekundę.
Kurtka
2
+1 za sprawdzenie systemu plików, o ile opróżnianie bufora jest wystarczająco regularne (domyślnie mniej więcej raz na sekundę), to ten znacznik czasu będzie dość dokładny i prawdopodobnie wystarczający w większości przypadków ...
Dave Rix
1
Może jest w porządku dla lokalnej bazy danych, ale mam wielu zdalnych slaveów, więc to nie działa ...
Jacket
3

Myślę, że znalazłem rozwiązanie. Przez jakiś czas patrzyłem na Percona Server, aby wymienić moje serwery MySQL, a teraz myślę, że jest ku temu dobry powód.

Serwer Percona wprowadza wiele nowych tabel INFORMACJE_SCHEMA, takich jak INNODB_TABLE_STATS, które nie są dostępne w standardowym serwerze MySQL. Kiedy to zrobisz:

SELECT rows, modified FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table'

Otrzymasz rzeczywistą liczbę wierszy i licznik. Oficjalna dokumentacja mówi o tym następujące pola:

Jeśli wartość zmodyfikowanej kolumny przekracza „wiersze / 16” lub 2000000000, ponowne obliczenie statystyki jest wykonywane, gdy innodb_stats_auto_update == 1. Możemy oszacować starość statystyki na podstawie tej wartości.

Tak więc ten licznik jest zawijany co jakiś czas, ale możesz zrobić sumę kontrolną liczby wierszy i licznika, a następnie przy każdej modyfikacji tabeli otrzymujesz unikalną sumę kontrolną. Na przykład:

SELECT MD5(CONCAT(rows,'_',modified)) AS checksum FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table';

W każdym razie zamierzałem zaktualizować swoje serwery do serwera Percona, więc to ograniczenie nie stanowi dla mnie problemu. Zarządzanie setkami wyzwalaczy i dodawanie pól do tabel jest dużym problemem dla tej aplikacji, ponieważ jest bardzo późno w fazie rozwoju.

Oto funkcja PHP, którą wymyśliłem, aby upewnić się, że tabele można sprawdzać niezależnie od używanego silnika i serwera:

function checksum_table($input_tables){
    if(!$input_tables) return false; // Sanity check
    $tables = (is_array($input_tables)) ? $input_tables : array($input_tables); // Make $tables always an array
    $where = "";
    $checksum = "";
    $found_tables = array();
    $tables_indexed = array();
    foreach($tables as $table_name){
        $tables_indexed[$table_name] = true; // Indexed array for faster searching
        if(strstr($table_name,".")){ // If we are passing db.table_name
            $table_name_split = explode(".",$table_name);
            $where .= "(table_schema='".$table_name_split[0]."' AND table_name='".$table_name_split[1]."') OR ";
        }else{
            $where .= "(table_schema=DATABASE() AND table_name='".$table_name."') OR ";
        }
    }
    if($where != ""){ // Sanity check
        $where = substr($where,0,-4); // Remove the last "OR"
        $get_chksum = mysql_query("SELECT table_schema, table_name, rows, modified FROM information_schema.innodb_table_stats WHERE ".$where);
        while($row = mysql_fetch_assoc($get_chksum)){
            if($tables_indexed[$row[table_name]]){ // Not entirely foolproof, but saves some queries like "SELECT DATABASE()" to find out the current database
                $found_tables[$row[table_name]] = true;
            }elseif($tables_indexed[$row[table_schema].".".$row[table_name]]){
                $found_tables[$row[table_schema].".".$row[table_name]] = true;
            }
            $checksum .= "_".$row[rows]."_".$row[modified]."_";
        }
    }

    foreach($tables as $table_name){
        if(!$found_tables[$table_name]){ // Table is not found in information_schema.innodb_table_stats (Probably not InnoDB table or not using Percona Server)
            $get_chksum = mysql_query("CHECKSUM TABLE ".$table_name); // Checksuming the old-fashioned way
            $chksum = mysql_fetch_assoc($get_chksum);
            $checksum .= "_".$chksum[Checksum]."_";
        }
    }

    $checksum = sprintf("%s",crc32($checksum)); // Using crc32 because it's faster than md5(). Must be returned as string to prevent PHPs signed integer problems.

    return $checksum;
}

Możesz użyć tego w następujący sposób:

// checksum a signle table in the current db
$checksum = checksum_table("test_table");

// checksum a signle table in db other than the current
$checksum = checksum_table("other_db.test_table");

// checksum multiple tables at once. It's faster when using Percona server, because all tables are checksummed via one select.
$checksum = checksum_table(array("test_table, "other_db.test_table")); 

Mam nadzieję, że zaoszczędzi to trochę problemów innym osobom mającym ten sam problem.

Kurtka
źródło
Dalszy rozwój historii dla zainteresowanych: forum.percona.com/…
Kurtka
1

W tej wersji powinieneś zaktualizować do MySQL v5.6 +. Innodb obsługuje także tabelę sum kontrolnych. http://dev.mysql.com/doc/refman/5.6/en/checksum-table.html

poza tym idealnym rozwiązaniem byłoby, gdyby Twój klient nie sondował ciągle wyników, ale zamiast tego przesuwałeś nowe i zmienione dane, kiedy i jeśli były dostępne. Byłoby to szybsze i mniej obciążałoby serwer. jeśli używasz GUI przez Internet, powinieneś zajrzeć do APE http://ape-project.org/ lub innych podobnych projektów.

Gamesh
źródło
Niestety jest to zabójca wydajności. Suma kontrolna jest tworzona przez haszowanie wszystkich wierszy jeden po drugim . Z dokumentacji: „To obliczenie wiersz po rzędzie uzyskuje się dzięki klauzuli EXTENDED, InnoDB i wszystkim innym silnikom pamięci masowej innym niż MyISAM oraz tabelom MyISAM nie utworzonym z klauzulą ​​CHECKSUM = 1” :-(
LSerni
1

Jeśli głównie dodajesz do tabeli, możesz podłączyć AUTO_INCREMENT jako miarę aktualizacji.

SELECT `AUTO_INCREMENT` FROM `information_schema`.`tables` 
WHERE `table_schema` = DATABASE() AND `table_name` = 'YOUR_TABLE';

Ale wolałbym odwoływać się do innego źródła, takiego jak licznik w Memcached, który będzie zwiększany za każdym razem, gdy zmienisz coś w bazie danych.

sanmai
źródło
0

Możesz spróbować wykonać następujące czynności:

SELECT rows_changed
FROM information_schema.table_statistics
WHERE table_schema = 'mydb' AND table_name='mytable';

Zwraca liczbę, która rośnie z każdą aktualizacją tabeli, śledzenie jej pozwoli wykryć zmianę.

Ważna uwaga: wartość zmienia się natychmiast po aktualizacji, a nie po zatwierdzeniu. Dlatego zmiany mogą nie być widoczne, jeśli zmiany zostały wprowadzone w ramach innej transakcji, która się nie zakończyła.

Romuald Brunet
źródło
0

Ta odpowiedź nie ma nic wspólnego z wersjami lub typami baz danych MySQL, chciałem wiedzieć, czy instrukcje aktualizacji wprowadzają zmiany ORAZ zrobić to w moim kodzie PHP.

  1. Utworzono fikcyjną tabelę z jednym rekordem i jednym polem, które chciałbym zapytać, aby uzyskać wartość parametru current_timestamp mysql.

  2. Do aktualizowanej tabeli danych dodano pole znacznika czasu i użyłem opcji mysql „ON UPDATE CURRENT_TIMESTAMP”

  3. W porównaniu # 1 i # 2

To nie zadziała w 100% przypadków, ale dla mojej aplikacji było to proste i świetne rozwiązanie. Mam nadzieję, że to komuś pomoże

Steve Padgett
źródło