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?

Jaqueta
fonte

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 ()) .

RolandoMySQLDBA
fonte
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.

Jaqueta
fonte
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.

Gamesh
fonte
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.

sanmai
fonte
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.

Romuald Brunet
fonte
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

Steve Padgett
fonte