MySQL - самый быстрый способ проверить, изменились ли данные в таблице InnoDB - PullRequest
5 голосов
/ 27 декабря 2011

Мое приложение очень интенсивно использует базу данных. В настоящее время я использую MySQL 5.5.19 и использую MyISAM, но я нахожусь в процессе перехода на InnoDB. Единственная проблема - это проверка контрольной суммы.

Мое приложение выполняет около 500-1000 операторов CHECKSUM TABLE в секунду в пиковое время, потому что клиентский графический интерфейс постоянно опрашивает базу данных на предмет изменений (это система мониторинга, поэтому она должна быть очень отзывчивой и быстрой).

С MyISAM существуют контрольные суммы Live, которые предварительно рассчитываются при модификации таблицы и ОЧЕНЬ быстрые. Однако в InnoDB такого нет. Итак, CHECKSUM TABLE очень медленный ...

Я надеялся, что смогу проверить время последнего обновления таблицы. К сожалению, это также не доступно в InnoDB. Я застрял сейчас, потому что тесты показали, что производительность приложения резко падает ...

Просто слишком много строк кода, которые обновляют таблицы, поэтому о реализации логики в приложении для регистрации изменений таблиц не может быть и речи ...

Экосистема базы данных состоит из одного главного или трех подчиненных, поэтому проверка локальных файлов не возможна. Я подумал о методе имитации кеша контрольной суммы - таблицы поиска с двумя столбцами - table_name, checkum и обновлении этой таблицы с помощью триггеров, когда происходят изменения в таблице, но у меня есть около 100 таблиц для мониторинга, и это означает 3 триггера на таблицу = 300 триггеров. Трудно поддерживать, и я не уверен, что это опять не будет боровом.

Так есть ли метод FAST для обнаружения изменений в таблицах InnoDB?

Спасибо!

Ответы [ 2 ]

2 голосов
/ 27 декабря 2011

Самый простой способ - добавить обнуляемый столбец с типом TIMESTAMP с триггером: ON UPDATE CURRENT_TIMESTAMP .

Следовательно, вставки не изменятся, поскольку столбец принимает значения NULL, и вы можете выбрать только новые и измененные столбцы, сказав:

SELECT * FROM `table` WHERE `mdate` > '2011-12-21 12:31:22'

Каждый раз, когда вы обновляете строку, этот столбец будет меняться автоматически.

Вот еще немного информации: http://dev.mysql.com/doc/refman/5.0/en/timestamp.html

Чтобы увидеть удаленные строки, просто создайте триггер, который будет регистрировать каждое удаление в другой таблице:

DELIMITER $$
CREATE TRIGGER MyTable_Trigger
AFTER DELETE ON MyTable
FOR EACH ROW
BEGIN
    INSERT INTO MyTable_Deleted VALUES(OLD.id, NOW());
END$$
0 голосов
/ 02 января 2012

Я думаю, что нашел решение. Некоторое время я смотрел на Percona Server, чтобы заменить мои 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. Мы можем оценить старость статистики по этому значению.

Таким образом, этот счетчик время от времени оборачивается, но вы можете сделать контрольную сумму количества строк и счетчика, а затем при каждой модификации таблицы вы получите уникальную контрольную сумму. E.g.:

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

Надеюсь, это спасет некоторых людей от подобных проблем.

...