ป้องกันการรีเซ็ต auto_increment id ในฐานข้อมูล Innodb หลังจากเซิร์ฟเวอร์รีสตาร์ท


11

ฉันเพิ่งอ่านว่าเนื่องจาก InnoDB คำนวณค่า AUTO_INCREMENT ใหม่เมื่อเซิร์ฟเวอร์รีสตาร์ตระเบียนใด ๆ ที่อยู่ท้ายสุดของรายการรหัสอาจมีการนำ ID กลับมาใช้ใหม่

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

แต่ฉันจงใจปล่อยให้โพสต์ฟอรัมของพวกเขากำพร้าติดป้ายว่า "โพสต์โดย = ผู้ใช้ # 123 =" ดังนั้นการสนทนาที่ผ่านมาจะถูกเก็บไว้ เห็นได้ชัดว่าควรใช้ ID ซ้ำนี่จะเป็นปัญหา

ฉันไม่เคยมีปัญหานี้มาก่อนเพราะมีผู้ใช้ใหม่เพียงพอที่จะทำให้ ID ไม่สามารถนำมาใช้ซ้ำได้ด้วยวิธีนี้ อย่างไรก็ตามในการสมัครสมาชิกโครงการใหม่ของฉันนั้นหายากและการลบผู้ใช้ที่ไม่ได้ใช้งานบ่อย (โดยเฉพาะอย่างยิ่งเนื่องจากบัญชี "Open Alpha" ใช้งานได้ล่าสุดเป็นเวลาสามวันในการแสดงตัวอย่าง) และการใช้ ID ดังกล่าวได้เกิดขึ้นสามครั้งในขณะนี้

ฉันได้ "แก้ไข" ปัญหาด้วยการบันทึกค่าที่ถูกต้องสำหรับ AUTO_INCREMENT ที่อื่นและใช้สิ่งนั้นแทนการพึ่งพาค่าภายใน มีวิธีการที่แท้จริงที่จะให้ InnoDB จดจำค่าสุดท้ายจริงหรือไม่


คุณมีบทความที่คุณอ่านหรือไม่?
gbn

@gbn ลิงก์สำหรับบทความdev.mysql.com/doc/refman/5.1/en/…
Naveen Kumar

สำหรับการอ้างอิงนี่คือbugs.mysql.com/bug.php?id=199
Laurynas Biveinis

ALTER TABLE table_name ENGINE = MyISAM เหมาะกับฉัน โต๊ะของเรามีขนาดเล็กมากดังนั้นจึงไม่จำเป็นต้องใช้ InnoDB

1
@QuickFix คุณควรเพิ่มรายละเอียดบางอย่างเกี่ยวกับสาเหตุที่ใช้งานได้
Max Vernon

คำตอบ:


5

(หลีกเลี่ยงปัญหาโดยไม่ต้องลบ)

เนื่องจากคุณต้องการเก็บ"Posted by =User #123="ข้อมูลหลังจากลบผู้ใช้ด้วยid=123คุณสามารถลองใช้ตาราง 2 ตารางเพื่อจัดเก็บข้อมูลผู้ใช้ หนึ่งรายการสำหรับActiveผู้ใช้และอีกหนึ่งรายการสำหรับทั้งหมด (รวมถึงรายการที่ถูกลบจากผู้ใช้ที่ใช้งานอยู่) และอย่าลบรหัสเหล่านั้นออกจากAllUserตาราง:

CREATE TABLE AllUser
( user_id INT AUTO_INCREMENT
, ...
, PRIMARY KEY (user_id)
) ;

------
--- Forum posts FK should reference the `AllUser` table

CREATE TABLE ActiveUser
( user_id INT 
, ...
, PRIMARY KEY (user_id)
, FOREIGN KEY (user_id)
    REFERENCES AllUser (user_id)
) ;

------
--- All other FKs should reference the `ActiveUser` table

หลักสูตรนี้จะทำให้การแทรกของผู้ใช้ใหม่มีความซับซ้อน ผู้ใช้ใหม่ใด ๆ จะหมายถึง 2 ส่วนแทรกหนึ่งอันในแต่ละตาราง การลบผู้ใช้แม้ว่าจะเป็นการลบจากActiveUserตารางเท่านั้น FKs ทั้งหมดจะถูกลบด้วยการเรียงซ้อนยกเว้นการโพสต์ในฟอรัมซึ่งจะอ้างอิงAlluserตาราง (ที่ไม่มีการลบจะเกิดขึ้น)


4

ไม่มีวิธีที่เป็นธรรมชาติในการทำเช่นนี้ยกเว้นการใช้ information_schema.tables เพื่อบันทึกคอลัมน์ทั้งหมดด้วยตัวเลือก auto_increment

คุณสามารถรวบรวมคอลัมน์เหล่านี้ได้ดังนี้:

CREATE TABLE mysql.my_autoinc ENGINE=MyISAM
SELECT table_schema,table_name,auto_increment
FROM information_schema.tables WHERE 1=2;
ALTER TABLE mysql.my_autoinc ADD PRIMARY KEY (table_schema,table_name);
INSERT INTO mysql.my_autoinc
SELECT table_schema,table_name,auto_increment
FROM information_schema.tables WHERE auto_increment IS NOT NULL;

สร้างสคริปต์ที่จะรีเซ็ตค่า auto_increment

AUTOINC_SCRIPT=/var/lib/mysql/ResetAutoInc.sql
mysql -u... -p... -AN -e"SELECT CONCAT('ALTER TABLE ',table_schema,'.',table_name,' AUTO_INCREMENT=',auto_increment,';') FROM mysql.my_autoinc" > ${AUTOINC_SCRIPT}

จากนั้นคุณสามารถทำหนึ่งในสองสิ่งต่อไปนี้:

OPTION # 1: เรียกใช้สคริปต์ด้วยตนเองหลังจากเริ่มต้น

mysql> source /var/lib/mysql/ResetAutoInc.sql

OPTION # 2: ให้ mysqld ทำงานสคริปต์ก่อนอนุญาตการเชื่อมต่อ

คุณจะต้องเพิ่มตัวเลือกนี้

[mysqld]
init-file=/var/lib/mysql/ResetAutoInc.sql

ด้วยวิธีนี้ทุกครั้งที่คุณรีสตาร์ท mysql สคริปต์นี้จะถูกเรียกใช้งานในตอนแรก คุณจะต้องจำไว้ว่าจะสร้าง /var/lib/mysql/ResetAutoInc.sql ใหม่ก่อนที่จะทำการรีสตาร์ท mysql ตามแผน


3

5.5 เอกสารแนะนำการจัดเก็บค่าเพิ่มอัตโนมัติอื่น ๆ ที่คุณมีอยู่แล้ว

ทางเลือกอื่นคือการจำลอง SEQUENCE ดังนั้นคุณจะไม่ใช้การเพิ่มอัตโนมัติในตารางจริง นี้ได้รับการกล่าวถึงในดังนั้นก่อนและอีกครั้ง บล็อก MySQL ผลการดำเนินงานกล่าวถึงมัน

ยังมีอีกข้อมูล MySQL ที่คาดการณ์ว่า RDBMS อื่นไม่มี ...


2

อย่าลบผู้ใช้ ความซื่อสัตย์สัมพันธ์นั้นสำคัญกว่า หากคุณต้องด้วยเหตุผลความเป็นส่วนตัวหรืออะไรก็ตามเพียงแค่เปลี่ยนชื่อผู้ใช้เป็น 'ลบ' และล้างฟิลด์อื่น ๆ


1

นี่เป็นคำถามเก่าและยังเกี่ยวข้อง

1) พฤติกรรมนี้กำลังได้รับการแก้ไขใน Mysql 8.0

2) วิธีแก้ปัญหาหนึ่งคือการใช้แถวจำลองกับข้อมูลของคุณเพื่อให้ AUTO_INCREMENT อยู่เหนือค่าที่แน่นอน ไม่สะดวกอย่างยิ่งขึ้นกับสิ่งที่คุณกำลังเก็บ แต่เป็นวิธีแก้ปัญหาง่ายๆในบางกรณี


0

เรามีความต้องการสำหรับสิ่งนี้และคาดการณ์การแก้ปัญหาสำหรับระบบของเราตามคำแนะนำในโพสต์นี้ หากสิ่งนี้สามารถช่วยให้ทุกคนบรรลุเป้าหมายของพวกเขาในวิธีที่ง่ายยิ่งขึ้น

ระบบของเราใช้รูปแบบตาราง tombstone เพื่อจัดเก็บรายการที่ถูกลบเนื่องจากเราทำการซิงค์แบบ 2 ทางในระบบที่ไม่ได้เชื่อมต่อดังนั้นเราจึงใช้รหัสนี้เพื่อจับคู่ตาราง tombstone กับตารางสดและดึงค่าสูงสุดที่เป็นไปได้ :)

DROP PROCEDURE IF EXISTS `reset_auto_increments`;
DELIMITER $
CREATE PROCEDURE reset_auto_increments()
BEGIN

    DECLARE done INT DEFAULT 0;
    DECLARE schemaName VARCHAR(255) DEFAULT '';
    DECLARE liveTableName VARCHAR(255) DEFAULT '';
    DECLARE tombstoneTableName VARCHAR(255) DEFAULT '';
    DECLARE liveAutoIncrement INT DEFAULT 0;
    DECLARE tombstoneAutoIncrement INT DEFAULT 0;
    DECLARE newAutoIncrement INT DEFAULT 0;

    DECLARE autoIncrementPairs CURSOR FOR 
        SELECT
            liveTables.TABLE_SCHEMA AS schemaName,
            liveTables.TABLE_NAME AS liveTable, 
            tombstoneTables.TABLE_NAME AS tombstoneTable,
            liveTables.AUTO_INCREMENT AS live_auto_increment,
            tombstoneTables.AUTO_INCREMENT AS tombstone_auto_increment,
            GREATEST(liveTables.AUTO_INCREMENT, tombstoneTables.AUTO_INCREMENT) AS new_auto_increment
        FROM 
            information_schema.tables AS liveTables
            JOIN information_schema.tables AS tombstoneTables
                ON liveTables.TABLE_SCHEMA = tombstoneTables.TABLE_SCHEMA
                    AND CONCAT('deleted', UCASE(LEFT(liveTables.TABLE_NAME, 1)), SUBSTRING(liveTables.TABLE_NAME, 2))
                        = tombstoneTables.TABLE_NAME
        WHERE
            GREATEST(liveTables.AUTO_INCREMENT, tombstoneTables.AUTO_INCREMENT) IS NOT NULL;

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    SET done = 0;

    SET schemaName = '';
    SET liveTableName = '';
    SET tombstoneTableName = '';
    SET liveAutoIncrement = 0;
    SET tombstoneAutoIncrement = 0;
    SET newAutoIncrement = 0;

    OPEN autoIncrementPairs;
    REPEAT

        FETCH autoIncrementPairs INTO 
            schemaName, 
            liveTableName, 
            tombstoneTableName, 
            liveAutoIncrement, 
            tombstoneAutoIncrement, 
            newAutoIncrement;

        SET @statement = CONCAT('ALTER TABLE ', schemaName, '.', liveTableName, ' AUTO_INCREMENT=', newAutoIncrement);
        PREPARE updateAutoIncrementStatement FROM @statement;
        EXECUTE updateAutoIncrementStatement;
        DEALLOCATE PREPARE updateAutoIncrementStatement;

    UNTIL done END REPEAT;

    CLOSE autoIncrementPairs;

END$

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