Il modo più veloce per verificare se la tabella InnoDB è cambiata


22

La mia applicazione richiede molto database. Attualmente eseguo MySQL 5.5.19 e utilizzo MyISAM, ma sto eseguendo la migrazione a InnoDB. L'unico problema rimasto è la prestazione di checksum.

La mia applicazione fa circa 500-1000 CHECKSUM TABLEistruzioni al secondo nelle ore di punta, perché la GUI del client esegue costantemente il polling del database per le modifiche (è un sistema di monitoraggio, quindi deve essere molto reattivo e veloce).

Con MyISAM, ci sono checksum live che sono precalcolati sulla modifica della tabella e sono MOLTO veloci. Tuttavia, non esiste nulla di simile in InnoDB. Quindi, CHECKSUM TABLEè MOLTO lento.

Spero di essere in grado di controllare l'ultimo tempo di aggiornamento della tabella, Sfortunatamente, neanche questo è disponibile in InnoDB. Sono bloccato ora, perché i test hanno dimostrato che le prestazioni dell'applicazione calano drasticamente.

Esistono semplicemente troppe righe di codice che aggiornano le tabelle, quindi l'implementazione della logica nell'applicazione per registrare le modifiche alla tabella è fuori discussione.

Esiste un metodo rapido per rilevare le modifiche nelle tabelle InnoDB?

Risposte:


15

Per la tabella mydb.mytable, eseguire questa query:

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

Se vuoi sapere quali tabelle sono state modificate negli ultimi 5 minuti, esegui questo:

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

Provaci !!!

AGGIORNAMENTO 2011-12-21 20:04 EDT

Il mio datore di lavoro (DB / Wweb hosting comany) ha un client con 112.000 tabelle InnoDB. È molto difficile leggere INFORMATION_SCHEMA.TABLES nelle ore di punta. Ho un suggerimento alternativo:

Se innodb_file_per_table è abilitato e tutte le tabelle di InnoDB sono archiviate in .ibdfile, esiste un modo per accertare l'ora dell'ultimo aggiornamento (fino al minuto).

Per la tabella mydb.mytable, procedi come segue nel sistema operativo:

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

Questo timestamp proviene dal sistema operativo. Non puoi sbagliare su questo.

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

Aggiungilo a my.cnf, riavvia mysql e tutte le tabelle di InnoDB subiranno vampate veloci dal pool di buffer.

Per evitare il riavvio, basta eseguire

mysql> SET GLOBAL innodb_max_dirty_pages_pct=0;

AGGIORNAMENTO 2013-06-27 07:15 EDT

Quando si tratta di recuperare la data e l'ora di un file, ls ha l' --time-styleopzione:

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

È possibile confrontare il timestamp del file con UNIX_TIMESTAMP (NOW ()) .


Sei sicuro di non poter sbagliare con il moddate idb? Un cambiamento potrebbe risiedere nel pool di buffer in memoria e non essere ancora scaricato sul disco.
Atxdba,

6
Grazie per la risposta, ma come ho detto, update_time in information_schema.tables è NULL per le tabelle InnoDB. Inoltre non sono sicuro che innodb_max_dirty_pages_pct = 0 sia una buona idea, perché sacrificherà le prestazioni ... Stavo pensando a una soluzione con trigger, per inserire un valore casuale in una tabella di riferimento per ciascuna delle tabelle osservate, ma poi avrò bisogno di 3 grilletti per tavolo solo per questo ...
Jacket

Anche selezionare da information_schema.tables è un po 'lento ... ho bisogno di circa 300ms per controllare una tabella. Per fare un confronto, fare una "TABELLA DI CHECKSUM" su una tabella MyISAM con milioni di righe con Live Checksum abilitato richiede meno di un millisecondo.
Giacca il

2
+1 per il controllo del file system, a condizione che lo svuotamento del buffer sia abbastanza regolare (all'incirca una volta al secondo è l'impostazione predefinita), questo timestamp sarà piuttosto accurato e probabilmente abbastanza buono per la maggior parte dei casi ...
Dave Rix

1
Forse è OK per un database locale, ma ho più slave remoti, quindi questo non funziona ...
Jacket

3

Penso di aver trovato la soluzione. Da qualche tempo stavo guardando Percona Server per sostituire i miei server MySQL, e ora penso che ci sia una buona ragione per questo.

Il server Percona introduce molte nuove tabelle INFORMATION_SCHEMA come INNODB_TABLE_STATS, che non è disponibile nel server MySQL standard. Quando lo fai:

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

Ottieni il conteggio delle righe e un contatore. La documentazione ufficiale dice quanto segue su questo campo:

Se il valore della colonna modificata supera "righe / 16" o 2000000000, il ricalcolo delle statistiche viene eseguito quando innodb_stats_auto_update == 1. Possiamo stimare la vecchiaia delle statistiche con questo valore.

Quindi questo contatore si avvolge di tanto in tanto, ma puoi fare un checksum del numero di righe e del contatore, e quindi ad ogni modifica della tabella ottieni un checksum unico. Per esempio:

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

Avrei comunque aggiornato i miei server al server Percona, quindi questo limite non è un problema per me. La gestione di centinaia di trigger e l'aggiunta di campi alle tabelle è una vera seccatura per questa applicazione, perché è in fase molto avanzata di sviluppo.

Questa è la funzione PHP che ho creato per assicurarsi che le tabelle possano essere verificate indipendentemente dal motore e dal server utilizzati:

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

Puoi usarlo in questo modo:

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

Spero che questo risparmi qualche problema ad altre persone che hanno lo stesso problema.


Ulteriore sviluppo della storia per coloro che sono interessati: forum.percona.com/…
Jacket

1

È necessario aggiornare a Mysql v5.6 + in quella versione innodb ha anche il supporto per la tabella di checksum. http://dev.mysql.com/doc/refman/5.6/en/checksum-table.html

a parte questo, la soluzione ideale sarebbe se il tuo client non eseguisse il polling per il risultato è costantemente, ma invece dove spingevi i dati nuovi e modificati quando e se fossero disponibili. Sarebbe più veloce e meno carico sarebbe sul server. se stai usando una GUI basata sul web, dovresti guardare APE http://ape-project.org/ o altri progetti simili.


Sfortunatamente, questo è un killer delle prestazioni. Il checksum è composto da hashing di tutte le righe una per una . Dai documenti: "Questo calcolo riga per riga è quello che ottieni con la clausola EXTENDED, con InnoDB e tutti gli altri motori di archiviazione diversi da MyISAM e con le tabelle MyISAM non create con la clausola CHECKSUM = 1" :-(
LSerni

1

Se stai aggiungendo principalmente a una tabella, puoi agganciare AUTO_INCREMENT come misura di aggiornamento.

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

Ma preferirei fare riferimento a una fonte otside come un contatore in Memcached che aumenterai ogni volta che cambi qualcosa nel database.


0

Potresti provare a fare quanto segue:

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

Ciò restituisce un numero che aumenta con ogni aggiornamento della tabella, tenerne traccia consentirà di rilevare le modifiche.

Nota importante: il valore viene modificato immediatamente dopo un AGGIORNAMENTO, non dopo COMMIT. Quindi potresti non vedere le modifiche se le modifiche sono state apportate all'interno di un'altra transazione che non è stata completata.


0

Questa risposta non ha nulla a che fare con le versioni o i tipi di database mysql, volevo sapere se le istruzioni di aggiornamento stavano apportando modifiche E farlo nel mio codice php ..

  1. Creato una tabella fittizia con un record e un campo su cui interrogare per ottenere il valore del mysql's current_timestamp.

  2. Alla tabella di dati da aggiornare, aggiunto un campo timestamp e utilizzato l'opzione mysql "ON UPDATE CURRENT_TIMESTAMP"

  3. Rispetto al n. 1 e al n. 2

Questo non funzionerà al 100% delle volte, ma per la mia applicazione è stata una soluzione semplice e ottima. Spero che questo aiuti qualcuno

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.