Loading

MySQL事务隔离级别

ACID

  • 原子性(Atomic): 事务中的多个操作,不可分割,要么都成功,要么都失败; All or Nothing.
  • 一致性(Consistency): 事务操作之后, 数据库所处的状态和业务规则是一致的; 比如a,b账户相互转账之后,总金额不变;
  • 隔离性(Isolation): 多个事务之间就像是串行执行一样,不相互影响;
  • 持久性(Durability): 事务提交后被持久化到永久存储.

几个"读"概念

类型 定义
脏读 可以读取其他事务未提交的数据
不可重复读 同一个事务中多次执行同一个select, 读取到的数据发生了改变(被其它事务update并且提交)
幻读 同一个事务中多次执行同一个select, 读取到的数据行发生改变。也就是行数减少或者增加了(被其它事务delete/insert并且提交)。
SERIALIZABLE要求解决幻读问题
可重复读 同一个事务中多次执行同一个select, 读取到的数据没有发生改变(一般使用MVCC实现);
RR隔离级别要求达到可重复读的标准

不可重复读 VS 幻读

  • 不可重复读。重点在update,同样的条件的select, 你读取过的数据, 再次读取出来发现值不一样了
  • 幻读。重点在insert或delete,同样的条件的select, 第1次和第2次读出来的行数不一样

小结

  • 从结果上来看, 两者都是为多次读取的结果不一致。但如果你从实现的角度来看, 它们的区别就比较大:
  • 对于前者, 在RC下只需要锁住满足条件的记录,就可以避免被其它事务修改,也就是 select for update, select in share mode; RR隔离下使用MVCC实现可重复读;
  • 对于后者, 要锁住满足条件的记录及所有这些记录之间的gap,也就是需要gap lock。

隔离级别

隔离级别 定义 脏读 不可重复读 幻读
读未提交 | RU(READ UNCOMMITTED) 可以读取未提交的数据,未提交的数据称为脏数据,所以又称脏读 Y Y Y
读提交 | RC(READ COMMITTED) 只能读取已经提交的数据 N Y Y
可重复读 | RR(REPEATABLE READ) 同一个事务中多次执行同一个select,读取到的数据没有发生改变 N N Y
串行化 | (SERIALIZABLE) 读写操作相互阻塞 N N N
  • 小结

    • 可重复读RR并不能完全避免幻读,因为在A、B事务中通过A.select - B.insert - A.update - A.select顺序,就会出现幻读
    • 只有串行化唯一把脏读、不可重复读、幻读都解决

数据库的默认隔离级别

  • ORACLE。默认RC,且只实现了RCSERIALIZABLE
  • MySQL。默认RR。
  • 其他数据库。默认RC

间隙锁(Gap Lock)

  • 为了解决幻读问题,MySQL引入了间隙锁,间隙锁锁的就是两个值之间的空隙,且在RRS级别下才有
  • Next-Key Lock = Gap Lock + Record Lock(行锁)
CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  `d` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `c` (`c`)
) ENGINE=InnoDB;

insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);

行锁的冲突关系是另外一个行锁

img

间隙锁存在冲突关系的,是“往这个间隙中插入一个记录”这个操作

img
  • Session A:因为c=7这行记录不存在,所以加的是间隙锁(5,10)
  • Session B:同样是在间隙加锁,

RC VS RR

  • 一致性读的区别。RC在一个事务中可以读到另外一个事务已经提交的数据。RR是快照读,在一个事务中从第一个select触发建立快照,直到事务结束都保持一致,不管其他事务是否有提交都不会改变。

  • 锁的区别。主要体现在并发性上,RC的并发要好于RR,原因如下

    • 间隙锁(gap lock)RR也可以通过间隙锁来解决幻读问题,RC只解决了脏读和不可重复读
    • RC会在where 条件过滤之后,将不符合条件的记录上的行锁释放掉。而RR即使不符合where条件的记录,也不会释放行锁gap lock
  • 主从复制区别。

    • RC不支持 statement 格式的bin log,因为该格式的复制,会导致主从数据的不一致;只能使用 mixed 或者 row 格式的bin log; 这也是为什么MySQL默认使用RR隔离级别的原因。复制时,我们最好使用:binlog_format=row
  • 半一致性读。


参考

posted @ 2023-12-09 12:29  FynnWang  阅读(19)  评论(0编辑  收藏  举报