检查InnoDB表是否已更改的最快方法


22

我的应用程序非常耗费数据库。当前,我正在运行MySQL 5.5.19并使用MyISAM,但是我正在迁移到InnoDB。剩下的唯一问题是校验和性能。

我的应用程序CHECKSUM TABLE在高峰时间内每秒执行约500-1000条语句,因为客户端GUI不断轮询数据库以查找更改(它是一个监视系统,因此必须非常敏感且快速)。

使用MyISAM,可以在修改表时预先计算出实时校验和,而且校验和非常快。但是,InnoDB中没有这样的东西。所以,CHECKSUM TABLE非常慢。

我希望能够检查表的最后更新时间,不幸的是,这在InnoDB中也不可用。我被困住了,因为测试表明应用程序的性能急剧下降。

太多的代码行更新了表,因此在应用程序中实现逻辑以记录表更改就成为不可能了。

有什么快速的方法可以检测InnoDB表中的更改?

Answers:


15

对于表mydb.mytable,运行以下查询:

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

如果您想知道最近5分钟内哪些表已更改,请运行以下命令:

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

试试看 !!!

更新2011-12-21 20:04 EDT

我的雇主(DB / Wweb托管comany)的客户拥有112,000个InnoDB表。在高峰时段很难阅读INFORMATION_SCHEMA.TABLES。我还有一个建议:

如果启用了innodb_file_per_table并且所有InnoDB表都存储在.ibd文件中,则有一种方法可以确定上一次更新的时间(直到分钟)。

对于表mydb.mytable,在操作系统中执行以下操作:

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

该时间戳来自操作系统。您不能在这一方面犯错。

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

将其添加到my.cnf中,重新启动mysql,所有InnoDB表都会从缓冲池中快速刷新。

为避免重启,只需运行

mysql> SET GLOBAL innodb_max_dirty_pages_pct=0;

更新2013-06-27 07:15 EDT

当要获取文件的日期和时间时,ls可以--time-style选择:

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

您可以将文件的时间戳与UNIX_TIMESTAMP(NOW())进行比较


您确定使用idb moddate不会出错吗?更改可能只存在于内存的缓冲池中,而尚未刷新到磁盘。
atxdba 2011年

6
感谢您的回答,但正如我所说,InnoDB表的information_schema.tables中的update_time为NULL。另外我不确定innodb_max_dirty_pages_pct = 0是否是个好主意,因为它会牺牲性能...我正在考虑使用触发器的解决方案,在每个观察表的参考表中插入随机值,但是我只需要为此每桌3个触发器...
夹克

另外,从information_schema.tables中选择也是有点慢...我花了大约300ms来检查一张桌子。为了进行比较,在启用了实时校验和的数百万行的MyISAM表上执行“ CHECKSUM TABLE”花费的时间不到一毫秒。
夹克

2
+1用于文件系统检查,只要缓冲区刷新足够规则(默认情况下每秒大约一次),则此时间戳将非常准确,并且在大多数情况下可能已经足够好了
Dave Rix

1
也许对于本地数据库来说还可以,但是我有多个远程从站,所以这不起作用...
Jacket

3

我想我已经找到了解决方案。一段时间以来,我一直在寻找Percona服务器来替换我的MySQL服务器,现在我认为这样做有充分的理由。

Percona服务器引入了许多新的INFORMATION_SCHEMA表,例如INNODB_TABLE_STATS,在标准MySQL服务器中不可用。当您这样做时:

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

您将获得实际的行数和一个计数器。在正式文件说,有关这一领域的情况如下:

如果修改后的列的值超过“行/ 16”或2000000000,则当innodb_stats_auto_update == 1时,将进行统计重新计算。

因此,此计数器每隔一段时间会自动换行,但是您可以对行数和计数器进行校验和,然后对表进行每次修改都会得到唯一的校验和。例如:

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

无论如何,我都打算将服务器升级到Percona服务器,所以这对我来说不是问题。管理该应用程序的主要难题是管理数百个触发器并将字段添加到表中,因为它尚处于开发后期。

这是我提供的PHP函数,以确保无论使用哪种引擎和服务器,都可以对表进行校验和:

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

您可以像这样使用它:

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

我希望这可以为遇到相同问题的其他人节省一些麻烦。


对于感兴趣的人,进一步的故事发展是:forum.percona.com/…–
夹克

1

您应该更新到该版本的Mysql v5.6 +,innodb也支持校验和表。 http://dev.mysql.com/doc/refman/5.6/zh-CN/checksum-table.html

除此之外,理想的解决方案是,如果您的客户端不是持续轮询结果,而是在可用时推送新数据和更改数据。这样会更快,服务器上的负载也会更少。如果您使用的是基于Web的gui,则应查看APE http://ape-project.org/或其他类似项目。


不幸的是,这是一个性能杀手。校验和是通过将所有行一个个地散列来组成的。来自文档:“此逐行计算是您使用EXTENDED子句,使用InnoDB和MyISAM以外的所有其他存储引擎以及未使用CHECKSUM = 1子句创建的MyISAM表所获得的” :-(
LSerni

1

如果您主要是添加到表中,则可以挂钩AUTO_INCREMENT作为更新的度量。

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

但是我更喜欢引用otside源,例如Memcached中的计数器,每次更改数据库中的内容时,它都会增加。


0

您可以尝试执行以下操作:

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

这将返回一个数字,该数字随着表的每次更新而增加,对其进行跟踪可以检测到更改。

重要说明:该值将在UPDATE之后立即更改,而不是在COMMIT之后更改。因此,如果修改是在另一个未完成的事务中进行的,则您可能看不到更改。


0

这个答案与mysql数据库版本或类型无关,我想知道更新语句是否在进行更改,并在我的php代码中执行此操作。

  1. 创建了一个虚拟表,其中有一条记录和一个字段,我将查询该表以获得mysql的current_timestamp的值。

  2. 向要更新的数据表中,添加一个时间戳字段,并使用mysql选项“ ON UPDATE CURRENT_TIMESTAMP”

  3. 比较#1和#2

这不会100%地起作用,但是对于我的应用程序来说,这是一个简单而出色的解决方案。希望这对某人有帮助

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.