我想知道是否有必要在最有可能的并发环境中使用锁定以及在以下情况下 。将MySQL database server
与InnoDB
引擎
假设我有一张桌子
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 UPDATE
和LOCK IN SHARE MODE
也没有意义,因为事务处理新记录并且与特定行的更新无关。在存储过程中不允许进一步LOCK TABLES
,这是不够的。
所以,我的问题是如果可能实际发生,如何避免标记场景中的不一致状态。任何建议都将不胜感激
答案 0 :(得分:2)
交易处理新记录,与特定行的更新无关
这样的新记录称为phantom:
幻象
显示在查询结果集中但不在先前查询的结果集中的行。例如,如果查询在 事务 中运行两次,同时另一个事务在插入新行或更新行后提交,以使其与{查询的{1}}子句。
这种情况称为幻像读取。与 不可重复的读取 相比,更难防范,因为锁定第一个查询结果集中的所有行并不会阻止导致幻像出现的更改。
在不同的 隔离级别 中, 可序列化读取 级别阻止了幻像读取,并且 可重复阅读 , 一致阅读 ,并阅读 未提交的级别 强>
因此,为了防止任何语句上出现幻像,可以简单地将事务隔离级别设置为SERIALIZABLE
。 InnoDB使用next-key locks实现此功能,它不仅锁定查询匹配的记录,而且还锁定这些记录之间的间隙。
使用locking reads可以在每个语句的基础上完成相同的操作,例如您在问题中描述的:WHERE
或LOCK IN SHARE MODE
(前者允许并发会话读取在锁定到位时匹配记录,而后者没有)。
答案 1 :(得分:1)
首先是序列表
CREATE TABLE m_id_sequence (
id integer primary key auto_increment
);
然后更改过程以从序列表中获取下一个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 ;