Самый быстрый способ проверить, изменилась ли таблица InnoDB


22

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

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

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

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

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

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

Ответы:


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 ПО ВОСТОЧНОМУ ВРЕМЕНИ

У моего работодателя (хостинг 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}'

Эта временная метка от ОС. Вы не можете ошибиться в этом.

ОБНОВЛЕНИЕ 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 ПО ВОСТОЧНОМУ ВРЕМЕНИ

Когда дело доходит до получения даты и времени для файла, у ls есть --time-styleопция:

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

Вы можете сравнить временную метку файла с UNIX_TIMESTAMP (NOW ()) .


Вы уверены, что не ошибетесь с моддатом idb? Изменением может быть только сохранение в пуле буферов в памяти и еще не запись на диск.
atxdba

6
Спасибо за ответ, но, как я уже сказал, update_time в information_schema.tables имеет значение NULL для таблиц InnoDB. Также я не уверен, что innodb_max_dirty_pages_pct = 0 - хорошая идея, потому что она жертвует производительностью ... Я думал о решении с триггерами, чтобы вставить случайное значение в справочную таблицу для каждой из наблюдаемых таблиц, но потом Мне нужно 3 триггера на таблицу только для этого ...
Куртка

Кроме того, выбор из information_schema.tables тоже довольно медленный ... мне требуется около 300 мсек, чтобы проверить одну таблицу. Для сравнения, выполнение таблицы CHECKSUM TABLE для таблицы MyISAM с миллионами строк с включенной Live Checksum занимает менее миллисекунды.
Куртка

2
+1 для проверки файловой системы, если очистка буфера достаточно регулярна (по умолчанию примерно один раз в секунду), тогда эта отметка времени будет довольно точной и, вероятно, достаточно хорошей для большинства случаев ...
Дейв Рикс

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

3

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

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

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/en/checksum-table.html

кроме того, идеальным решением было бы, если бы ваш клиент не опрашивал результаты постоянно, а вместо этого вы отправляли новые и измененные данные, когда и если они были доступны. Это было бы быстрее и меньше нагрузки на сервер. Если вы используете веб-интерфейс, вам следует обратиться к APE http://ape-project.org/ или другим подобным проектам.


К сожалению, это убийца производительности. Контрольная сумма составляется путем хеширования всех рядов один за другим . Из документов: «Это построчное вычисление - это то, что вы получаете с предложением EXTENDED, с InnoDB и всеми другими механизмами хранения, кроме MyISAM, и с таблицами MyISAM, не созданными с предложением CHECKSUM = 1» :-(
LSerni

1

Если вы в основном добавляете в таблицу, вы можете подключить AUTO_INCREMENT в качестве меры обновления.

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

Но я бы предпочел сослаться на исходный источник, такой как счетчик в Memcached, который вы будете увеличивать при каждом изменении чего-либо в базе данных.


0

Вы можете попробовать сделать следующее:

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

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

Важное примечание: значение изменяется сразу после ОБНОВЛЕНИЯ, а не после COMMIT. Таким образом, вы можете не увидеть изменения, если изменения были сделаны внутри другой транзакции, которая не была завершена.


0

Этот ответ не имеет ничего общего с версиями или типами баз данных MySQL, я хотел знать, вносили ли изменения операторы обновления И делать это в моем php-коде ..

  1. Создала фиктивную таблицу с одной записью и одним полем, которые я бы запросил, чтобы получить значение mysql current_timestamp.

  2. В обновляемую таблицу данных добавили поле временной метки и использовали опцию mysql «ON UPDATE CURRENT_TIMESTAMP»

  3. По сравнению № 1 и № 2

Это не будет работать 100% времени, но для моего приложения это было простое и отличное решение. Надеюсь, это кому-нибудь поможет

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.