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