春天的环境中有一种大现象,或者我错了。
但是默认的spring @Transactional注释不是ACID,只有缺少隔离的ACD。这意味着如果您具有以下方法:

@Transactional
public TheEntity updateEntity(TheEntity ent){
  TheEntity storedEntity = loadEntity(ent.getId());
  storedEntity.setData(ent.getData);
  return saveEntity(storedEntity);
}

如果2个线程输入不同的计划更新会发生什么。他们都从数据库加载实体,都应用自己的更改,然后保存第一个并提交,当保存第二个并提交第一个UPDATE IS LOST。真的是这样吗?使用调试器,它可以像这样工作。

最佳答案

您并没有错,您的问题是一个非常有趣的观察。我相信(根据您的评论)您正在针对非常特殊的情况考虑此问题,而本主题则要广泛得多。让我们逐步进行。

ACID

我在ACID中确实代表隔离。但这并不意味着需要一个接一个地执行两个或多个事务。它们只需要被隔离到一定水平即可。大多数关系数据库都允许在事务上设置隔离级别,甚至允许您从其他未提交的事务中读取数据。这种情况是否良好取决于具体的应用。参见示例mysql文档:

https://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-isolation-levels.html

您当然可以将隔离级别设置为可序列化,并达到您的期望。

现在,我们还有不支持ACID的NoSQL数据库。最重要的是,如果您开始使用数据库集群,则可能需要包含数据的最终一致性,这甚至可能意味着刚刚写入某些数据的同一线程在进行读取时可能无法接收到该数据。同样,这是一个特定于应用程序的非常特殊的问题-我能否承受一时的数据不一致以换取快速写入?

您可能倾向于在银行或某些金融系统中以可序列化的方式处理一致的数据,并且在社交应用程序中使用不一致的数据可能会很好,但可以获得更高的性能。

更新丢失了-是这样吗?

是的,就是这样。

我们害怕可序列化吗?

是的,它可能会令人讨厌:-)但重要的是要了解它的工作原理以及后果。我不知道是否仍然如此,但是大约10年前我在一个使用DB2的项目中遇到过这种情况。由于非常特殊的情况,DB2正在执行锁升级以对整个表进行排他锁,从而有效地阻止了任何其他连接甚至访问表也无法访问该表。这意味着一次只能处理一个连接。

因此,如果您选择使用可序列化级别,则需要确保您的事务实际上是快速的,并且实际上是必需的。在编写时,其他线程正在读取数据也许很好?试想一下一种情况,其中您的文章具有评论系统。突然,病毒文章发表了,所有人开始发表评论。一次写入评论的事务需要100毫秒。 100个新的评论交易进入队列,这将在接下来的10秒钟内有效地阻止阅读评论。我敢肯定,此处提交的读取将绝对足够,并且可以使您实现两件事:更快地存储注释,并在编写注释时阅读它们。

长话短说:
这完全取决于您的数据访问模式,没有灵丹妙药。有时需要可序列化,但会降低性能,有时读未提交会很好,但会带来不一致的代价。

09-10 09:26