幻读是什么?
“Phantom Problem是指在同一事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次的SQL语句可能会返回之前不存在的行。”摘录来自 MySQL技术内幕:InnoDB存储引擎(第2版) (数据库技术丛书)
通俗来说就是,time1:事务A读取某个范围,time2:事务B在这个范围中插入了一条新记录并提交事务,time3:事务A再次读取该范围的记录时读取到事务B新增的记录。
如下:
目前InnoDB存储引擎中,数据库有表如下:
-- ----------------------------
-- Table structure for film
-- ----------------------------
DROP TABLE IF EXISTS `film`;
CREATE TABLE `film` (`id` int NOT NULL AUTO_INCREMENT,`name` varchar(10) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3;-- ----------------------------
-- Records of film
-- ----------------------------
BEGIN;
INSERT INTO `film` (`id`, `name`) VALUES (1, 'film1');
INSERT INTO `film` (`id`, `name`) VALUES (2, 'film2');
COMMIT;
Time1: 开启事务A , 查询表film中的所有记录。
Time2:在film表中插入一条ID 为 3的记录
Time3: 事务A查询表film中所有的数据;注意这时select出的记录中是没有ID为3的记录。
但是我们会发现update ID 为3的记录是成功的,也就是说事务A是可以感知到事务B新增的ID为3的记录。
这时再去读区数据发现是可以读取到的
这就是幻读问题。
如何避免幻读问题?
在RR级别下,采用Next-Key Locking的算法避免幻读问题,即使用FOR UPDATE。
SELECT * FROM film FOR UPDATE;
对于上面的SQL语句,会将(-∞,+∞)这个范围加锁,因此在这个范围内的插入都是不允许的,从而避免幻读。当然如果是id > 2 范围查找那么锁住的也就是(2,+∞)。