Loading

对幻读的思考

参考自极客时间-MySQL实战45讲

幻读的定义

幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。

幻读发生的条件

幻读仅发生在“当前读”的情况下。

请思考一下,为什么只有在当前读下会发生?

如果是快照读,在该事务之后的其他事务的变更,当前事务一定看不到,不可能发生幻读。

问题

表结构

CREATE TABLE `t` (
  `id` int NOT NULL,
  `c` int DEFAULT NULL,
  `d` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `c` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

初始数据

T3时刻情况下,session A是算发生了幻读吗?

不算,session A为修改了旧行而查询到了数据。只有“新插入的行”才算幻读。

幻读带来的问题

语义上的问题select ... for update; 的语义是锁住所有符合条件的记录,不允许进行读写操作(即加了写锁)。而幻读会使得插入新的符合条件的记录,明显破坏了该条SQL语义。

数据一致性问题:会导致binlog写入日志的顺序问题,导致备份库/从库的数据不一致问题。

如何避免幻读

间隙锁。保证了行与行之间拒绝插入了新的记录。

间隙锁和行锁合称 next-key lock,每个 next-key lock 是前开后闭区间。

间隙锁的问题:因为不同间隙锁之间并不互斥,可能导致不同session的相互等待导致死锁,使得并发度降低,锁住更大的范围。

那么如何取消使用间隙锁?

间隙锁只会在可重复读级别下生效,所以把隔离级别调整为读提交即可。(其实读提交也是会发生幻读,但是好像认为这算feature,在可重复读情况下这算bug???)

但是需要解决数据和日志可能不一致的问题,需要将binlog_format=row

拓展阅读

Binary Logging Formatsbinlog的格式有statement、row、mixed。

statement:记录执行的SQL。

row:记录每一条记录被修改就结果。

mixed:DDL使用statement、DML使用row。

posted @ 2021-03-02 10:49  wheelchen  阅读(118)  评论(0)    收藏  举报