Der schnellste Weg, um zu überprüfen, ob sich die InnoDB-Tabelle geändert hat

22

Meine Anwendung ist sehr datenbankintensiv. Momentan führe ich MySQL 5.5.19 aus und verwende MyISAM, bin aber dabei, auf InnoDB zu migrieren. Das einzige verbleibende Problem ist die Leistung der Prüfsumme.

Meine Anwendung führt CHECKSUM TABLEin Spitzenzeiten ca. 500-1000 Anweisungen pro Sekunde aus, da die Benutzeroberfläche des Clients die Datenbank ständig nach Änderungen abfragt (es handelt sich um ein Überwachungssystem, das daher sehr reaktionsschnell und schnell sein muss).

Mit MyISAM gibt es Live-Prüfsummen, die bei der Tabellenänderung vorberechnet werden und SEHR schnell sind. In InnoDB gibt es so etwas jedoch nicht. Also, CHECKSUM TABLEist sehr langsam.

Ich hatte gehofft, die letzte Aktualisierung der Tabelle überprüfen zu können. Leider ist dies auch in InnoDB nicht möglich. Ich stecke jetzt fest, weil Tests gezeigt haben, dass die Leistung der Anwendung drastisch abnimmt.

Es gibt einfach zu viele Codezeilen, die die Tabellen aktualisieren, sodass die Implementierung von Logik in der Anwendung zum Protokollieren von Tabellenänderungen nicht in Frage kommt.

Gibt es eine schnelle Methode zum Erkennen von Änderungen in InnoDB-Tabellen?

Jacke
quelle

Antworten:

15

Führen Sie für die Tabelle mydb.mytable die folgende Abfrage aus:

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

Wenn Sie wissen möchten, welche Tabellen sich in den letzten 5 Minuten geändert haben, führen Sie Folgendes aus:

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

Versuche es !!!

UPDATE 2011-12-21 20:04 EDT

Mein Arbeitgeber (DB / Wweb-Hosting-Unternehmen) hat einen Kunden mit 112.000 InnoDB-Tabellen. Während der Stoßzeiten ist es sehr schwierig, INFORMATION_SCHEMA.TABLES zu lesen. Ich habe einen alternativen Vorschlag:

Wenn Sie innodb_file_per_table aktiviert haben und alle InnoDB-Tabellen in .ibdDateien gespeichert sind, können Sie den Zeitpunkt der letzten Aktualisierung ermitteln (bis zur Minute).

Führen Sie für die Tabelle mydb.mytable im Betriebssystem die folgenden Schritte aus:

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

Dieser Zeitstempel stammt aus dem Betriebssystem. Da kann man nichts falsch machen.

UPDATE 2011-12-21 22:04 EDT [mysqld] innodb_max_dirty_pages_pct = 0;

Fügen Sie dies zu my.cnf hinzu, starten Sie mysql neu, und alle InnoDB-Tabellen werden schnell aus dem Pufferpool gelöscht.

Um einen Neustart zu vermeiden, einfach ausführen

mysql> SET GLOBAL innodb_max_dirty_pages_pct=0;

UPDATE 2013-06-27 07:15 EDT

Wenn Sie das Datum und die Uhrzeit einer Datei abrufen möchten, haben Sie folgende --time-styleMöglichkeiten:

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

Sie können den Zeitstempel der Datei mit UNIX_TIMESTAMP (NOW ()) vergleichen .

RolandoMySQLDBA
quelle
Sind Sie sicher, dass Sie mit dem IDB-Moddate nichts falsch machen können? Eine Änderung könnte darin bestehen, dass sie nur im Pufferspeicher vorhanden ist und noch nicht auf die Festplatte geschrieben wurde.
atxdba
6
Danke für die Antwort, aber wie gesagt, update_time in information_schema.tables ist für InnoDB-Tabellen NULL. Ich bin mir auch nicht sicher, ob innodb_max_dirty_pages_pct = 0 eine gute Idee ist, weil es die Leistung opfert ... Ich dachte über eine Lösung mit Triggern nach, um einen zufälligen Wert an einer Referenztabelle für jede der beobachteten Tabellen einzufügen, aber dann Ich brauche nur 3 Trigger pro Tisch für diese ...
Jacke
Auch das Auswählen aus information_schema.tables ist etwas zu langsam ... Ich brauche ungefähr 300 ms, um eine Tabelle zu überprüfen. Zum Vergleich: Das Erstellen einer "CHECKSUM TABLE" für eine MyISAM-Tabelle mit Millionen von Zeilen und aktivierter Live-Prüfsumme dauert weniger als eine Millisekunde.
Jacke
2
+1 für die Dateisystemprüfung, solange die Pufferlöschung regelmäßig genug ist (ungefähr einmal pro Sekunde ist die Standardeinstellung), ist dieser Zeitstempel ziemlich genau und wahrscheinlich in den meisten Fällen gut genug ...
Dave Rix
1
Vielleicht ist es in Ordnung für eine lokale Datenbank, aber ich habe mehrere Remote-Slaves, das funktioniert also nicht ...
Jacke
3

Ich denke, ich habe die Lösung gefunden. Ich habe eine Zeit lang nach Percona Server gesucht, um meine MySQL-Server zu ersetzen, und jetzt glaube ich, dass es einen guten Grund dafür gibt.

Percona Server führt viele neue INFORMATION_SCHEMA-Tabellen wie INNODB_TABLE_STATS ein, die in Standard-MySQL-Servern nicht verfügbar sind. Wenn Sie das tun:

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

Sie erhalten die tatsächliche Zeilenzahl und einen Zähler. In der offiziellen Dokumentation heißt es zu diesem Feld:

Wenn der Wert der geänderten Spalte "Zeilen / 16" oder 2000000000 überschreitet, wird die Statistik neu berechnet, wenn innodb_stats_auto_update == 1. Wir können die Alterung der Statistik anhand dieses Werts schätzen.

Dieser Zähler wird also von Zeit zu Zeit umgebrochen. Sie können jedoch eine Prüfsumme aus der Anzahl der Zeilen und dem Zähler erstellen. Bei jeder Änderung der Tabelle erhalten Sie eine eindeutige Prüfsumme. Z.B:

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

Ich wollte sowieso meine Server auf Percona-Server upgraden, sodass diese Einschränkung für mich kein Problem darstellt. Das Verwalten von Hunderten von Triggern und das Hinzufügen von Feldern zu Tabellen ist für diese Anwendung ein großes Problem, da die Entwicklung sehr spät ist.

Dies ist die PHP-Funktion, die ich entwickelt habe, um sicherzustellen, dass Tabellen unabhängig von der verwendeten Engine und dem verwendeten Server mit Prüfsummen versehen werden können:

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;
}

Du kannst es so benutzen:

// 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")); 

Ich hoffe, dies erspart anderen Menschen, die das gleiche Problem haben, einige Schwierigkeiten.

Jacke
quelle
Weitere Geschichte Entwicklung für diejenigen, die interessiert sind: forum.percona.com/…
Jacke
1

Sie sollten auf Mysql v5.6 + aktualisieren, da innodb auch die Prüfsummentabelle unterstützt. http://dev.mysql.com/doc/refman/5.6/en/checksum-table.html

Andernfalls wäre die ideale Lösung, wenn Ihr Client nicht ständig nach Ergebnissen fragt, sondern Sie neue und geänderte Daten abrufen, wenn diese verfügbar sind. Es wäre schneller und der Server würde weniger belastet. Wenn Sie eine webbasierte Benutzeroberfläche verwenden, sollten Sie sich APE http://ape-project.org/ oder andere ähnliche Projekte ansehen .

Gamesh
quelle
Leider ist dies ein Leistungskiller. Die Prüfsumme besteht aus dem Hashing aller Zeilen nacheinander . In den Dokumenten heißt es: "Diese zeilenweise Berechnung erhalten Sie mit der EXTENDED-Klausel, mit InnoDB und allen anderen Speicher-Engines außer MyISAM und mit MyISAM-Tabellen, die nicht mit der CHECKSUM = 1-Klausel erstellt wurden." :-(
LSerni
1

Wenn Sie hauptsächlich zu einer Tabelle hinzufügen, können Sie AUTO_INCREMENT als Maß für die Aktualität verwenden.

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

Aber ich würde es vorziehen, auf eine externe Quelle wie einen Zähler in Memcached zu verweisen, den Sie jedes Mal erhöhen, wenn Sie etwas in der Datenbank ändern.

sanmai
quelle
0

Sie könnten versuchen, Folgendes zu tun:

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

Dies gibt eine Zahl zurück, die mit jeder Tabellenaktualisierung zunimmt. Wenn Sie dies nachverfolgen, können Sie Änderungen erkennen.

Wichtiger Hinweis: Der Wert wird sofort nach einem UPDATE geändert, nicht nach einem COMMIT. Daher werden die Änderungen möglicherweise nicht angezeigt, wenn sie in einer anderen Transaktion vorgenommen wurden, die nicht abgeschlossen wurde.

Romuald Brunet
quelle
0

Diese Antwort hat nichts mit MySQL-Datenbankversionen oder -typen zu tun. Ich wollte wissen, ob Update-Anweisungen Änderungen vornehmen UND dies in meinem PHP-Code tun.

  1. Erstellt eine Dummy-Tabelle mit einem Datensatz und einem Feld, die ich abfragen würde, um den Wert von MySQLs current_timestamp zu erhalten.

  2. Fügen Sie der zu aktualisierenden Datentabelle ein Zeitstempelfeld hinzu und verwenden Sie die mysql-Option "ON UPDATE CURRENT_TIMESTAMP".

  3. Verglichen mit # 1 und # 2

Dies wird in den meisten Fällen nicht 100% ig funktionieren, aber für meine Anwendung war es eine einfache und großartige Lösung. Hoffe das hilft jemandem

Steve Padgett
quelle