mysql读取数据实际上有两种读取模式:当前读和快照读
- 快照读:快照读的执行方式是生成 ReadView,直接利用 MVCC 机制来进行读取,并不会对记录进行加锁。
- 当前读:每次读取的都是当前最新的数据,但是读的时候不允许写,写的时候也不允许读。
1、当前读
-
在读取数据时直接读取数据库中的最新版本,即读取的是已经提交的事务所修改的数据。当前读操作会获取共享锁,阻塞其他事务对该数据的写操作,以保证读取到的数据是最新的。这对于需要读取最新数据的场景非常重要,例如查询实时数据或进行数据分析。
-
SELECT … LOCK IN SHARE MODE (共享读锁):
- 使用共享读锁时,其他事务可以同时读取相同的数据,但不能修改它。
- 适用于需要读取数据的场景,但不希望其他事务修改该数据。
-
SELECT … FOR UPDATE:
- 使用排他锁时,其他事务无法同时读取或修改相同的数据。
- 适用于需要读取数据并且在之后修改该数据的场景,例如更新或删除操作。
-
UPDATE / DELETE / INSERT:
- 当前读确保读取的是最新版本的数据,并且对读取的记录加锁,阻塞其他事务同时改动相同记录,避免出现安全问题。
- 例如,假设要更新一条记录,但另一个事务已经删除了这条数据并且提交了,如果不加锁就会产生冲突。因此,update的时候肯定要是当前读,得到最新的信息并且锁定相应的记录。
2、 快照读
mysql中的快照读是通过MVCC+undolog实现的。
我们只是查询数据而不涉及数据的更新。在这种情况下,我们只关心历史快照数据,不需要像更新操作那样获取最新版本的数据。因此,快照读可以在一定程度上提高 MySQL 的并发性能。
快照读,顾名思义,就是读取快照数据,也就是说当某个数据正在被修改的时候,也可以进行读取该数据,保证读写不冲突。
刚刚提到undolog,当我们对记录做了变更操作时,就会产生undo记录,undo记录中存储的是老版数据,当一个旧的事务需要读取数据时,为了能够读取到老版本的数据,需要顺着undo列找到满足其可见性的记录,这个找满足可见行的记录依赖。就是说每次都是读取undolog中的数据。
3、快照读和当前读来解决幻读问题
- 针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。
- 针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select ... for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。