วิธีที่เร็วที่สุดในการตรวจสอบว่าตาราง InnoDB เปลี่ยนไปหรือไม่


22

แอปพลิเคชันของฉันเข้มข้นมากฐานข้อมูล ขณะนี้ฉันใช้งาน MySQL 5.5.19 และใช้ MyISAM แต่ฉันอยู่ในขั้นตอนการโอนย้ายไปยัง InnoDB ปัญหาเดียวที่เหลืออยู่คือประสิทธิภาพการตรวจสอบ

แอปพลิเคชันของฉันทำประมาณ 500-1,000 CHECKSUM TABLEข้อความต่อวินาทีในช่วงเวลาเร่งด่วนเนื่องจากไคลเอนต์ GUI กำลังสำรวจฐานข้อมูลอย่างต่อเนื่องเพื่อการเปลี่ยนแปลง (เป็นระบบตรวจสอบดังนั้นต้องตอบสนองและรวดเร็วมาก)

ด้วย 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 โฮสติ้ง comany) มีลูกค้าที่มีตาราง InnoDB 112,000 ตาราง มันยากมากที่จะอ่าน INFORMATION_SCHEMA.TABLES ในช่วงเวลาเร่งด่วน ฉันมีข้อเสนอแนะอื่น:

หากคุณเปิดใช้งาน innodb_file_per_table และตาราง InnoDB ทั้งหมดจะถูกเก็บไว้ใน.ibdไฟล์มีวิธีที่จะตรวจสอบเวลาของการอัปเดตล่าสุด (ไม่เกินหนึ่งนาที)

สำหรับตาราง mydb.mytable ให้ทำดังต่อไปนี้ในระบบปฏิบัติการ:

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

เวลาประทับนี้มาจากระบบปฏิบัติการ คุณไม่สามารถไปผิดที่นี้

UPDATE 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
ขอบคุณสำหรับคำตอบ แต่อย่างที่ฉันบอกว่า update_time ใน information_schema.tables คือ NULL สำหรับตาราง InnoDB นอกจากนี้ฉันไม่แน่ใจว่า innodb_max_dirty_pages_pct = 0 เป็นความคิดที่ดีเพราะมันจะเสียสละประสิทธิภาพ ... ฉันกำลังคิดเกี่ยวกับวิธีแก้ปัญหาด้วยทริกเกอร์เพื่อแทรกค่าสุ่มที่ตารางอ้างอิงสำหรับแต่ละตารางที่ดู แต่แล้ว ฉันต้องการทริกเกอร์ 3 ตัวต่อตารางสำหรับสิ่งนี้เท่านั้น
แจ็คเก็ต

data_schema.tables ก็ช้าเช่นกัน ... ฉันใช้เวลาประมาณ 300 มิลลิวินาทีในการตรวจสอบหนึ่งตาราง สำหรับการเปรียบเทียบการทำ "ตารางตรวจสอบ" บนตาราง MyISAM ที่มีแถวนับล้านที่เปิดใช้งาน Live Checksum นั้นใช้เวลาน้อยกว่าหนึ่งวินาที
แจ็คเก็ต

2
+1 สำหรับการตรวจสอบระบบไฟล์ตราบใดที่การล้างบัฟเฟอร์เป็นปกติเพียงพอ (ประมาณหนึ่งครั้งต่อวินาทีเป็นค่าเริ่มต้น) ดังนั้นการประทับเวลานี้จะค่อนข้างแม่นยำและอาจดีพอสำหรับกรณีส่วนใหญ่ ...
Dave Rix

1
อาจจะโอเคสำหรับฐานข้อมูลท้องถิ่น แต่ฉันมีทาสระยะไกลหลายตัวดังนั้นนี่จึงใช้งานไม่ได้ ...
Jacket

3

ฉันคิดว่าฉันพบวิธีแก้ปัญหาแล้ว บางครั้งฉันได้ดูที่เซิร์ฟเวอร์ Percona เพื่อแทนที่เซิร์ฟเวอร์ 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

นอกจากนั้นโซลูชันที่เหมาะสมที่สุดคือหากลูกค้าของคุณไม่ได้ทำการสำรวจเพื่อให้ได้ผลลัพธ์อย่างต่อเนื่อง แต่คุณจะต้องผลักดันข้อมูลใหม่และข้อมูลที่มีการเปลี่ยนแปลงเมื่อใดและพร้อมใช้งานหรือไม่ มันจะเร็วขึ้นและโหลดน้อยลงบนเซิร์ฟเวอร์ หากคุณใช้ GUI บนเว็บคุณควรดู APE http://ape-project.org/หรือโครงการอื่นที่คล้ายคลึงกัน


น่าเสียดายที่นี่เป็นนักฆ่าประสิทธิภาพ การตรวจสอบที่ถูกสร้างขึ้นโดย hashing แถวทั้งหมดหนึ่งโดยหนึ่ง จากเอกสาร: "การคำนวณแบบแถวต่อแถวนี้เป็นสิ่งที่คุณได้รับพร้อมกับส่วนขยายพิเศษที่มี 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';

แต่ฉันต้องการอ้างถึงแหล่งข้อมูล otside เช่นตัวนับใน Memcached ซึ่งคุณจะเพิ่มขึ้นทุกครั้งที่คุณเปลี่ยนแปลงบางสิ่งในฐานข้อมูล


0

คุณสามารถลองทำสิ่งต่อไปนี้:

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

ส่งคืนตัวเลขที่เพิ่มขึ้นเมื่ออัปเดตแต่ละตารางการติดตามข้อมูลจะช่วยให้สามารถตรวจจับการเปลี่ยนแปลงได้

หมายเหตุสำคัญ: ค่าจะเปลี่ยนทันทีหลังจาก UPDATE ไม่ใช่หลัง COMMIT ดังนั้นคุณอาจไม่เห็นการเปลี่ยนแปลงหากมีการแก้ไขภายในธุรกรรมอื่นที่ไม่เสร็จสิ้น


0

คำตอบนี้ไม่เกี่ยวกับรุ่นหรือชนิดฐานข้อมูล mysql ฉันต้องการทราบว่าคำสั่งการปรับปรุงกำลังทำการเปลี่ยนแปลงและจะทำเช่นนี้ในรหัส PHP ของฉัน ..

  1. สร้างตารางจำลองที่มีหนึ่งระเบียนและหนึ่งเขตข้อมูลซึ่งฉันต้องการสอบถามเพื่อรับค่า current_timestamp ของ mysql

  2. ในตารางข้อมูลที่กำลังอัปเดตเพิ่มเขตเวลาและใช้ตัวเลือก mysql "ในการอัปเดต CURRENT_TIMESTAMP"

  3. เปรียบเทียบ # 1 และ # 2

นี่จะใช้งานไม่ได้ 100% แต่สำหรับแอปพลิเคชันของฉันมันเป็นวิธีที่ง่ายและยอดเยี่ยม หวังว่านี่จะช่วยใครซักคน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.