La forma más rápida de verificar si la tabla InnoDB ha cambiado

22

Mi aplicación requiere mucha base de datos. Actualmente, estoy ejecutando MySQL 5.5.19 y usando MyISAM, pero estoy en el proceso de migrar a InnoDB. El único problema que queda es el rendimiento de la suma de comprobación.

Mi aplicación realiza entre 500 CHECKSUM TABLEy 1000 declaraciones por segundo en horas pico, porque la GUI del cliente está sondeando constantemente los cambios en la base de datos (es un sistema de monitoreo, por lo que debe ser muy sensible y rápido).

Con MyISAM, hay sumas de comprobación en vivo que se calculan previamente en la modificación de la tabla y son MUY rápidas. Sin embargo, no existe tal cosa en InnoDB. Entonces, CHECKSUM TABLEes MUY lento.

Esperaba poder verificar la última hora de actualización de la tabla. Desafortunadamente, esto tampoco está disponible en InnoDB. Ahora estoy atascado, porque las pruebas han demostrado que el rendimiento de la aplicación cae drásticamente.

Simplemente hay demasiadas líneas de código que actualizan las tablas, por lo que no es posible implementar la lógica en la aplicación para registrar los cambios de la tabla.

¿Existe algún método rápido para detectar cambios en las tablas de InnoDB?

Chaqueta
fuente

Respuestas:

15

Para la tabla mydb.mytable, ejecute esta consulta:

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

Si desea saber qué tablas han cambiado en los últimos 5 minutos, ejecute esto:

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

Darle una oportunidad !!!

ACTUALIZACIÓN 2011-12-21 20:04 EDT

Mi empleador (DB / Wweb hosting comany) tiene un cliente con 112,000 tablas InnoDB. Es muy difícil leer INFORMATION_SCHEMA.TABLES durante las horas pico. Tengo una sugerencia alternativa:

Si tiene innodb_file_per_table habilitado y todas las tablas de InnoDB se almacenan en .ibdarchivos, hay una manera de determinar la hora de la última actualización (hasta el minuto).

Para la tabla mydb.mytable, haga lo siguiente en el sistema operativo:

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

Esta marca de tiempo es del sistema operativo. No te puedes equivocar en este caso.

ACTUALIZACIÓN 2011-12-21 22:04 EDT [mysqld] innodb_max_dirty_pages_pct = 0;

Agregue esto a my.cnf, reinicie mysql, y todas las tablas de InnoDB experimentarán descargas rápidas del grupo de búferes.

Para evitar reiniciar, simplemente ejecute

mysql> SET GLOBAL innodb_max_dirty_pages_pct=0;

ACTUALIZACIÓN 2013-06-27 07:15 EDT

Cuando se trata de recuperar la fecha y la hora de un archivo, ls tiene la --time-styleopción:

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

Puede comparar la marca de tiempo del archivo con UNIX_TIMESTAMP (NOW ()) .

RolandoMySQLDBA
fuente
¿Estás seguro de que no puedes equivocarte con el idb moddate? Un cambio podría estar viviendo en el grupo de búferes en la memoria y aún no se ha vaciado al disco.
atxdba
66
Gracias por la respuesta, pero como dije, update_time en information_schema.tables es NULL para las tablas de InnoDB. Además, no estoy seguro de que innodb_max_dirty_pages_pct = 0 sea una buena idea, porque sacrificará el rendimiento ... Estaba pensando en una solución con desencadenantes, para insertar un valor aleatorio en una tabla de referencia para cada una de las tablas observadas, pero luego Necesitaré 3 disparadores por tabla solo para esto ...
Jacket
También seleccionar de information_schema.tables es un poco lento también ... me toma unos 300 ms para verificar una tabla. Para comparar, hacer una "TABLA DE CHECKSUM" en una tabla MyISAM con millones de filas con Live Checksum habilitado está tomando menos de un milisegundo.
Chaqueta
2
+1 para la verificación del sistema de archivos, siempre que el lavado del búfer sea lo suficientemente regular (aproximadamente una vez por segundo es el valor predeterminado), entonces esta marca de tiempo será bastante precisa y probablemente lo suficientemente buena para la mayoría de los casos ...
Dave Rix
1
Tal vez esté bien para una base de datos local, pero tengo varios esclavos remotos, así que esto no funciona ...
Jacket
3

Creo que he encontrado la solución. Durante algún tiempo estuve buscando en el Servidor Percona para reemplazar mis servidores MySQL, y ahora creo que hay una buena razón para esto.

El servidor Percona presenta muchas nuevas tablas INFORMATION_SCHEMA como INNODB_TABLE_STATS, que no está disponible en el servidor MySQL estándar. Cuando tu lo hagas:

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

Obtiene el recuento real de filas y un contador. La documentación oficial dice lo siguiente sobre este campo:

Si el valor de la columna modificada excede "filas / 16" o 2000000000, el recálculo de estadísticas se realiza cuando innodb_stats_auto_update == 1. Podemos estimar la antigüedad de las estadísticas por este valor.

Por lo tanto, este contador se ajusta de vez en cuando, pero puede hacer una suma de verificación del número de filas y el contador, y luego con cada modificación de la tabla obtendrá una suma de verificación única. P.ej:

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

De todos modos, iba a actualizar mis servidores al servidor Percona, por lo que este límite no es un problema para mí. Administrar cientos de desencadenantes y agregar campos a las tablas es un gran problema para esta aplicación, porque es muy tarde en el desarrollo.

Esta es la función PHP que he creado para asegurarme de que las tablas se puedan sumar en cualquier motor y servidor que se use:

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

Puedes usarlo así:

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

Espero que esto ahorre algunos problemas a otras personas que tienen el mismo problema.

Chaqueta
fuente
Desarrollo adicional de la historia para aquellos que estén interesados: forum.percona.com/…
Jacket
1

Debe actualizar a Mysql v5.6 + en esa versión innodb también tiene soporte para la tabla de suma de comprobación. http://dev.mysql.com/doc/refman/5.6/en/checksum-table.html

aparte de eso, la solución ideal sería si su cliente no estuviera sondeando los resultados constantemente, sino que usted estuviera empujando datos nuevos y modificados cuando y si estuvieran disponibles. Sería más rápido y habría menos carga en el servidor. si está utilizando una interfaz gráfica de usuario basada en la web, debe buscar APE http://ape-project.org/ u otros proyectos similares.

Gamesh
fuente
Desafortunadamente, esto es un asesino de rendimiento. La suma de comprobación se compone de hash todas las filas una por una . De los documentos: "Este cálculo fila por fila es lo que obtienes con la cláusula EXTENDED, con InnoDB y todos los demás motores de almacenamiento que no sean MyISAM, y con tablas MyISAM no creadas con la cláusula CHECKSUM = 1" :-(
LSerni
1

Si está agregando principalmente a una tabla, puede conectar AUTO_INCREMENT como una medida de actualización.

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

Pero preferiría referirme a una fuente externa como un contador en Memcached que incrementará cada vez que cambie algo en la base de datos.

sanmai
fuente
0

Puede intentar hacer lo siguiente:

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

Esto devuelve un número que aumenta con cada actualización de la tabla, y realizar un seguimiento permitirá detectar cambios.

Nota importante: el valor se cambia inmediatamente después de una ACTUALIZACIÓN, no después de COMPROMISO. Por lo tanto, es posible que no vea los cambios si las modificaciones se realizaron dentro de otra transacción que no finalizó.

Romuald Brunet
fuente
0

Esta respuesta no tiene nada que ver con las versiones o tipos de bases de datos mysql, quería saber si las declaraciones de actualización estaban haciendo cambios Y hacer esto en mi código php ...

  1. Creé una tabla ficticia con un registro y un campo que consultaría para obtener el valor de current_timestamp de mysql.

  2. A la tabla de datos que se está actualizando, se agregó un campo de marca de tiempo y se usó la opción mysql "ON UPDATE CURRENT_TIMESTAMP"

  3. Comparado # 1 y # 2

Esto no funcionará el 100% del tiempo, pero para mi aplicación fue una solución simple y excelente. Espero que esto ayude a alguien

Steve Padgett
fuente