Maneira mais rápida de verificar se a tabela do InnoDB mudou


22

Meu aplicativo é muito intensivo em banco de dados. Atualmente, estou executando o MySQL 5.5.19 e usando o MyISAM, mas estou migrando para o InnoDB. O único problema que resta é o desempenho da soma de verificação.

Meu aplicativo faz cerca de 500-1000 CHECKSUM TABLEinstruções por segundo nos horários de pico, porque a GUI do cliente está pesquisando constantemente o banco de dados em busca de alterações (é um sistema de monitoramento, portanto deve ser muito responsivo e rápido).

Com o MyISAM, existem somas de verificação ativas que são pré-calculadas na modificação da tabela e são MUITO rápidas. No entanto, não existe isso no InnoDB. Então, CHECKSUM TABLEé MUITO lento.

Eu esperava poder verificar o horário da última atualização da tabela. Infelizmente, isso também não está disponível no InnoDB. Estou preso agora, porque os testes mostraram que o desempenho do aplicativo cai drasticamente.

Há simplesmente muitas linhas de código que atualizam as tabelas, portanto, implementar a lógica no aplicativo para registrar alterações na tabela está fora de questão.

Existe algum método rápido para detectar alterações nas tabelas do InnoDB?

Respostas:


15

Para a tabela mydb.mytable, execute esta consulta:

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

Se você deseja saber quais tabelas foram alteradas nos últimos 5 minutos, execute o seguinte:

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

De uma chance !!!

UPDATE 2011-12-21 20:04 EDT

Meu empregador (DB / Wweb comany) tem um cliente com 112.000 tabelas InnoDB. É muito difícil ler o arquivo INFORMATION_SCHEMA.TABLES durante o horário de pico. Tenho uma sugestão alternativa:

Se você tiver o innodb_file_per_table ativado e todas as tabelas do InnoDB estiverem armazenadas em .ibdarquivos, existe uma maneira de verificar a hora da última atualização (até o minuto).

Para a tabela mydb.mytable, faça o seguinte no sistema operacional:

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

Este carimbo de data / hora é do SO. Você não pode dar errado neste.

ATUALIZAÇÃO 21/12/2011 22:04 EDT [mysqld] innodb_max_dirty_pages_pct = 0;

Adicione isso ao my.cnf, reinicie o mysql e todas as tabelas do InnoDB experimentarão liberações rápidas do buffer pool.

Para evitar reiniciar, basta executar

mysql> SET GLOBAL innodb_max_dirty_pages_pct=0;

ATUALIZAÇÃO 27-06-2013 07:15 EDT

Quando se trata de recuperar a data e a hora de um arquivo, ls tem a --time-styleopção:

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

Você pode comparar o registro de data e hora do arquivo com UNIX_TIMESTAMP (NOW ()) .


Tem certeza de que não pode dar errado com o moddate do idb? Uma mudança pode estar apenas vivendo no buffer pool na memória e não ter sido liberada para o disco ainda.
Atxdba

6
Obrigado pela resposta, mas como eu disse, update_time em information_schema.tables é NULL para tabelas do InnoDB. Também não tenho certeza de que innodb_max_dirty_pages_pct = 0 seja uma boa ideia, porque sacrificará o desempenho ... Eu estava pensando em uma solução com gatilhos, para inserir um valor aleatório em uma tabela de referência para cada uma das tabelas monitoradas, mas depois precisarei de 3 gatilhos por tabela apenas para isso ... #
Jacket Jacket

Também selecionar entre information_schema.tables é meio lento também ... eu levo cerca de 300ms para verificar uma tabela. Para comparação, fazer uma "CHECKSUM TABLE" em uma tabela MyISAM com milhões de linhas com o Live Checksum ativado está demorando menos de um milissegundo.
Jacket

2
+1 para a verificação do sistema de arquivos, desde que a liberação do buffer seja regular o suficiente (aproximadamente uma vez por segundo é o padrão), esse carimbo de hora será bastante preciso e provavelmente bom o suficiente para a maioria dos casos ...
Dave Rix

1
Talvez é OK para um banco de dados local, mas eu tenho vários escravos remotos, de modo que este não está funcionando ...
Jacket

3

Eu acho que encontrei a solução. Por algum tempo, eu estava olhando o Percona Server para substituir meus servidores MySQL, e agora acho que há uma boa razão para isso.

O servidor Percona apresenta muitas novas tabelas de INFORMAÇÕES_SCHEMA como INNODB_TABLE_STATS, que não estão disponíveis no servidor MySQL padrão. Quando você faz:

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

Você obtém uma contagem real de linhas e um contador. A documentação oficial diz o seguinte sobre este campo:

Se o valor da coluna modificada exceder “linhas / 16” ou 2000000000, o recálculo das estatísticas será feito quando innodb_stats_auto_update == 1. Podemos estimar a antiguidade das estatísticas por esse valor.

Portanto, esse contador é agrupado de vez em quando, mas você pode fazer uma soma de verificação do número de linhas e do contador e, a cada modificação da tabela, obtém uma soma de verificação exclusiva. Por exemplo:

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

Eu iria atualizar meus servidores para o servidor Percona de qualquer maneira, então esse limite não é um problema para mim. Gerenciar centenas de gatilhos e adicionar campos a tabelas é um grande problema para este aplicativo, porque é muito tarde no desenvolvimento.

Esta é a função PHP que criei para garantir que as tabelas possam ser somadas de verificação, independentemente do mecanismo e servidor usado:

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

Você pode usá-lo assim:

// 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 isso economize alguns problemas para outras pessoas com o mesmo problema.


Desenvolvimento de mais histórias para aqueles que estão interessados: forum.percona.com/…
Jacket

1

Você deve atualizar para o Mysql v5.6 + nessa versão O innodb também possui suporte para a tabela de soma de verificação. http://dev.mysql.com/doc/refman/5.6/en/checksum-table.html

caso contrário, a solução ideal seria se seu cliente não estivesse pesquisando constantemente os resultados, mas em vez disso, você enviaria dados novos e alterados quando e se estivessem disponíveis. Seria mais rápido e menos carga estaria no servidor. Se você estiver usando o GUI baseado na Web, consulte o APE http://ape-project.org/ ou outros projetos semelhantes.


Infelizmente, este é um assassino de desempenho. A soma de verificação é composta pelo hash de todas as linhas, uma por uma . Dos documentos: "Este cálculo linha a linha é o que você obtém com a cláusula EXTENDED, com o InnoDB e todos os outros mecanismos de armazenamento que não sejam o MyISAM, e com tabelas MyISAM não criadas com a cláusula CHECKSUM = 1" :-(
LSerni

1

Se você estiver adicionando principalmente a uma tabela, poderá conectar-se a AUTO_INCREMENT como uma medida da atualidade.

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

Mas eu preferiria me referir a uma fonte do outro lado como um contador no Memcached, que você aumentará toda vez que alterar algo no banco de dados.


0

Você pode tentar fazer o seguinte:

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

Isso retorna um número que aumenta a cada atualização da tabela, mantendo o controle permitirá detectar alterações.

Nota importante: o valor é alterado imediatamente após um UPDATE, não após COMMIT. Portanto, talvez você não veja as alterações se as modificações foram feitas dentro de outra transação que não foi concluída.


0

Esta resposta não tem nada a ver com versões ou tipos de banco de dados mysql, eu queria saber se as instruções de atualização estavam fazendo alterações E fazer isso no meu código php.

  1. Criou uma tabela fictícia com um registro e um campo que gostaria de consultar para obter o valor do current_timestamp do mysql.

  2. Na tabela de dados que está sendo atualizada, adicionou um campo de carimbo de data e hora e usou a opção mysql "ON UPDATE CURRENT_TIMESTAMP"

  3. Comparado # 1 e # 2

Isso não funcionará 100% do tempo, mas para o meu aplicativo era uma solução simples e ótima. Espero que isso ajude alguém

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.