InnoDB 테이블이 변경되었는지 확인하는 가장 빠른 방법


22

내 응용 프로그램은 데이터베이스를 많이 사용합니다. 현재 MySQL 5.5.19를 실행 중이고 MyISAM을 사용하고 있지만 InnoDB로 마이그레이션하는 중입니다. 남아있는 유일한 문제는 체크섬 성능입니다.

CHECKSUM TABLE클라이언트 GUI가 변경을 위해 데이터베이스를 지속적으로 폴링하고 있기 때문에 내 응용 프로그램은 피크 시간에 초당 약 500-1000 개의 명령문을 수행합니다 (모니터링 시스템이므로 매우 반응적이고 빠르지 않아야 함).

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

이 타임 스탬프는 OS에서 온 것입니다. 당신은 이것에 잘못 갈 수 없습니다.

업데이트 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

6
답변 주셔서 감사하지만, 내가 말했듯이 information_schema.tables의 update_time은 InnoDB 테이블의 경우 NULL입니다. 또한 innodb_max_dirty_pages_pct = 0이 성능을 희생시킬 것이기 때문에 좋은 아이디어인지 확실하지 않습니다 ... 트리거가있는 솔루션에 대해 생각하고 있습니다. 각 테이블에 대한 참조 테이블에 임의의 값을 삽입합니다 이 경우에만 테이블 당 3 개의 트리거가 필요합니다.
Jacket

또한 information_schema.tables에서 선택하는 것도 너무 느립니다 ... 한 테이블을 확인하는 데 약 300ms가 걸립니다. Live Checksum이 활성화 된 수백만 행의 MyISAM 테이블에서 "CHECKSUM TABLE"을 수행하는 데 걸리는 시간은 1 밀리 초 미만입니다.
Jacket

2
파일 시스템 검사의 경우 +1, 버퍼 플러싱이 충분히 규칙적인 경우 (대략 초당 1 회가 기본값 임)이 타임 스탬프는 매우 정확하며 대부분의 경우에 충분할 것입니다 ...
Dave Rix

1
로컬 데이터베이스에는 문제가 없지만 여러 개의 원격 슬레이브가 있으므로 작동하지 않습니다.
Jacket

3

해결책을 찾았습니다. 얼마 동안 Percona Server에서 MySQL 서버를 교체하려고했지만 지금은 그럴만한 이유가 있다고 생각합니다.

Percona 서버는 표준 MySQL 서버에서 사용할 수없는 INNODB_TABLE_STATS와 같은 많은 새로운 INFORMATION_SCHEMA 테이블을 도입했습니다. 할 때 :

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

실제 행 수와 카운터를 얻습니다. 공식 문서는 이 분야에 대한 다음 말한다 :

수정 된 열의 값이 "rows / 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/…
Jacket

1

해당 버전에서 Mysql v5.6 +로 업데이트해야합니다. innodb는 체크섬 테이블도 지원합니다. http://dev.mysql.com/doc/refman/5.6/en/checksum-table.html

다른 경우, 이상적인 해결책은 고객이 지속적으로 결과를 폴링하지 않았을 때 대신 새로운 데이터와 변경된 데이터를 사용할 수있는 경우에 푸시하는 경우입니다. 서버에서 더 빠르고 더 적은 부하가 발생합니다. 웹 기반 GUI를 사용하는 경우 APE http://ape-project.org/ 또는 기타 유사한 프로젝트를 살펴보십시오 .


불행히도 이것은 성능 저하입니다. 체크섬은 모든 행을 하나씩 해시하여 구성됩니다 . 문서에서 : "이 행별 계산은 InnoDB 및 MyISAM 이외의 다른 모든 스토리지 엔진 및 CHECKSUM = 1 절로 작성되지 않은 MyISAM 테이블을 사용하여 EXTENDED 절을 사용하여 얻은 것입니다.":-(
LSerni

1

주로 테이블에 추가하는 경우 업데이트의 척도로 AUTO_INCREMENT를 연결할 수 있습니다.

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

그러나 Memcached의 카운터와 같은 otside 소스를 참조하여 데이터베이스에서 무언가를 변경할 때마다 증가합니다.


0

다음을 시도 할 수 있습니다.

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

이것은 각 테이블 업데이트에 따라 증가하는 숫자를 리턴하며,이를 추적하면 변경을 감지 할 수 있습니다.

중요 사항 : 값은 COMMIT 이후가 아니라 UPDATE 직후에 변경됩니다. 따라서 완료되지 않은 다른 트랜잭션에서 수정 한 경우 변경 사항이 표시되지 않을 수 있습니다.


0

이 답변은 mysql 데이터베이스 버전 또는 유형과 아무 관련이 없습니다. 업데이트 명령문이 변경되는지 여부를 알고 싶고 PHP 코드 에서이 작업을 수행하려고합니다.

  1. mysql의 current_timestamp 값을 얻기 위해 쿼리 할 하나의 레코드와 하나의 필드로 더미 테이블을 만들었습니다.

  2. 업데이트되는 데이터 테이블에 타임 스탬프 필드를 추가하고 mysql 옵션 "ON UPDATE CURRENT_TIMESTAMP"를 사용했습니다.

  3. # 1과 # 2 비교

이것은 100 % 작동하지 않지만 내 응용 프로그램에서는 간단하고 훌륭한 솔루션이었습니다. 희망이 누군가에게 도움이되기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.