简体   繁体   English

数据库不一致状态MySQL / InnoDB

[英]Database inconsistent state MySQL/InnoDB

I am wondering if it is necessary to use locking in most likely concurrent environment and how in following case . 我想知道是否有必要在最可能的并发环境中使用锁定以及如何在以下情况下使用 Using MySQL database server with InnoDB engine 使用带有InnoDB引擎的MySQL database server

Let's say I have a table 假设我有一张桌子

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

And the procedure 和程序

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$$

Like you see the fact is that I am increasing m_id manually and concurrent transactions are most likely happening. 就像你看到的事实是我手动增加m_id并且很可能发生并发事务。 I can't make my mind if database might become in inconsistent state. 如果数据库可能变得处于不一致状态,我无法理解。 Also using FOR UPDATE and LOCK IN SHARE MODE has no point in this situation as transaction deals with new records and has nothing to do with updates on a specific row. 在这种情况下,使用FOR UPDATELOCK IN SHARE MODE也没有意义,因为事务处理新记录并且与特定行的更新无关。 Further LOCK TABLES are not allowed in stored procedures and is quite insufficient. 存储过程中不允许使用其他LOCK TABLES ,这是非常不够的。

So, my question is how to avoid inconsistent state in marked scenario if it is possible to happen actually. 所以,我的问题是如果有可能实际发生,如何避免标记场景中的不一致状态。 Any advice will be grateful 任何建议都将不胜感激

transaction deals with new records and has nothing to do with updates on a specific row 事务处理新记录,与特定行的更新无关

Such a new record is known as a 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. 例如,如果查询在事务中运行两次,同时另一个事务在插入新行或更新行后提交,以便它与查询的WHERE子句匹配。

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 . 在不同的隔离级别中 ,可序列化读取级别阻止了幻像读取,并且可重复读取一致读取和读取未提交级别允许。

So to prevent phantoms from occurring on any statement , one can simply set the transaction isolation level to be SERIALIZABLE . 因此,为了防止在任何语句上出现幻像,可以简单地将事务隔离级别设置为SERIALIZABLE InnoDB implements this using next-key locks , which not only locks the records that your queries match but also locks the gaps between those records . InnoDB使用next-key锁实现这一点,它不仅锁定查询匹配的记录, 还锁定这些记录之间的间隙

The same can be accomplished on a per-statement basis by using locking reads , such as you describe in your question: LOCK IN SHARE MODE or FOR UPDATE (the former allows concurrent sessions to read the matching records while the lock is in place, whilst the latter does not). 通过使用锁定读取可以在每个语句的基础上完成相同的操作,例如您在问题中描述的: LOCK IN SHARE MODEFOR UPDATE (前者允许并发会话在锁定到位时读取匹配的记录,同时后者不)。

First, a sequence table 首先是序列表

  CREATE TABLE m_id_sequence (
      id integer primary key auto_increment
    );

and then alter the procedure to get the next m_id from the sequence 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 ;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM