Cegah pengaturan ulang id auto_increment di database Innodb setelah server restart

11

Saya baru-baru ini membaca bahwa karena bagaimana InnoDB menghitung ulang nilai AUTO_INCREMENT ketika server restart, catatan apa pun di ujung atas daftar ID mungkin memiliki ID mereka digunakan kembali.

Biasanya, ini bukan masalah, karena ketika pengguna dihapus semua yang terkait dengan ID juga dihapus dari tabel lain.

Tapi saya sengaja meninggalkan posting forum mereka yatim piatu, dilabeli sebagai "Diposting oleh = Pengguna # 123 =", sehingga percakapan sebelumnya tetap dipertahankan. Jelas, jika ID digunakan kembali, ini akan menjadi masalah.

Saya belum pernah mengalami masalah ini sebelumnya karena selalu ada cukup banyak pengguna baru yang membuat ID tidak mungkin digunakan kembali dengan cara ini. Namun pada pendaftaran proyek baru saya jarang dan penghapusan pengguna tidak aktif sering (terutama karena akun "Open Alpha" hanya berlangsung selama tiga hari sebagai pratinjau), dan penggunaan kembali ID tersebut telah terjadi tiga selama tiga sekarang.

Saya telah "memperbaiki" masalah dengan menyimpan nilai yang benar untuk AUTO_INCREMENT di tempat lain dan menggunakannya daripada mengandalkan nilai internal. Apakah ada cara aktual untuk membuat InnoDB mengingat nilai terakhir aktual?

Naveen Kumar
sumber
Apakah Anda memiliki artikel yang Anda baca?
gbn
@gbn Tautan untuk artikel dev.mysql.com/doc/refman/5.1/en/…
Naveen Kumar
Untuk referensi, ini bugs.mysql.com/bug.php?id=199
Laurynas Biveinis
ALTER TABLE table_name ENGINE = MyISAM Berfungsi untuk saya. Meja kami selalu disimpan sangat kecil, jadi tidak perlu untuk InnoDB.
1
@QuickFix Anda harus menambahkan beberapa detail tentang mengapa ini bekerja.
Max Vernon

Jawaban:

5

(menghindari masalah dengan tidak pernah menghapus)

Karena Anda ingin menyimpan "Posted by =User #123="informasi setelah Anda menghapus pengguna id=123, Anda juga dapat mempertimbangkan menggunakan 2 tabel untuk menyimpan data pengguna. Satu untuk Activepengguna dan satu untuk semua (termasuk yang dihapus dari pengguna aktif). Dan jangan pernah menghapus id itu dari AllUsertabel:

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

Ini tentu saja akan mempersulit memasukkan operasi pengguna baru. Setiap pengguna baru akan berarti 2 sisipan, satu di setiap tabel. Menghapus pengguna akan dengan hanya menghapus dari ActiveUsertabel. Semua FK akan dihapus dengan cascading, kecuali posting forum, yang akan merujuk Allusertabel (di mana tidak ada penghapusan akan pernah terjadi).

ypercubeᵀᴹ
sumber
4

Tidak ada cara alami untuk melakukan ini kecuali menggunakan information_schema.tables untuk merekam semua kolom dengan opsi auto_increment.

Anda bisa mengumpulkan kolom-kolom itu sebagai berikut:

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;

Buat skrip yang akan mengatur ulang nilai 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}

Anda kemudian dapat melakukan satu dari dua hal:

OPSI # 1: Jalankan skrip secara manual setelah startup

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

OPSI # 2: Minta skrip mysqld mengeksekusi sebelum mengizinkan koneksi

Anda harus menambahkan opsi ini

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

Dengan begitu, setiap kali Anda me-restart mysql, skrip ini dijalankan di awal. Anda harus ingat untuk membuat ulang /var/lib/mysql/ResetAutoInc.sql sebelum melakukan restart mysql yang direncanakan.

RolandoMySQLDBA
sumber
3

Dokumen 5,5 menyarankan untuk menyimpan nilai kenaikan otomatis di tempat lain seperti yang sudah Anda miliki.

Solusi alternatif adalah meniru SEQUENCE sehingga Anda tidak menggunakan kenaikan otomatis dalam tabel itu sendiri. Ini telah dibahas pada SO sebelumnya dan lagi . The MySQL Kinerja blog menyebutkan itu.

Namun data MySQL lain yang tidak dimiliki RDBMS lain ...

gbn
sumber
2

Hanya saja, jangan menghapus pengguna. Integritas relasional lebih penting. Jika Anda harus melakukannya karena alasan privasi atau apa pun, cukup ubah nama pengguna menjadi 'dihapus' dan kosongkan bidang lainnya.

Jannes
sumber
1

Ini adalah pertanyaan lama dan masih relevan.

1) Perilaku ini diperbaiki di Mysql 8.0.

2) Salah satu solusinya adalah menggunakan baris dummy ke data Anda, untuk menjaga AUTO_INCREMENT di atas nilai tertentu. Tidak super nyaman tergantung pada apa yang Anda simpan, tetapi merupakan solusi sederhana dalam beberapa kasus.

Garr Godfrey
sumber
0

Kami membutuhkan solusi ekstrapolasi untuk sistem kami sendiri berdasarkan instruksi pada posting ini. Jika ini dapat membantu siapa pun mencapai tujuan mereka dengan cara yang bahkan lebih mudah.

Sistem kami menggunakan pola tabel batu nisan untuk menyimpan barang yang dihapus karena kami melakukan sinkronisasi 2 arah pada sistem yang terputus, jadi kami menggunakan kode ini untuk mencocokkan tabel batu nisan dengan tabel langsungnya dan mengekstrak nilai setinggi mungkin :)

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 ;
Mathieu Dumoulin
sumber