innodb事务隔离级别、MVCC

隔离级别#

未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据

提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)

可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读

串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞

MVCC#

InnoDB是一个多版本的存储引擎:为了支持事务的一些特性诸如并发和回滚,它保持着被修改行的旧版本信息。这些信息被存储在一个被叫做回滚段(rollback segment)的表空间中。InnoDB在回滚段中用这些信息来执行undo操作,以此支持事务回滚。它也用这些信息来构造行的更早的版本,以此支持一致性读(快照读)。

在内部,InnoDB为数据库中存储的每一行添加三个隐藏字段。

DB_TRX_ID:表明插入或者修改这一行的最后一个事务的事务标识符。如何查看行事务ID

DB_ROLL_PTR:指向回滚段中的一个undo log记录,如果行被修改了,那么这个undo log记录包含的信息必须先于行修改被重新修改。

DB_ROW_ID:单调递增的行ID。如果InnoDB自动生成了一个聚集索引,那么这个索引包含行ID值,否则DB_ROW_ID列不会出现在任何索引中。

锁类型#

共享锁(读锁,S锁):若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放S锁。

排他锁(写锁,X锁):若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他事务不能再对A加作何类型的锁,直到T释放A上的X锁。

意向共享锁(IS锁):事务T在对表中数据对象加S锁前,首先需要对该表加IS(或更强的IX)锁。

意向排他锁(IX锁):事务T在对表中的数据对象加X锁前,首先需要对该表加IX锁。

意向锁补充#
  • InnoDB 支持多粒度锁,特定场景下,行级锁可以与表级锁共存。
  • 意向锁之间互不排斥,但除了 IS 与 S 兼容外,意向锁会与 共享锁 / 排他锁 互斥。
  • IX,IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,S发生冲突。
  • 意向锁在保证并发性的前提下,实现了行锁和表锁共存且满足事务隔离性的要求。

2pl 两阶段锁#

两段锁协议,先随便加,最后commit才能一起释放.

GAP锁有何用#

其实这个多出来的GAP锁,就是RR隔离级别,相对于RC隔离级别,不会出现幻读的关键。

确实,GAP锁锁住的位置,也不是记录本身,而是两条记录之间的GAP。

所谓幻读,就是同一个事务,连续做两次当前读 (例如:select * from t1 where id = 10 for update;),那么这两次当前读返回的是完全相同的记录 (记录数量一致,记录本身也一致),第二次的当前读,不会比第一次返回更多的记录 (幻象)。

如何保证两次当前读返回一致的记录,那就需要在第一次当前读与第二次当前读之间,其他的事务不会插入新的满足条件的记录并提交。为了实现这个功能,GAP锁应运而生

快照读 & 当前读#

快照读:就是select

1
select * from table ….;

当前读:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁。

1
2
3
4
5
select * from table where ? lock in share mode;
select * from table where ? for update;
insert;
update ;
delete;

聊天讨论#

全快照、全当前,不幻读, 先快照,再当前,幻读

「 球球: 其实产生幻读的原因,就是以为读走了mvcc的副本, 」


读如果也加gap锁,就真的不会幻读了,但代价太大

「 宏伟: 张三账户余额有10000元;要转账给李四9000元,转给王五5000元;这两次转账肯定有一次因余额不足转账失败;如果在 rr 下,可重复读, 两次update 张三都会成功? 」


这个如果两个事务并发,都先读的1w元快照读,然后第1个事务直接update张三减钱, commit, 第2个事务 接着update张三减钱,都会成功,除非
1 用数据库乐观锁
2 自己搞个分布式锁,将读写操作原子化
3 用kafka让同一用户操作串行化

敏: 交易相关的就不要快照读了, 交易要算钱,select的时候就要锁,不然这条数据之后未必可用

总结#

RR级别下,通过MVCC, 解决了两个快照读的幻读问题,
通过next-key锁,解决了两个当前读的幻读问题,但是快照读后的当前读是会幻读的.

# mysql
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×