MVCC——一致性读的原子性问题

MVCC - The atomicity problem of consistent reads

同时读取和写入同一个元组可能会因为写入覆盖元组的非原子操作而导致读取逻辑异常。

对于 MySql、

中的 MVCC

从概念上讲,因为ReadView,可以通过可见性规则避免访问正在写入的元组,从而避免在同一区域发生的读写竞争

但是在实现细节上,我还有一个疑问: 覆盖元组的字段将替换堆中的数据。如果有读操作进来,会读写同一个区域,可能会造成读写冲突(字节复制不是原子的)

如何避免这种读写冲突?是锁吗?


我的意思表达的不是很好,其实我想表达的是锁竞争的问题:

插入 tableA(age,num) 值(1,1) 假设数据库中有一条数据。

此时在read commit下,同时并发了以下两个事务运行: 事务 1:select * 来自表 A 事务2:更新tableA set age=2

他们运行在数据库中的步骤如下:

  1. 事务1访问页面中唯一的一条数据:访问该行数据的事务id,通过可见性规则判断数据可见
  2. 事务2定位到该行数据,发现写入的age字段与当前数据占用的大小相同,于是开始执行替换逻辑
  3. 事务2复制当前数据中age字段的值进行undo,然后将undo指针指向过去,事务id更新
  4. 事务2将值2写回当前数据的age字段
  5. 事务1开始访问age字段,读取当前数据值为2,访问num字段的值为1,并且returns(2, 1)
  6. 事务 2 提交

通过以上步骤的操作,可以看出事务1返回的结果不是预期的,根本原因是读事务id的动作和写数据的动作(undo , 交易id, 新数据)不互斥

mvcc是一种访问设计,但是数据库引擎在访问和写入元组时仍然会竞争同一个区域。 Innodb在实现mvcc时如何巧妙避免读写冲突?

首先要意识到的是,任何时候都可能存在一行的多个版本。

想想这样的处理过程。当事务开始时,会获取整个数据库的副本。事务只看到那些行。当下一个交易出现时,它会获得自己的快照。

没有真正完整的副本;相反,每一行(或一行的版本)都有一个与之关联的顺序事务编号。并且每个事务都可以看到别人是否锁定了一行。有(至少)两种类型的锁——读取和读写。关于彼此之间的距离有多少有不同的规则。

回到你的问题... 行是activity的单位。当要触摸一行时,它必须查看该行还发生了什么。一个简单的 Select 可能会用 'shared read lock',但 Insert/Update/Delete 需要更强的锁。

事物原子的。但是锁定是在行级别,具有事务 ID、历史列表(每行的待处理版本)等。

并且提早发现死锁;一个事务被选择回滚。其他一些冲突通过将一项事务暂停直到另一项事务完成来解决(参见 innodb_lock_wait_timeout)。

如果我尝试读取 一行并且有一个尚未提交的写入待处理到该行,我可能看不到待处理的写入;相反,我看到了该行的旧副本。 (对比事务隔离模式)

如果“元组的一个字段”是指“一行的一列”,那么我回到“ 是锁定和 ACID 的单元。

对不起,如果我没说清楚。我希望我给了你一些线索并提到了一些需要研究的东西。

版本

(对版本控制的简单讨论)每个版本都与一个事务相关联。如果事务是 ROLLBACK'd,则该版本不再有任何用处。对于一个COMMIT,它会成为赢家,其他的就不需要了。这种“清理”发生在客户端的查询“完成”之后。

每个版本都有一个单调的事务 ID。这个“历史列表”记录了潜在的 winners/loosers。它让事务只看到“事务隔离”允许它看到的行的版本。例如,“READ COMMITTED”显示的是旧版本,而不是尚未提交的版本。 “READ UNCOMMITTED”(“脏读”)显示的是后者。