Bar*_*lom 32 sql database concurrency transactions race-condition
我不完全清楚数据库系统中的事务是做什么的.我知道他们可以用来完全回滚更新列表(例如在一个帐户上扣钱并将其添加到另一个帐户),但这就是他们所做的一切吗?具体来说,他们可以用来预防竞争条件吗?例如:
// 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,但也欢迎一般的答案.
Cra*_*ger 30
TL/DR:交易本身并不能阻止所有竞争条件.在所有实际数据库实现中,您仍需要锁定,中止和重试处理或其他保护措施.事务不是你可以添加到查询中的秘密,以使它们免受所有并发效应的影响.
什么你与你的问题越来越善于就是我在ACID - 隔离.学术上纯粹的想法是,事务应该提供完美的隔离,因此结果与每个事务串行执行的结果相同.实际上,在真正的RDBMS实现中很少出现这种情况; 能力由实现方式而变化,并且规则可以通过使用更弱的被削弱隔离级别等READ COMMITTED.在实践中,您不能假设交易会阻止所有竞争条件,即使是在SERIALIZABLE隔离时也是如此.
一些RDBMS比其他RDBMS具有更强的能力.例如,PostgreSQL 9.2和更新版本具有相当好的SERIALIZABLE隔离性,可以检测事务之间的大多数(但不是全部)可能的交互,并中止除了一个冲突事务之外的所有事务.因此它可以非常安全地并行运行事务.
很少,如果有的3,系统具有真正完美的SERIALIZABLE,防止一切可能的种族和异常情况,包括像锁升级和锁排序死锁问题隔离.
即使有强烈的隔离,一些系统(如PostgreSQL)也会中止冲突的事务,而不是让它们等待并连续运行它们.您的应用必须记住它正在做什么并重新尝试交易.因此,虽然事务阻止了与并发相关的异常存储到DB,但它是以对应用程序不透明的方式完成的.
可以说,数据库事务的主要目的是提供原子提交.在您提交事务之前,更改不会生效.提交时,所有更改都会在与其他事务相关的同一时刻生效.没有任何交易可以看到交易所产生的一些变化1,2.同样,如果您ROLLBACK,那么任何其他交易都不会看到任何交易的变化; 这就像你的交易从未存在过一样.
那是一个在ACID.
另一种是持久性-在d的ACID.它指定当您提交事务时,它必须真正保存到存储中,以便在断电或突然重启等故障时继续存在.
见维基百科
通常,像Hibernate,EclipseLink等ORM使用乐观并发控制(通常称为"乐观锁定")来克服较弱隔离级别的限制,同时保持性能,而不是使用锁定和/或高隔离级别.
这种方法的一个关键特性是它允许您跨越多个事务跨越工作,这对于具有高用户数且在与任何给定用户的交互之间可能具有长延迟的系统来说是一个很大的优点.
除了文本内链接之外,请参阅有关锁定,隔离和并发的PostgreSQL文档章节.即使您使用的是不同的RDBMS,您也可以从它解释的概念中学到很多东西.
1READ UNCOMMITTED为简单起见,我忽略了很少实现的隔离级别; 它允许脏读.
2正如@meriton指出的那样,推论并不一定如此.幻影读取发生在下面的任何内容中SERIALIZABLE.正在进行的事务的一部分没有看到某些更改(通过尚未提交的事务),然后正在进行的事务的下一部分确实会在其他事务提交时看到更改.
3嗯,IIRC SQLite2通过在尝试写入时锁定整个数据库来做,但这不是我称之为并发问题的理想解决方案.
mer*_*ike 15
数据库层支持不同程度的事务原子性,称为隔离级别.检查数据库管理系统的文档,了解支持的隔离级别及其权衡.最强的隔离级别Serializable要求事务执行就好像它们是逐个执行的一样.这通常通过在数据库中使用独占锁来实现.这可能导致死锁,数据库管理系统通过回滚一些涉及的事务来检测和修复死锁.这种方法通常被称为悲观锁定.
许多对象关系映射器(包括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;
并抛出异常,以便应用程序代码可以处理失败的事务,例如通过重试.
简介:无论采用哪种方法,您的陈述都可以在竞争条件下保持安全.
| 归档时间: | 
 | 
| 查看次数: | 15220 次 | 
| 最近记录: |