我对丢失的更新事务隔离问题进行了询问。
这是相应的数字:
引用附图中的文字:
如果两个事务都更新数据项,则会发生丢失更新 然后第二个事务中止,导致两个更改都丢失。 这发生在没有实现并发控制的系统中,其中 并发事务不是孤立的。
我的询问与上图有关:考虑到 Tx A 的提交发生在 Tx A 的更改丢失了> Tx B ? (数字表示事件的顺序)。
有人可以解释一下吗?
P.S。 我引用Manning出版的Java Persistence with Hibernate Second Edition一书。 (参见:https://www.manning.com/books/java-persistence-with-hibernate-second-edition)
修改:我上面引用的文字与图一起用于演示丢失的更新问题。因此,它假设数据库几乎没有隔离,因此丢失了更新。我无法理解的是图上操作的顺序。换句话说,如果提交发生在回滚之前,那么问题出在哪里?不应该考虑回滚......
答案 0 :(得分:1)
答案取决于每个数据库的事务日志系统的实现。这种系统有不同类型,如果你想深入研究这个主题,你需要先详细说明这些。
此外,答案取决于已采取的事务隔离级别。同样,它不仅取决于特定的数据库,还取决于用于更新数据库的特定指令。有些数据库允许设置任何隔离级别,即使它可以在并发更新或回滚的情况下使数据不一致。
因此,由于您未提供有关特定实现的任何详细信息,我可以猜测您的抽象数据库使用快照隔离。这意味着,在任何数据修改,行,行范围或整个表之前,都会生成该数据的副本。副本是修改数据的初始状态。
通常,在另一个事务尚未完成之前无法启动事务,该要求是通过锁实现的。但是在您发生的示例中,一个事务成功修改了数据,另一个事务又回滚了。
任何无法提交的事务都应将未完全修改的数据返回到初始状态。对于事务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);
如您所见,Delete操作上的回滚会将数据重置为初始状态。暗示没有事务日志。通常数据不会真正改变。取而代之的是,在事务日志中将写入应该执行某些操作并且应该更改某些数据的信息。在这种情况下,回滚确实不会丢弃更新操作的结果。因为它唯一要做的就是从事务日志中删除记录。如果操作成功,则事务日志中的更改将应用于实际数据。但看起来您的抽象数据库没有这样的机制。或者它说明了对无事务数据库的访问,该数据库可能是NoSQL数据库之一。在这种情况下,没有这样的日志,并且应该在客户端执行同步。