糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > oracle中悲观锁定_如何使用悲观锁定修复乐观锁定竞争条件

oracle中悲观锁定_如何使用悲观锁定修复乐观锁定竞争条件

时间:2020-03-21 15:31:37

相关推荐

oracle中悲观锁定_如何使用悲观锁定修复乐观锁定竞争条件

oracle中悲观锁定

回顾

在我以前的文章中 ,我解释了使用显式乐观锁定的好处。 然后我们发现,在很短的时间范围内,并发交易仍可以在我们当前交易被提交之前立即提交产品价格更改。

此问题可以描述如下:

爱丽丝拿产品 然后,她决定订购 获得产品乐观锁 订单已插入当前交易数据库会话中 产品版本由Hibernate显式乐观锁定例程检查 价格引擎设法提交产品价格更改 承诺进行Alice交易而未意识到产品价格刚刚改变

复制问题

因此,我们需要一种在乐观锁支票和订单交易提交之间注入产品价格变化的方法。

在分析了Hibernate源代码之后,我们发现SessionImpl.beforeTransactionCompletion()方法正在内部actionQueue阶段处理程序(检查显式乐观锁定实体版本)之后紧接着调用当前配置的Interceptor.beforeTransactionCompletion()回调:

public void beforeTransactionCompletion(TransactionImplementor hibernateTransaction) {LOG.trace( "before transaction completion" );actionQueue.beforeTransactionCompletion();try {interceptor.beforeTransactionCompletion( hibernateTransaction );}catch (Throwable t) {LOG.exceptionInBeforeTransactionCompletionInterceptor( t );}}

有了这些信息,我们可以设置一个测试来复制我们的比赛条件:

private AtomicBoolean ready = new AtomicBoolean();private final CountDownLatch endLatch = new CountDownLatch(1);@Overrideprotected Interceptor interceptor() {return new EmptyInterceptor() {@Overridepublic void beforeTransactionCompletion(Transaction tx) {if(ready.get()) {LOGGER.info("Overwrite product price asynchronously");executeNoWait(new Callable<Void>() {@Overridepublic Void call() throws Exception {Session _session = getSessionFactory().openSession();_session.doWork(new Work() {@Overridepublic void execute(Connection connection) throws SQLException {try(PreparedStatement ps = connection.prepareStatement("UPDATE product set price = 14.49 WHERE id = 1")) {ps.executeUpdate();}}});_session.close();endLatch.countDown();return null;}});try {LOGGER.info("Wait 500 ms for lock to be acquired!");Thread.sleep(500);} catch (InterruptedException e) {throw new IllegalStateException(e);}}}};}@Testpublic void testExplicitOptimisticLocking() throws InterruptedException {try {doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session session) {try {final Product product = (Product) session.get(Product.class, 1L, new LockOptions(LockMode.OPTIMISTIC));OrderLine orderLine = new OrderLine(product);session.persist(orderLine);lockUpgrade(session, product);ready.set(true);} catch (Exception e) {throw new IllegalStateException(e);}return null;}});} catch (OptimisticEntityLockException expected) {LOGGER.info("Failure: ", expected);}endLatch.await();}protected void lockUpgrade(Session session, Product product) {}

运行它时,测试将生成以下输出:

#Alice selects a ProductDEBUG [main]: Query:{[select abstractlo0_.id as id1_1_0_, abstractlo0_.description as descript2_1_0_, abstractlo0_.price as price3_1_0_, abstractlo0_.version as version4_1_0_ from product abstractlo0_ where abstractlo0_.id=?][1]} #Alice inserts an OrderLineDEBUG [main]: Query:{[insert into order_line (id, product_id, unitPrice, version) values (default, ?, ?, ?)][1,12.99,0]} #Alice transaction verifies the Product versionDEBUG [main]: Query:{[select version from product where id =?][1]} #The price engine thread is startedINFO [main]: c.v.h.m.l.c.LockModeOptimisticRaceConditionTest - Overwrite product price asynchronously#Alice thread sleeps for 500ms to give the price engine a chance to execute its transactionINFO [main]: c.v.h.m.l.c.LockModeOptimisticRaceConditionTest - Wait 500 ms for lock to be acquired!#The price engine changes the Product priceDEBUG [pool-1-thread-1]: Query:{[UPDATE product set price = 14.49 WHERE id = 1][]} #Alice transaction is committed without realizing the Product price changeDEBUG [main]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

因此,比赛条件是真实的。 由您决定当前的应用程序是否需要更强的数据完整性要求,但是根据经验,安全性要好于遗憾。

解决问题

要解决此问题,我们只需要在结束事务处理方法之前添加一个悲观的锁定请求即可。

@Overrideprotected void lockUpgrade(Session session, Product product) {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);}

显式共享锁将防止对我们之前乐观地锁定的实体进行并发写入。 使用此方法,在释放此锁之前(在提交或回滚当前事务之后),没有其他并发事务可以更改产品。

有了新的悲观锁定请求,先前的测试将产生以下输出:

#Alice selects a ProductDEBUG [main]: Query:{[select abstractlo0_.id as id1_1_0_, abstractlo0_.description as descript2_1_0_, abstractlo0_.price as price3_1_0_, abstractlo0_.version as version4_1_0_ from product abstractlo0_ where abstractlo0_.id=?][1]} #Alice inserts an OrderLineDEBUG [main]: Query:{[insert into order_line (id, product_id, unitPrice, version) values (default, ?, ?, ?)][1,12.99,0]} #Alice applies an explicit physical lock on the Product entityDEBUG [main]: Query:{[select id from product where id =? and version =? for update][1,0]} #Alice transaction verifies the Product versionDEBUG [main]: Query:{[select version from product where id =?][1]} #The price engine thread is startedINFO [main]: c.v.h.m.l.c.LockModeOptimisticRaceConditionTest - Overwrite product price asynchronously#Alice thread sleeps for 500ms to give the price engine a chance to execute its transactionINFO [main]: c.v.h.m.l.c.LockModeOptimisticRaceConditionTest - Wait 500 ms for lock to be acquired!#The price engine cannot proceed because of the Product entity was locked exclusively, so Alice transaction is committed against the ordered Product priceDEBUG [main]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection#The physical lock is released and the price engine can change the Product priceDEBUG [pool-1-thread-1]: Query:{[UPDATE product set price = 14.49 WHERE id = 1][]}

即使我们要求使用PESSIMISTIC_READ锁,HSQLDB也只能执行FOR UPDATE排他锁,这等效于显式的PESSIMISTIC_WRITE锁模式。

结论

如果您想知道为什么我们在当前事务中同时使用乐观锁定和悲观锁定,则必须记住, 乐观锁定是多请求对话唯一可行的并发控制机制。

在我们的示例中,第一个请求使用只读事务加载了Product实体。 产品实体具有关联的版本,并且在写时事务期间将乐观地锁定此读时实体快照。

悲观锁仅在写时事务期间有用,以防止在检查产品实体版本后发生任何并发更新。 因此,逻辑锁和物理锁都可以协同工作以确保订单价格数据的完整性。

在我撰写此博客文章时, Java冠军 Markus Eisele 接受了有关Hibernate Master Class计划的采访 。 在采访中,我试图解释当前的帖子示例,同时强调了解参考文档之外的工具的真正重要性。

代码可在GitHub上获得 。

翻译自: //02/fix-optimistic-locking-race-conditions-pessimistic-locking.html

oracle中悲观锁定

如果觉得《oracle中悲观锁定_如何使用悲观锁定修复乐观锁定竞争条件》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。