我正在使用我的Java Spring应用程序与Spring Hibernate Orm一起使用MySQL DBMS进行数据管理。我有一个简单的要求 - 当有人使用我的应用程序时,我的数据库中的计数器将增加1,以便我可以跟踪用户的使用计数。
我在这里有两个简短的实现,一个我想讨论的是,一个在高负载下给了我一个乐观的锁例外(我对许多并发用户进行了模拟以测试加载(,一个没有。有人可以指导我理解这种行为背后的差异,原因以及是否确保数据正确性?
这是我的拳头代码样本。在压力测试下,抛出了OptipisticLockexcextion。
@Transactional
public void updateLog() {
ConnectionUseLog log = getLog();
log.setCount(log.getCount() + 1);
// ... other unrelated updates to database
}
这里的连接uselog是一个@data类,带有@version属性用于乐观锁定和" count"属性(这是我以1的增量来增加的(。
@Data
public class ConnectionUseLog {
@Version
@Column(name="optlock_version")
Integer version;
@Column(name="count")
Integer count;
}
- getlog((是我写的原始选择查询,得到我想要的行。它使用springframework.data.repository的pagingandSortingRepository界面。
- getCount(...(在列中获得值,方法是自动化的。
- setCount(...(更新计数,方法是自动化的。
现在,在下一个示例中,它更简单,简单,简单的原始更新查询(不是以前的选择( - 没有问题。RAW查询还通过springframework.data.repository作为上一个示例的pagingandSortingRepository接口嵌入。
。@Transactional
public void updateDailyLog() {
incrementCount(1);
// ... other unrelated updates to database
}
当两者都包裹在 @transactional中时,为什么会有乐观的并发问题?
有人可以帮助理解差异并帮助我权衡这两种方法吗?谢谢。
我不确定为什么您认为@Transactional
应该使您的OptimisticLockingException
消失。乐观的锁定与不同交易的并发性有关。
它可以通过在每次交易中汇总一个版本,还可以检查更新,以使该版本与加载实体时保持不变。
在高负载下,对于此类事件,这通常会失败:
- 交易1(t1(加载一排,版本23。
- 交易2(t2(用同一版本23加载同一行。
- t1更新计数,将值增加到24,并提交使更改的值可见到其他交易。
- T2也尝试更新该行,但是版本不再是23,因此您会得到一个乐观的锁定例外。
- 您应该重新启动T2,直到成功更新数据库为止。
请注意,这两个事务都将阻塞锁定在行上。在大多数情况下,这是一件好事,因为它可以提高性能。如果数字4经常发生。
,这个好处就会消失。进行"原始更新"时,我猜这与UPDATE log set counter = counter + 1 WHERE ...
一样,没有乐观的锁定。相反,您基本上正在使用悲观的锁定:您在阅读时要更新的行锁定。这是因为读写和写入发生在同一语句中。这意味着类似上述类似的过程现在将如下:
- 交易1(T1(更新一行锁定。
- 交易2(T2(试图更新同一行,但被锁定并必须等待。
- T1进行交易,使其他交易可见并释放锁。
- T2现在可以根据T1的更改值进行进行并更新行。
锁会限制您应用程序的吞吐量,尤其是当交易较长时,如果行被锁定,最终不会更新时。在您的示例中不是这种情况,而是在更典型的示例中,用户加载一行以查看它,也许编辑它更常见。
在您的情况下,直接更新的方法似乎更合适。只需确保交易尽可能短。