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

Jac*_*ket 27 mysql innodb performance

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

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

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

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

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

是否有任何快速方法来检测 InnoDB 表中的变化?

Rol*_*DBA 24

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

SELECT update_time
FROM information_schema.tables
WHERE table_schema='mydb'
AND table_name='mytable';
Run Code Online (Sandbox Code Playgroud)

如果您想知道过去 5 分钟内哪些表发生了变化,请运行以下命令:

SELECT table_schema,table_name,update_time
FROM information_schema.tables
WHERE update_time > (NOW() - INTERVAL 5 MINUTE);
Run Code Online (Sandbox Code Playgroud)

试一试 !!!

更新 2011-12-21 20:04 EDT

我的雇主(DB/Wweb 托管公司)有一个拥有 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}'
Run Code Online (Sandbox Code Playgroud)

此时间戳来自操作系统。在这一点上你不会出错。

更新 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;
Run Code Online (Sandbox Code Playgroud)

更新 2013-06-27 07:15 EDT

在检索文件的日期和时间时, ls 有以下--time-style选项:

$ cd /var/lib/mysql/mydb
$ ls -l --time-style="+%s" mytable.ibd | awk '{print $6}'
Run Code Online (Sandbox Code Playgroud)

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

  • 感谢您的回答,但正如我所说,对于 InnoDB 表,information_schema.tables 中的 update_time 为 NULL。此外,我不确定 innodb_max_dirty_pages_pct=0 是一个好主意,因为它会牺牲性能......我正在考虑一个带有触发器的解决方案,在每个被监视的表的参考表中插入一个随机值,但是然后我只需要每张表 3 个触发器... (6认同)
  • +1 用于文件系统检查,只要缓冲区刷新足够规律(默认情况下大约每秒一次),那么此时间戳将非常准确,并且对于大多数情况可能已经足够了...... (3认同)

Jac*_*ket 4

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

\n\n

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

\n\n
SELECT rows, modified FROM information_schema.innodb_table_stats WHERE table_schema=\'db\' AND table_name=\'table\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

您将获得实际行数和计数器。官方文档对此字段的说明如下:

\n\n
\n

如果修改列的值超过 \xe2\x80\x9crows / 16\xe2\x80\x9d 或 2000000000,则当 innodb_stats_auto_update == 1 时\n 会重新计算统计信息。\n 我们可以通过该值估计统计信息的旧度。

\n
\n\n

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

\n\n
SELECT MD5(CONCAT(rows,\'_\',modified)) AS checksum FROM information_schema.innodb_table_stats WHERE table_schema=\'db\' AND table_name=\'table\';\n
Run Code Online (Sandbox Code Playgroud)\n\n

无论如何,我打算将我的服务器升级到 Percona 服务器,所以这个边界对我来说不是问题。管理数百个触发器并向表添加字段是该应用程序的一大难题,因为它的开发已经很晚了。

\n\n

这是我想出的 PHP 函数,以确保无论使用什么引擎和服务器,都可以对表进行校验和:

\n\n
function checksum_table($input_tables){\n    if(!$input_tables) return false; // Sanity check\n    $tables = (is_array($input_tables)) ? $input_tables : array($input_tables); // Make $tables always an array\n    $where = "";\n    $checksum = "";\n    $found_tables = array();\n    $tables_indexed = array();\n    foreach($tables as $table_name){\n        $tables_indexed[$table_name] = true; // Indexed array for faster searching\n        if(strstr($table_name,".")){ // If we are passing db.table_name\n            $table_name_split = explode(".",$table_name);\n            $where .= "(table_schema=\'".$table_name_split[0]."\' AND table_name=\'".$table_name_split[1]."\') OR ";\n        }else{\n            $where .= "(table_schema=DATABASE() AND table_name=\'".$table_name."\') OR ";\n        }\n    }\n    if($where != ""){ // Sanity check\n        $where = substr($where,0,-4); // Remove the last "OR"\n        $get_chksum = mysql_query("SELECT table_schema, table_name, rows, modified FROM information_schema.innodb_table_stats WHERE ".$where);\n        while($row = mysql_fetch_assoc($get_chksum)){\n            if($tables_indexed[$row[table_name]]){ // Not entirely foolproof, but saves some queries like "SELECT DATABASE()" to find out the current database\n                $found_tables[$row[table_name]] = true;\n            }elseif($tables_indexed[$row[table_schema].".".$row[table_name]]){\n                $found_tables[$row[table_schema].".".$row[table_name]] = true;\n            }\n            $checksum .= "_".$row[rows]."_".$row[modified]."_";\n        }\n    }\n\n    foreach($tables as $table_name){\n        if(!$found_tables[$table_name]){ // Table is not found in information_schema.innodb_table_stats (Probably not InnoDB table or not using Percona Server)\n            $get_chksum = mysql_query("CHECKSUM TABLE ".$table_name); // Checksuming the old-fashioned way\n            $chksum = mysql_fetch_assoc($get_chksum);\n            $checksum .= "_".$chksum[Checksum]."_";\n        }\n    }\n\n    $checksum = sprintf("%s",crc32($checksum)); // Using crc32 because it\'s faster than md5(). Must be returned as string to prevent PHPs signed integer problems.\n\n    return $checksum;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

你可以这样使用它:

\n\n
// checksum a signle table in the current db\n$checksum = checksum_table("test_table");\n\n// checksum a signle table in db other than the current\n$checksum = checksum_table("other_db.test_table");\n\n// checksum multiple tables at once. It\'s faster when using Percona server, because all tables are checksummed via one select.\n$checksum = checksum_table(array("test_table, "other_db.test_table")); \n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n