MVCC原理

并发一致性问题:

1. 丢失修改:

2. 读脏数据:

事务A读取事务B未提交的修改,事务B之后进行了回滚。

3. 不可重复读:

事务A读取了事务B未提交的修改。一个事务读取了另外一个事务 修改 后记录 强调的是 update 和delete , 只需要锁住满足条件的记录即可。

4. 幻读:

一个事务读取了另外一个事务插入的数据,强调的是 insert ,要锁住满足条件及相近的记录。

MYSQL 中默认的隔离级别是可重复读,可解决脏读和不可重复读的问题。但是不能解决幻读的问题。 Oracle 默认的是Read Commit 读已提交,可以避免脏读的问题。

幻读是因为update的操作的where是属于当前读,所以与快照读无关,从而导致了update修改的数据的版本号也被更新为了当前的版本号(可能存在低的事务版本号覆盖高的事务版本号),然后update后续的查询就能查询到了其他高版本事务提交后的数据了;主要的原因还是因为update当前读数据再不同事务中冲突导致,所以解决思路也是如何避免此冲突,通常是加行锁


1. MVCC

MVCC 的英文全称是 Multiversion Concurrency Control ,中文意思是多版本并发控制技术。原理是,通过数据行的多个版本管理来实现数据库的并发控制,简单来说就是保存数据的历史版本。可以通过比较版本号决定数据是否显示出来。读取数据的时候不需要加锁可以保证事务的隔离效果。

备注:

MYSQL 中默认的隔离级别是可重复读,可解决脏读和不可重复读的问题。但是不能解决幻读的问题。 Oracle 默认的是Read Commit 读已提交,可以避免脏读的问题。

2. 解决什么问题?

一般解决不可重复读 和 幻读问题,是采用锁机制实现,有没有一种乐观锁的问题去处理,可以采用 MVCC 机制的设计,可以用来解决这个问题。取代 行锁,降低系统开销。

3.优点或解决什么问题?

  • 读写不阻塞,读旧版本的快照,写最新版本的快照。提升数据并发处理能力。
  • 降低了死锁的概率, MVCC 采用乐观锁,读取数据时,不需要加锁,写操作,只需要锁定必要的行。
  • 解决了一致性读的问题,当我们朝向某个数据库在时间点的快照是,只能看到这个时间点之前事务提交更新的结果,不能看到时间点之后事务提交的更新结果。

4.快照读与当前读?

快照读:

读取的是快照数据,不加锁的简单 Select 都属于快照读.

当前读:

读的是最新数据,而不是历史的数据,加锁的 SELECT,或者对数据进行增删改都会进行当前读。

5. InnoDB 的 MVCC 是如何实现的?

InnoDB 是如何存储记录多个版本的?这些数据是 事务版本号行记录中的隐藏列和Undo Log

事务版本号

每开启一个日志,都会从数据库中获得一个事务ID(也称为事务版本号),这个事务 ID 是自增的,通过 ID 大小,可以判断事务的时间顺序

行记录的隐藏列

  1. row_id : 隐藏的行 ID ,用来生成默认的聚集索引。如果创建数据表时没指定聚集索引,这时 InnoDB 就会用这个隐藏 ID 来创建聚集索引。采用聚集索引的方式可以提升数据的查找效率
  2. trx_id : 操作这个数据事务 ID ,也就是最后一个对数据插入或者更新的事务 ID 。
  3. roll_ptr : 回滚指针,指向这个记录的 Undo Log 信息。

 

Undo Log

InnoDB 将行记录快照保存在 Undo Log 里。

 数据行通过快照记录都通过链表的结构的串联了起来,每个快照都保存了 trx_id 事务ID,如果要找到历史快照,就可以通过遍历回滚指针的方式进行查找。

Read View 是啥?

如果一个事务要查询行记录,需要读取哪个版本的行记录呢? Read View 就是来解决这个问题的。Read View 可以帮助我们解决可见性问题。 Read View 保存了当前事务开启时所有活跃的事务列表。换个角度,可以理解为: Read View 保存了不应该让这个事务看到的其他事务 ID 列表。

  1. trx_ids:系统当前正在活跃的事务ID集合
  2. low_limit_id :活跃事务的最大的事务 ID。
  3. up_limit_id :活跃的事务中最小的事务 ID。
  4. creator_trx_id:创建这个 ReadView 的事务ID。

如果当前事务的 creator_trx_id 想要读取某个行记录,这个行记录ID 的trx_id ,这样会有以下的情况:

  • 如果 trx_id < 活跃的最小事务ID(up_limit_id),也就是说这个行记录在这些活跃的事务创建前就已经提交了,那么这个行记录对当前事务是可见的。
  • 如果trx_id > 活跃的最大事务ID(low_limit_id),这个说明行记录在这些活跃的事务之后才创建,说明这个行记录对当前事务是不可见的。
  • 如果 up_limit_id <= trx_id <= low_limit_id, 说明该记录在 trx_ids 集合中,可能还处于活跃状态,因此我们需要在 trx_ids 集合中遍历 ,如果trx_id 存在于 trx_ids 集合中,证明这个事务 trx_id 还处于活跃状态,不可见,否则 ,trx_id 不存在于 trx_ids 集合中,说明事务trx_id 已经提交了,这行记录是可见的。

如何查询一条记录

  1. 获取事务自己的版本号,即 事务ID
  2. 获取 Read View
  3. 查询得到的数据的快照,然后与 Read View 中的事务版本号进行比较。
  4. 如果不符合 ReadView 规则, 那么就需要 Undo Log 中历史快照
  5. 最后返回符合规则的数据

InnoDB 实现多版本控制 (MVCC)是通过 ReadView+ UndoLog 实现的,UndoLog 保存了历史快照,ReadView 规则帮助判断当前版本的数据是否可见。

总结

  • 如果事务隔离级别是 ReadCommit ,一个事务的每一次 Select 都会去查一次ReadView ,每次查询的Read View 不同,就可能会造成不可重复读或者幻读的情况。
  • 如果事务的隔离级别是可重读,为了避免不可重读读,一个事务只在第一次 Select 的时候会获取一次Read View ,然后后面索引的Select 会复用这个 ReadView.

MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种数据库并发控制机制,主要用于保证在高并发环境下数据的一致性和完整性。在Java中,MVCC通常与关系型数据库系统如Oracle、MySQL等的InnoDB存储引擎相关。 MVCC原理主要包含以下几个关键点: 1. 数据版本:每个事务看到的是数据的一个独立版本,而不是实时的最新状态。每个版本都有一个时间戳或版本号,表示该数据的修改时间。 2. 读已提交(Read Committed):当事务开始时,它只会看到已经提交的更改。这样,即使有其他事务在修改同一数据,当前事务也能看到稳定的结果,不会看到未提交的更改。 3. 少数读脏(Read Uncommitted):一些数据库支持更高的并发度,允许事务看到其他事务未提交的修改,但这可能导致“脏读”(读到未提交的数据)。这是有风险的,通常仅在特定场景下使用。 4. 不可重复读(Repeatable Read):事务在执行过程中,如果多次读取同一数据,结果应该一致,不会因为其他事务的提交而改变,这通常通过锁定机制来实现。 5. 可串行化(Serializable):这是最高的隔离级别,确保所有事务看起来像是顺序执行,但实际性能较低。 在Java中,MVCC不直接体现在语言层面,而是由底层数据库管理系统实现。例如,Java应用程序通过JDBC连接数据库时,其实质上是在和数据库进行MVCC相关的交互。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值