MySQL - 检查InnoDB表中数据是否发生变化的最快方法

Jac*_*ket 5 mysql triggers innodb checksum

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

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

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

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

有更多的代码行更新表,因此在应用程序中实现逻辑表更改是不可能的......

数据库生态系统由一个主na 3从属组成,因此不能选择本地文件检查.我想到了一种模拟校验和缓存的方法 - 一个包含两列的查找表 - table_name,checksum,并在表中发生更改时更新该表和触发器,但我有大约100个要监视的表,这意味着每个表有3个触发器= 300个触发器.很难维护,我不确定这不会再次成为一次表现.

那么有没有任何FAST方法来检测InnoDB表中的变化?

谢谢!

Jac*_*ket 2

我想我已经找到了解决方案。一段时间以来,我一直在寻找 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