为啥 MVCC 需要对 DML 语句进行锁定

Posted

技术标签:

【中文标题】为啥 MVCC 需要对 DML 语句进行锁定【英文标题】:Why does MVCC require locking for DML statements为什么 MVCC 需要对 DML 语句进行锁定 【发布时间】:2015-08-13 07:12:42 【问题描述】:

在 PostgreSQL 中,MVCC concurrency control 机制表示:

为查询(读取)数据而获取的 MVCC 锁与 为写入数据而获得的锁,因此读取永远不会阻塞写入 写作永远不会阻碍阅读

所以,even for READ_COMMITTED,UPDATE 语句将锁定当前受影响的行,因此其他事务无法修改它们,直到当前事务提交或回滚。

如果并发事务对锁定的行发出 UPDATE,第二个事务将阻塞,直到第一个事务释放它的锁。

    这种行为是trying to prevent the write-write conflicts吗?

    在 READ_COMMITTED 中仍然可能发生丢失更新,因为在第一个事务提交后,第二个事务将覆盖该行(即使数据库在 UPDATE 查询开始和查询结束之间发生了变化)。因此,如果仍然可能丢失更新,为什么第二个事务必须等待?难道不能使用行级快照来存储未提交的事务更改以避免事务必须等待写锁被释放吗?

【问题讨论】:

【参考方案1】:

第一个问题的答案是肯定的。没有 DBMS 可以支持脏写;如果两个事务 T1 和 T2 正在同时执行并且 T2 覆盖了来自 T1 的更新,则系统无法处理 T1 随后发出 ROLLBACK 的情况,因为 T2 的更新已经发生。

为了避免脏写,快照隔离的原始定义是“第一个提交者获胜”——也就是说,允许发生冲突的写入,但只有第一个发出 COMMIT 的事务能够——所有其他冲突的事务将不得不回滚。但是这种编程模型有些问题,如果不是浪费的话,因为一个事务可能会更新数据库的很大一部分,但最后却被拒绝提交的能力。因此,大多数支持 MVCC 的 DBMS 系统不是“先提交者获胜”,而是使用相当传统的两阶段锁定来实现“第一个更新者获胜”。

【讨论】:

但脏读不会发生,因为我们处于 READ_COMMITTED 状态。我想知道为什么不更新隔离副本并在提交时刷新它们。现在,在 PostgreSQL 中,第二个 tx 只是覆盖了第一个,但也有阻塞发生。为什么不在这个隔离级别使用会话范围的快照而不阻塞。 我认为您所说的“更新隔离副本”将对应于“第一个提交者获胜”。一个系统可以实现它——但是编程模型很奇怪,因为每个事务都必须准备好处理每个修改“工作”(即它确实完成并且没有返回错误)但事务不能提交的情况. 我想锁定模型更容易采用,因为每个数据库都已经有一个锁定层次结构。正如您之前提到的,不仅是行数据,还有所有索引。

以上是关于为啥 MVCC 需要对 DML 语句进行锁定的主要内容,如果未能解决你的问题,请参考以下文章

oracle行级锁和表级锁的区别?

请教一下mysql 行锁命令是啥?

深夜浅谈MYSQL 的MVCC

select for update行锁

Oracle - 数据更新 - 事务

INNODB锁