关于lost update事务隔离问题的质疑
Interrogation about the lost update transaction isolation issue
我有一个关于丢失更新事务隔离问题的疑问。
对应图如下:
并引用图中的文字:
A lost update occurs if two transactions both update a data item and
then the second transaction aborts, causing both changes to be lost.
This occurs in systems that don’t implement concurrency control, where
concurrent transactions aren’t isolated.
我的疑问与上图有关:考虑到Tx A[=35=的提交,为什么Tx A的更改丢失了]发生在回滚Tx B之前? (数字表示事件的顺序)。
有人可以解释一下吗?
P.S。 我引用的是 Manning 出版的 Java Persistence with Hibernate 第二版一书。 (参见:https://www.manning.com/books/java-persistence-with-hibernate-second-edition)
编辑:我上面引用的文字和图片是为了演示丢失更新的问题。所以它假设数据库几乎没有隔离,因此丢失了更新。我看不懂的是图中的操作顺序。。换句话说,如果提交发生在回滚之前,那么问题在哪里?不应该考虑回滚...
答案取决于每个数据库的事务日志系统的实现。此类系统有多种类型,如果您想深入了解该主题,则需要先详细说明这些内容。
此外,答案取决于所采用的事务隔离级别。同样,它不仅取决于特定的数据库,还取决于用于更新数据库的特定指令。一些数据库允许设置任何隔离级别,即使它可以在并发更新或回滚的情况下使数据不一致。
因此,由于您没有提供有关特定实现的任何细节,我可以猜测您的抽象数据库使用了快照隔离。这意味着,在对一行、行范围或整个 table 进行任何数据修改之前,已创建该数据的副本。副本是修改数据的初始状态。
通常情况下,一个事务不能在另一个事务未完成之前启动,这个要求是通过锁来实现的。但是在你的例子中发生了,一个事务成功修改了数据,另一个事务回滚了。
任何无法提交的事务都应该return将数据不完全修改为初始状态。对于事务 B,状态没有事务 A 的变化,因为隔离级别是快照。顺便说一句,有些数据库的工作方式不同,在修改之前不制作任何初始状态的快照,而是仅将更改保存在事务日志中,然后在事务被认为已提交时应用它们。
所以,答案是:因为事务B的初始状态没有事务A的变化。这在某种程度上是正确的:回滚应该始终将数据恢复到事务开始时的状态。
更新:
如果我们用抽象编程语言实现描述的情况会是什么样子?
function Update(rowNumber, data){
initialState = getRowInitialState(rowNumber); // ------- 1
operationResult = updateRow(rowNumber, data);
if (operationResult == success)
commit(rowNumber); // ---------- 3
else
rollback(rowNumber, initialState)
}
function Delete(rowNumber){
initialState = getRowInitialState(rowNumber); // ---------- 2
operationResult = deleteRow(rowNumber); // <--- cause some problems
if (operationResult == success)
commit(rowNumber);
else
rollback(rowNumber, initialState) // -------- 4
}
Update(13, "aaaa");
Delete(13);
如您所见,删除操作回滚会将数据重置为初始状态。暗示没有事务日志。通常数据不会真正改变。取而代之的是,在事务日志中会写入一些信息,表明应该执行一些操作,一些数据应该被更改。在这种情况下,回滚实际上不会丢弃更新操作的结果。因为它唯一要做的就是从事务日志中删除记录。如果操作成功,事务日志中的更改将应用于真实数据。但是看起来你的抽象数据库没有这样的机制。或者它说明了对无事务数据库的访问,它可以是 NoSQL 数据库之一。这种情况下没有这样的日志,应该在客户端进行同步。
我有一个关于丢失更新事务隔离问题的疑问。
对应图如下:
并引用图中的文字:
A lost update occurs if two transactions both update a data item and then the second transaction aborts, causing both changes to be lost. This occurs in systems that don’t implement concurrency control, where concurrent transactions aren’t isolated.
我的疑问与上图有关:考虑到Tx A[=35=的提交,为什么Tx A的更改丢失了]发生在回滚Tx B之前? (数字表示事件的顺序)。
有人可以解释一下吗?
P.S。 我引用的是 Manning 出版的 Java Persistence with Hibernate 第二版一书。 (参见:https://www.manning.com/books/java-persistence-with-hibernate-second-edition)
编辑:我上面引用的文字和图片是为了演示丢失更新的问题。所以它假设数据库几乎没有隔离,因此丢失了更新。我看不懂的是图中的操作顺序。。换句话说,如果提交发生在回滚之前,那么问题在哪里?不应该考虑回滚...
答案取决于每个数据库的事务日志系统的实现。此类系统有多种类型,如果您想深入了解该主题,则需要先详细说明这些内容。
此外,答案取决于所采用的事务隔离级别。同样,它不仅取决于特定的数据库,还取决于用于更新数据库的特定指令。一些数据库允许设置任何隔离级别,即使它可以在并发更新或回滚的情况下使数据不一致。
因此,由于您没有提供有关特定实现的任何细节,我可以猜测您的抽象数据库使用了快照隔离。这意味着,在对一行、行范围或整个 table 进行任何数据修改之前,已创建该数据的副本。副本是修改数据的初始状态。
通常情况下,一个事务不能在另一个事务未完成之前启动,这个要求是通过锁来实现的。但是在你的例子中发生了,一个事务成功修改了数据,另一个事务回滚了。
任何无法提交的事务都应该return将数据不完全修改为初始状态。对于事务 B,状态没有事务 A 的变化,因为隔离级别是快照。顺便说一句,有些数据库的工作方式不同,在修改之前不制作任何初始状态的快照,而是仅将更改保存在事务日志中,然后在事务被认为已提交时应用它们。
所以,答案是:因为事务B的初始状态没有事务A的变化。这在某种程度上是正确的:回滚应该始终将数据恢复到事务开始时的状态。
更新:
如果我们用抽象编程语言实现描述的情况会是什么样子?
function Update(rowNumber, data){
initialState = getRowInitialState(rowNumber); // ------- 1
operationResult = updateRow(rowNumber, data);
if (operationResult == success)
commit(rowNumber); // ---------- 3
else
rollback(rowNumber, initialState)
}
function Delete(rowNumber){
initialState = getRowInitialState(rowNumber); // ---------- 2
operationResult = deleteRow(rowNumber); // <--- cause some problems
if (operationResult == success)
commit(rowNumber);
else
rollback(rowNumber, initialState) // -------- 4
}
Update(13, "aaaa");
Delete(13);
如您所见,删除操作回滚会将数据重置为初始状态。暗示没有事务日志。通常数据不会真正改变。取而代之的是,在事务日志中会写入一些信息,表明应该执行一些操作,一些数据应该被更改。在这种情况下,回滚实际上不会丢弃更新操作的结果。因为它唯一要做的就是从事务日志中删除记录。如果操作成功,事务日志中的更改将应用于真实数据。但是看起来你的抽象数据库没有这样的机制。或者它说明了对无事务数据库的访问,它可以是 NoSQL 数据库之一。这种情况下没有这样的日志,应该在客户端进行同步。