数据库事务是否会阻止竞争条件?

| 我尚不完全清楚数据库系统中的事务是做什么的。我知道他们可以用来完全回滚更新列表(例如,从一个帐户中扣除资金,然后将其添加到另一个帐户中),但这是他们所做的全部吗?特别是,它们可以用于预防比赛状况吗?例如:
// Java/JPA example
em.getTransaction().begin();
User u = em.find(User.class, 123);
u.credits += 10;
em.persist(u); // Note added in 2016: this line is actually not needed
em.getTransaction().commit();
(我知道这可能写为一个更新查询,但并非总是如此) 该代码是否可以防止竞争条件? 我对MySQL5 + InnoDB最为感兴趣,但也欢迎提供一般答案。     
已邀请:
        TL / DR:事务并不能固有地阻止所有竞争条件。在所有实际的数据库实现中,您仍然需要锁定,中止并重试处理或其他保护措施。事务不是秘密的东西,您可以添加到查询中以确保它们不受所有并发影响。 隔离 您要解决的问题是ACID中的I-隔离。从理论上讲,纯粹的想法是事务应该提供完美的隔离,这样结果就好像每个事务都是按顺序执行一样。实际上,在真正的RDBMS实现中很少出现这种情况。功能因实现而异,可以通过使用较弱的隔离级别(例如“ 1”)来削弱规则。实际上,您不能假设事务阻止了所有竞争条件,即使是在
SERIALIZABLE
隔离的情况下。 一些RDBMS具有比其他RDBMS更强大的功能。例如,PostgreSQL 9.2和更高版本具有很好的“ 2”隔离度,可检测到事务之间大多数(但不是全部)可能的交互,并中止其中一个冲突的事务。因此它可以非常安全地并行运行事务。 很少有系统具有真正完美的“ 2”隔离,可以防止所有可能的争用和异常,包括锁升级和锁排序死锁之类的问题。 即使隔离度很高,某些系统(如PostgreSQL)也会中止冲突的事务,而不是让它们等待并串行运行它们。您的应用必须记住它在做什么,然后重试该事务。因此,尽管事务阻止了与并发相关的异常存储到数据库,但是这样做对应用程序而言并不透明。 原子性 可以说,数据库事务的主要目的是提供原子提交。在提交事务之前,这些更改才会生效。提交后,就其他事务而言,所有更改均在同一瞬间生效。任何事务都无法看到事务所做的1,2,2部分更改。同样,如果您选择“ 5”,则任何其他交易都不会看到该交易的更改;好像您的交易不存在。 这就是ACID中的A。 耐用性 另一个是耐久性-ACID中的D。它指定当您提交事务时,必须将其真正保存到可以避免掉电或突然重启等故障的存储中。 一致性: 参见维基百科 乐观并发控制 对于Hibernate,EclipseLink等ORM,通常不使用锁定和/或较高的隔离级别,而使用乐观并发控制(通常称为“乐观锁定”)来克服较弱的隔离级别的局限性,同时又保留了性能。 。 这种方法的主要功能是,它使您可以跨多个事务处理工作,这对于具有大量用户并且与任何给定用户进行交互之间可能存在较长延迟的系统来说,是一个很大的优势。 参考文献 除了文本链接之外,请参见有关锁定,隔离和并发的PostgreSQL文档章节。即使您使用的是其他RDBMS,您也将从其解释的概念中学到很多东西。 为了简单起见,这里我忽略了很少实现的“ 6”隔离级别;它允许脏读。 2正如@meriton指出的,推论不一定是正确的。幻像读取出现在
SERIALIZABLE
以下。正在进行的事务的一部分看不到某些更改(尚未提交的事务),然后正在进行的事务的下一部分确实在另一事务提交时看到了更改。 3好吧,IIRC SQLite2通过在尝试写操作时锁定整个数据库来做到这一点,但这并不是我所说的并发问题的理想解决方案。     
数据库层在不同程度上支持事务的原子性,称为隔离级别。查看数据库管理系统的文档,以了解受支持的隔离级别及其权衡。最强的隔离级别“可序列化”要求事务的执行就像被一个接一个地执行。这通常通过使用数据库中的排他锁来实现。这可能会导致死锁,数据库管理系统会通过回滚某些涉及的事务来检测并修复死锁。这种方法通常称为悲观锁定。 许多对象关系映射器(包括JPA提供程序)也支持开放式锁定,在数据库中不会阻止更新冲突,但在应用程序层中会检测到更新冲突,然后更新事务将回滚。如果启用了乐观锁定,则示例代码的典型执行将发出以下sql查询:
select id, version, credits from user where id = 123;  
假设这返回了(123,13,100)。
update user set version = 14, credit = 110 where id = 123 and version = 13;
该数据库告诉我们更新了多少行。如果它是一个,则没有冲突的更新。如果为零,则发生冲突的更新,并且JPA提供程序将执行
rollback;
并引发异常,以便应用程序代码可以处理失败的事务,例如通过重试。 简介:无论采用哪种方法,都可以确保您的陈述不受比赛条件的影响。     
        它取决于隔离级别(在可序列化的情况下,它将防止竞争状况,因为通常在可序列化的隔离级别中,事务是按顺序而不是在并行处理中处理的(或至少使用排他锁定,因此,修改相同行的事务将在顺序)。 为了防止出现竞争状况,最好手动锁定记录(例如,mysql支持\'select ... for update \'语句,该语句要求对所选记录进行写锁定)     
        这取决于特定的rdbms。通常,事务获取查询评估计划期间确定的锁。有些可以请求表级别的锁定,其他可以请求列级别,可以选择其他记录级别,第二个可以提高性能。您问题的简短答案是肯定的。 换句话说,事务旨在将一组查询分组并将其表示为原子操作。如果操作失败,则会回滚更改。我不完全知道您使用的适配器是做什么的,但是如果它符合事务的定义,就可以了。 尽管这可以确保防止出现种族状况,但并不能明确防止饥饿或死锁。事务锁定管理器负责这一工作。表锁有时被使用,但是它们要付出巨大的代价以减少并发操作的数量。     

要回复问题请先登录注册