数据库不一致状态 MySQL/InnoDB

Database inconsistent state MySQL/InnoDB

我想知道是否有必要在最可能的并发环境中使用锁定以及如何在以下情况。将 MySQL database serverInnoDB 引擎一起使用

假设我有一个 table

CREATE TABLE `A` (
    `id`    INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    `m_id`  INT NOT NULL, -- manual id
    `name`  VARCHAR(10)
)  ENGINE=INNODB;

和程序

CREATE PROCEDURE `add_record`(IN _NAME VARCHAR(10))
BEGIN
    DECLARE _m_id INT;
    DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
    START TRANSACTION;

    SELECT (`m_id` + 1) INTO _m_id FROM `A` WHERE `id` = (SELECT MAX(`id`) FROM `A`);

    INSERT INTO `A`(`m_id`, `name`) VALUES(_m_id, _NAME);

    COMMIT;
END$$

如您所见,我正在手动增加 m_id 并且最有可能发生并发事务。如果数据库可能处于不一致状态,我无法下定决心。同样使用 FOR UPDATELOCK IN SHARE MODE 在这种情况下没有意义,因为事务处理新记录并且与特定行的更新无关。此外 LOCK TABLES 在存储过程中是不允许的,这是非常不够的。

所以,我的问题是如果实际可能发生,如何避免标记场景中的不一致状态。任何建议将不胜感激

首先,一个序列table

  CREATE TABLE m_id_sequence (
      id integer primary key auto_increment
    );

然后更改程序以从序列 table

中获取下一个 m_id
DELIMITER $$
CREATE PROCEDURE `add_record`(IN _NAME VARCHAR(10))
BEGIN
    DECLARE _m_id INT;
    DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
    START TRANSACTION;

    INSERT INTO m_id_sequence VALUES ();
    SET _m_id = LAST_INSERT_ID();

    INSERT INTO `A`(`m_id`, `name`) VALUES(_m_id, _NAME);

    COMMIT;
END$$
DELIMITER ;

transaction deals with new records and has nothing to do with updates on a specific row

这样的新记录被称为 phantom:

phantom

A row that appears in the result set of a query, but not in the result set of an earlier query. For example, if a query is run twice within a transaction, and in the meantime, another transaction commits after inserting a new row or updating a row so that it matches the WHERE clause of the query.

This occurrence is known as a phantom read. It is harder to guard against than a non-repeatable read, because locking all the rows from the first query result set does not prevent the changes that cause the phantom to appear.

Among different isolation levels, phantom reads are prevented by the serializable read level, and allowed by the repeatable read, consistent read, and read uncommitted levels.

所以为了防止在任何语句上出现幻象,可以简单地将事务隔离级别设置为SERIALIZABLE. InnoDB implements this using next-key locks,这不仅会锁定您查询的记录匹配,但 也锁定了这些记录之间的间隙

可以通过使用 locking reads 在每个语句的基础上完成相同的操作,例如您在问题中描述的:LOCK IN SHARE MODEFOR UPDATE(前者允许并发会话在锁定时读取匹配的记录,而后者则不)。