Cara tercepat untuk memeriksa apakah tabel InnoDB telah berubah

22

Aplikasi saya sangat intensif basis data. Saat ini, saya menjalankan MySQL 5.5.19 dan menggunakan MyISAM, tetapi saya sedang dalam proses migrasi ke InnoDB. Satu-satunya masalah yang tersisa adalah kinerja checksum.

Aplikasi saya melakukan sekitar 500-1000 CHECKSUM TABLEpernyataan per detik di masa puncaknya, karena GUI klien sedang mem-polling database secara konstan untuk perubahan (ini adalah sistem pemantauan, jadi harus sangat responsif dan cepat).

Dengan MyISAM, ada checksum langsung yang dihitung sebelumnya pada modifikasi tabel dan SANGAT cepat. Namun, tidak ada yang seperti itu di InnoDB. Jadi, CHECKSUM TABLESANGAT lambat.

Saya berharap dapat memeriksa waktu pembaruan terakhir dari tabel, Sayangnya, ini tidak tersedia di InnoDB juga. Saya buntu sekarang, karena tes telah menunjukkan bahwa kinerja aplikasi turun drastis.

Ada terlalu banyak baris kode yang memperbarui tabel, jadi menerapkan logika dalam aplikasi untuk mencatat perubahan tabel adalah hal yang mustahil.

Apakah ada metode cepat untuk mendeteksi perubahan dalam tabel InnoDB?

Jaket
sumber

Jawaban:

15

Untuk tabel mydb.mytable, jalankan kueri ini:

SELECT update_time
FROM information_schema.tables
WHERE table_schema='mydb'
AND table_name='mytable';

Jika Anda ingin tahu tabel apa yang telah berubah dalam 5 menit terakhir, jalankan ini:

SELECT table_schema,table_name,update_time
FROM information_schema.tables
WHERE update_time > (NOW() - INTERVAL 5 MINUTE);

Cobalah !!!

UPDATE 2011-12-21 20:04 EDT

Majikan saya (perusahaan hosting DB / Wweb) memiliki klien dengan 112.000 tabel InnoDB. Sangat sulit untuk membaca INFORMATION_SCHEMA.TABLES selama jam sibuk. Saya punya saran alternatif:

Jika Anda telah mengaktifkan innodb_file_per_table dan semua tabel InnoDB disimpan dalam .ibdfile, ada cara untuk memastikan waktu pembaruan terakhir (hingga menit).

Untuk tabel mydb.mytable, lakukan hal berikut di sistem operasi:

$ cd /var/lib/mysql/mydb
$ ls -l mytable.ibd | awk '{print $4,$5}'

Stempel waktu ini berasal dari OS. Anda tidak bisa salah dalam hal ini.

UPDATE 2011-12-21 22:04 EDT [mysqld] innodb_max_dirty_pages_pct = 0;

Tambahkan ini ke my.cnf, restart mysql, dan semua tabel InnoDB akan mengalami flushes cepat dari kumpulan buffer.

Untuk menghindari memulai kembali, jalankan saja

mysql> SET GLOBAL innodb_max_dirty_pages_pct=0;

UPDATE 2013-06-27 07:15 EDT

Ketika datang untuk mengambil tanggal dan waktu untuk suatu file, ls memiliki --time-styleopsi:

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

Anda dapat membandingkan timestamp file dengan UNIX_TIMESTAMP (SEKARANG ()) .

RolandoMySQLDBA
sumber
Anda yakin tidak salah dengan moddate idb? Perubahan bisa saja tinggal di buffer pool dalam memori dan belum sempat di-flush ke disk.
atxdba
6
Terima kasih atas jawabannya, tetapi seperti yang saya katakan, update_time di information_schema.tables adalah NULL untuk tabel InnoDB. Juga saya tidak yakin bahwa innodb_max_dirty_pages_pct = 0 adalah ide yang baik, karena itu akan mengorbankan kinerja ... Saya sedang memikirkan solusi dengan pemicu, untuk memasukkan nilai acak pada tabel referensi untuk setiap tabel yang ditonton, tapi kemudian saya akan membutuhkan 3 pemicu per tabel hanya untuk ini ...
Jacket
Juga memilih dari information_schema.tables juga agak lambat ... saya membutuhkan sekitar 300ms untuk memeriksa satu tabel. Sebagai perbandingan, melakukan "CHECKSUM TABLE" pada tabel MyISAM dengan jutaan baris dengan Live Checksum diaktifkan membutuhkan waktu kurang dari satu milidetik.
Jaket
2
+1 untuk pemeriksaan sistem file, asalkan buffer flushing cukup teratur (kira-kira sekali per detik adalah default), maka cap waktu ini akan cukup akurat, dan mungkin cukup baik untuk sebagian besar kasus ...
Dave Rix
1
Mungkin tidak apa-apa untuk database lokal, tetapi saya punya beberapa budak jarak jauh, jadi ini tidak berfungsi ...
Jacket
3

Saya pikir saya sudah menemukan solusinya. Untuk beberapa waktu saya melihat Percona Server untuk mengganti server MySQL saya, dan sekarang saya pikir ada alasan bagus untuk ini.

Server Percona memperkenalkan banyak tabel INFORMATION_SCHEMA baru seperti INNODB_TABLE_STATS, yang tidak tersedia di server MySQL standar. Saat kamu melakukan:

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

Anda mendapatkan jumlah baris aktual dan penghitung. The dokumentasi resmi mengatakan berikut tentang bidang ini:

Jika nilai kolom yang dimodifikasi melebihi "rows / 16" atau 2000000000, perhitungan ulang statistik dilakukan ketika innodb_stats_auto_update == 1. Kita dapat memperkirakan usia statistik dengan nilai ini.

Jadi penghitung ini membungkus sesekali, tetapi Anda dapat membuat checksum dari jumlah baris dan penghitung, dan kemudian dengan setiap modifikasi tabel Anda mendapatkan checksum yang unik. Misalnya:

SELECT MD5(CONCAT(rows,'_',modified)) AS checksum FROM information_schema.innodb_table_stats WHERE table_schema='db' AND table_name='table';

Saya akan melakukan upgrade server saya ke server Percona jadi ini tidak masalah bagi saya. Mengelola ratusan pemicu dan menambahkan bidang ke tabel adalah masalah besar untuk aplikasi ini, karena sangat terlambat dalam pengembangan.

Ini adalah fungsi PHP yang saya buat untuk memastikan bahwa tabel dapat dicek dengan mesin apa pun dan server yang digunakan:

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

Anda bisa menggunakannya seperti ini:

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

Saya harap ini menghemat masalah bagi orang lain yang memiliki masalah yang sama.

Jaket
sumber
Pengembangan cerita lebih lanjut untuk mereka yang tertarik: forum.percona.com/...
Jacket
1

Anda harus memperbarui ke Mysql v5.6 + pada versi itu innodb juga memiliki dukungan untuk tabel checksum. http://dev.mysql.com/doc/refman/5.6/en/checksum-table.html

selain itu, solusi ideal adalah jika klien Anda tidak melakukan polling untuk hasil secara konstan, tetapi sebaliknya Anda yang mendorong data baru dan yang diubah kapan dan jika itu tersedia. Itu akan lebih cepat dan lebih sedikit memuat di server. jika Anda menggunakan gui berbasis web, Anda harus melihat ke APE http://ape-project.org/ atau proyek serupa lainnya.

Gamesh
sumber
Sayangnya, ini adalah pembunuh kinerja. Checksum dibuat dengan mem - hashing semua baris satu per satu . Dari dokumen: "Perhitungan baris-demi-baris ini adalah apa yang Anda dapatkan dengan klausa yang DIPERPANJANG, dengan InnoDB dan semua mesin penyimpanan lain selain MyISAM, dan dengan tabel MyISAM yang tidak dibuat dengan klausa CHECKSUM = 1" :-(
LSerni
1

Jika Anda sebagian besar menambahkan ke tabel, Anda bisa mengaitkan AUTO_INCREMENT sebagai ukuran pembaruan.

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

Tapi saya lebih suka merujuk ke sumber otside seperti penghitung di Memcached yang akan Anda tambahkan setiap kali Anda mengubah sesuatu di database.

sanmai
sumber
0

Anda dapat mencoba melakukan hal berikut:

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

Ini mengembalikan angka yang meningkat dengan setiap pembaruan tabel, melacaknya akan memungkinkan untuk mendeteksi perubahan.

Catatan penting: nilainya diubah segera setelah UPDATE, bukan setelah COMMIT. Jadi, Anda mungkin tidak melihat perubahan jika modifikasi dilakukan di dalam transaksi lain yang tidak selesai.

Romuald Brunet
sumber
0

Jawaban ini tidak ada hubungannya dengan versi atau tipe database mysql, saya ingin tahu apakah pernyataan pembaruan membuat perubahan DAN melakukan ini dalam kode php saya ..

  1. Membuat tabel dummy dengan satu catatan dan satu bidang yang ingin saya kueri untuk mendapatkan nilai current_timestamp mysql.

  2. Ke tabel data yang sedang diperbarui, tambahkan bidang cap waktu dan gunakan opsi mysql "ON UPDATE CURRENT_TIMESTAMP"

  3. Dibandingkan # 1 dan # 2

Ini tidak akan berfungsi 100% dari waktu tetapi untuk aplikasi saya itu adalah solusi yang sederhana dan bagus. Semoga ini bisa membantu seseorang

Steve Padgett
sumber