Ngăn chặn đặt lại id auto_increment trong cơ sở dữ liệu Innodb sau khi máy chủ khởi động lại


11

Gần đây tôi đã đọc rằng vì cách InnoDB tính toán lại giá trị AUTO_INCREMENT khi máy chủ khởi động lại, mọi bản ghi trên danh sách ID cao cấp có thể được sử dụng lại ID của chúng.

Thông thường, đây không phải là vấn đề, vì khi người dùng bị xóa, mọi thứ liên quan đến ID cũng bị xóa khỏi các bảng khác.

Nhưng tôi cố tình để các bài đăng trên diễn đàn của họ mồ côi, được dán nhãn là "Đăng bởi = Người dùng # 123 =", để các cuộc hội thoại trong quá khứ được giữ lại. Rõ ràng, nếu một ID được sử dụng lại, đây sẽ là một vấn đề.

Tôi chưa bao giờ gặp phải vấn đề này trước đây vì luôn có đủ người dùng mới để khiến ID không thể được sử dụng lại theo cách này. Tuy nhiên, việc đăng ký dự án mới của tôi rất hiếm khi xảy ra và người dùng không hoạt động thường xuyên bị xóa (đặc biệt là khi tài khoản "Open Alpha" chỉ tồn tại trong ba ngày dưới dạng xem trước) và việc sử dụng lại ID như vậy đã xảy ra ba giờ.

Tôi đã "khắc phục" sự cố bằng cách lưu giá trị chính xác cho AUTO_INCREMENT ở nơi khác và sử dụng thay vì dựa vào giá trị nội bộ. Có cách nào thực sự để InnoDB nhớ giá trị thực tế cuối cùng không?


Bạn có bài viết bạn đọc?
gbn

@gbn Liên kết cho bài viết dev.mysql.com/doc/refman/5.1/en/NH
Naveen Kumar

Để tham khảo, đây là bug.mysql.com/orms.php?id=199
Laurynas Biveinis

ALTER TABLE table_name Engine = MyISAM Làm việc cho tôi. Bảng của chúng tôi luôn được giữ rất nhỏ, vì vậy không cần InnoDB.

1
@QuickFix Bạn nên thêm một số chi tiết về lý do tại sao điều này hoạt động.
Max Vernon

Câu trả lời:


5

(tránh vấn đề bằng cách không bao giờ xóa)

Vì bạn muốn giữ "Posted by =User #123="thông tin sau khi xóa người dùng id=123, bạn cũng có thể xem xét sử dụng 2 bảng để lưu trữ dữ liệu người dùng. Một cho Activengười dùng và một cho tất cả (bao gồm cả những người đã xóa khỏi người dùng đang hoạt động). Và không bao giờ xóa các id đó khỏi AllUserbảng:

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

Điều này tất nhiên sẽ làm phức tạp thêm thao tác chèn người dùng mới. Bất kỳ người dùng mới nào cũng có nghĩa là 2 lần chèn, một trong mỗi bảng. Xóa một người dùng mặc dù sẽ chỉ bằng cách xóa khỏi ActiveUserbảng. Tất cả các FK sẽ bị xóa theo tầng, ngoại trừ các bài đăng trên diễn đàn, sẽ tham chiếu Alluserbảng (nơi sẽ không có xóa sẽ xảy ra).


4

Không có cách tự nhiên nào để làm điều này ngoại trừ sử dụng information_schema.tables để ghi lại tất cả các cột với tùy chọn auto_increment.

Bạn có thể thu thập các cột như sau:

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;

Tạo một tập lệnh sẽ đặt lại các giá trị 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}

Sau đó, bạn có thể làm một trong hai điều sau:

TÙY CHỌN # 1: Chạy tập lệnh thủ công sau khi khởi động

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

TÙY CHỌN # 2: Yêu cầu mysqld thực thi tập lệnh trước khi cho phép kết nối

Bạn sẽ phải thêm tùy chọn này

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

Bằng cách đó, mỗi khi bạn khởi động lại mysql, tập lệnh này được thực thi ngay từ đầu. Bạn sẽ phải nhớ để tạo lại /var/lib/mysql/ResetAutoInc.sql trước khi thực hiện khởi động lại mysql theo kế hoạch.



2

Chỉ cần không xóa người dùng. Tính toàn vẹn quan hệ là quan trọng hơn. Nếu bạn phải vì lý do riêng tư hoặc bất cứ điều gì, chỉ cần thay đổi tên người dùng thành 'đã xóa' và xóa bất kỳ trường nào khác.


1

Đây là một câu hỏi cũ và vẫn còn có liên quan.

1) Hành vi này đang được sửa trong Mysql 8.0.

2) Một giải pháp là sử dụng một hàng giả cho dữ liệu của bạn, để giữ cho AUTO_INCREMENT trên một giá trị nhất định. Không siêu tiện lợi tùy thuộc vào những gì bạn đang lưu trữ, nhưng là một giải pháp đơn giản trong một số trường hợp.


0

Chúng tôi có nhu cầu về điều này ngoại suy một giải pháp cho hệ thống của chúng tôi dựa trên các hướng dẫn về bài đăng này. Nếu điều này có thể giúp bất cứ ai đạt được mục tiêu của họ theo một cách thậm chí dễ dàng hơn.

Hệ thống của chúng tôi sử dụng mẫu bảng mộ để lưu trữ các mục đã xóa vì chúng tôi đồng bộ hóa 2 chiều trên các hệ thống bị ngắt kết nối, vì vậy chúng tôi sử dụng mã này để khớp các bảng mộ với bảng trực tiếp của chúng và trích xuất giá trị cao nhất có thể :)

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 ;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.