欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

Hibernate锁定模式– PESSIMISTIC_READ和PESSIMISTIC_WRITE如何工作

发布时间:2023/12/3 编程问答 45 豆豆
生活随笔 收集整理的这篇文章主要介绍了 Hibernate锁定模式– PESSIMISTIC_READ和PESSIMISTIC_WRITE如何工作 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

介绍

Java Persistence API带有完善的并发控制机制,支持隐式和显式锁定。 隐式锁定机制很简单,它依赖于:

  • 乐观锁定:实体状态更改可以触发版本增加
  • 行级锁定:基于当前运行的事务隔离级别 ,INSERT / UPDATE / DELETE语句可能会获取排他行锁定

虽然隐式锁定适用于许多情况,但显式锁定机制可以利用更细粒度的并发控制。

在之前的文章中,我介绍了显式的乐观锁定模式:

  • 乐观的
  • OPTIMISTIC_FORCE_INCREMENT
  • PESSIMISTIC_FORCE_INCREMENT

在这篇文章中,我将解开显式的悲观锁模式:

  • PESSIMISTIC_READ
  • PESSIMISTIC_WRITE

读写器锁

数据库系统是高度并发的环境,因此许多并发理论习惯用法也适用于数据库访问。 必须将并发更改序列化以保留数据完整性,因此,即使通常通过Multiversion并发控制机制对其进行补充,大多数数据库系统也使用两阶段锁定策略。

因为互斥锁定会阻碍可伸缩性(平等地进行读写操作),所以大多数数据库系统都使用读写器锁定同步方案,因此:

  • 共享(读取)锁会阻止作者,从而允许多个读者继续
  • 排他(写入)锁同时阻止读取器和写入器,从而使所有写入操作顺序地应用

因为锁定语法不是SQL标准的一部分,所以每个RDBMS都选择了不同的语法:

数据库名称 共享锁语句 排他锁声明
Oracle 更新 更新
MySQL 锁定共享模式 更新
Microsoft SQL服务器 带(HOLDLOCK,ROWLOCK) 带(上锁,上锁)
PostgreSQL 分享 更新
DB2 只供RS阅读 用于RS更新

Java持久性抽象层隐藏了数据库特定的锁定语义,提供了仅需要两个锁定模式的通用API。 使用PESSIMISTIC_READ锁定模式类型获取共享/读取锁定,而使用PESSIMISTIC_WRITE请求排他/写入锁定。

PostgreSQL行级锁定模式

在下一个测试案例中,我们将使用PostgreSQL,因为它既支持独占锁定 ,也支持共享显式锁定 。

以下所有测试将使用相同的并发实用程序,模拟两个用户:Alice和Bob。 每个测试方案将验证特定的读/写锁定组合。

private void testPessimisticLocking(ProductLockRequestCallable primaryLockRequestCallable, ProductLockRequestCallable secondaryLockRequestCallable) {doInTransaction(session -> {try {Product product = (Product) session.get(Product.class, 1L);primaryLockRequestCallable.lock(session, product);executeAsync(() -> {doInTransaction(_session -> {Product _product = (Product) _session.get(Product.class, 1L);secondaryLockRequestCallable.lock(_session, _product);});},endLatch::countDown);sleep(WAIT_MILLIS);} catch (StaleObjectStateException e) {LOGGER.info("Optimistic locking failure: ", e);}});awaitOnLatch(endLatch); }

情况1:PESSIMISTIC_READ不阻止PESSIMISTIC_READ锁定请求

第一个测试将检查两个并发的PESSIMISTIC_READ锁定请求如何交互:

@Test public void testPessimisticReadDoesNotBlockPessimisticRead() throws InterruptedException {LOGGER.info("Test PESSIMISTIC_READ doesn't block PESSIMISTIC_READ");testPessimisticLocking((session, product) -> {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);LOGGER.info("PESSIMISTIC_READ acquired");},(session, product) -> {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);LOGGER.info("PESSIMISTIC_READ acquired");}); }

运行此测试,我们得到以下输出:

[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Test PESSIMISTIC_READ doesn't block PESSIMISTIC_READ#Alice selects the Product entity [Alice]: Time:1 Query:{[ SELECT lockmodepe0_.id AS id1_0_0_,lockmodepe0_.description AS descript2_0_0_,lockmodepe0_.price AS price3_0_0_,lockmodepe0_.version AS version4_0_0_ FROM product lockmodepe0_ WHERE lockmodepe0_.id = ? ][1]} #Alice acquires a SHARED lock on the Product entity [Alice]: Time:1 Query:{[ SELECT id FROM product WHERE id =? AND version =? FOR share ][1,0]} [Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_READ acquired#Alice waits for 500ms [Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Wait 500 ms!#Bob selects the Product entity [Bob]: Time:1 Query:{[SELECT lockmodepe0_.id AS id1_0_0_,lockmodepe0_.description AS descript2_0_0_,lockmodepe0_.price AS price3_0_0_,lockmodepe0_.version AS version4_0_0_ FROM product lockmodepe0_ WHERE lockmodepe0_.id = ? ][1]}#Bob acquires a SHARED lock on the Product entity [Bob]: Time:1 Query:{[ SELECT id FROM product WHERE id =? AND version =? FOR share ][1,0]} [Bob]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_READ acquired#Bob's transactions is committed [Bob]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection#Alice's transactions is committed [Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

在这种情况下,没有任何争用。 爱丽丝和鲍勃都可以获取共享锁,而不会发生任何冲突。

情况2:PESSIMISTIC_READ阻止UPDATE隐式锁定请求

第二种情况将演示共享锁如何防止并发修改。 爱丽丝将获取共享锁,而鲍勃将尝试修改锁定的实体:

@Test public void testPessimisticReadBlocksUpdate() throws InterruptedException {LOGGER.info("Test PESSIMISTIC_READ blocks UPDATE");testPessimisticLocking((session, product) -> {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);LOGGER.info("PESSIMISTIC_READ acquired");},(session, product) -> {product.setDescription("USB Flash Memory Stick");session.flush();LOGGER.info("Implicit lock acquired");}); }

测试生成以下输出:

[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Test PESSIMISTIC_READ blocks UPDATE#Alice selects the Product entity [Alice]: Time:0 Query:{[ SELECT lockmodepe0_.id AS id1_0_0_,lockmodepe0_.description AS descript2_0_0_,lockmodepe0_.price AS price3_0_0_,lockmodepe0_.version AS version4_0_0_ FROM product lockmodepe0_ WHERE lockmodepe0_.id = ? ][1]} #Alice acquires a SHARED lock on the Product entity [Alice]: Time:0 Query:{[ SELECT id FROM product WHERE id =? AND version =? FOR share ][1,0]} [Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_READ acquired#Alice waits for 500ms [Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Wait 500 ms!#Bob selects the Product entity [Bob]: Time:1 Query:{[ SELECT lockmodepe0_.id AS id1_0_0_,lockmodepe0_.description AS descript2_0_0_,lockmodepe0_.price AS price3_0_0_,lockmodepe0_.version AS version4_0_0_ FROM product lockmodepe0_ WHERE lockmodepe0_.id = ? ][1]} #Alice's transactions is committed [Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection#Bob can acquire the Product entity lock, only after Alice's transaction is committed [Bob]: Time:427 Query:{[ UPDATE product SET description = ?,price = ?,version = ? WHERE id = ?AND version = ? ][USB Flash Memory Stick,12.99,1,1,0]} [Bob]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Implicit lock acquired#Bob's transactions is committed [Bob]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

尽管Bob可以选择Product实体,但UPDATE会一直延迟到提交Alice事务为止(这就是UPDATE花费427ms运行的原因)。

情况3:PESSIMISTIC_READ阻止PESSIMISTIC_WRITE锁定请求

辅助PESSIMISTIC_WRITE锁定请求也表现出相同的行为:

@Test public void testPessimisticReadBlocksPessimisticWrite() throws InterruptedException {LOGGER.info("Test PESSIMISTIC_READ blocks PESSIMISTIC_WRITE");testPessimisticLocking((session, product) -> {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);LOGGER.info("PESSIMISTIC_READ acquired");},(session, product) -> {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(product);LOGGER.info("PESSIMISTIC_WRITE acquired");}); }

提供以下输出:

[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Test PESSIMISTIC_READ blocks PESSIMISTIC_WRITE#Alice selects the Product entity [Alice]: Time:0 Query:{[ SELECT lockmodepe0_.id AS id1_0_0_,lockmodepe0_.description AS descript2_0_0_,lockmodepe0_.price AS price3_0_0_,lockmodepe0_.version AS version4_0_0_ FROM product lockmodepe0_ WHERE lockmodepe0_.id = ? ][1]}#Alice acquires a SHARED lock on the Product entity [Alice]: Time:1 Query:{[ SELECT id FROM product WHERE id =? AND version =? FOR share ][1,0]} [Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_READ acquired#Alice waits for 500ms [Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Wait 500 ms!#Bob selects the Product entity [Bob]: Time:1 Query:{[ SELECT lockmodepe0_.id AS id1_0_0_,lockmodepe0_.description AS descript2_0_0_,lockmodepe0_.price AS price3_0_0_,lockmodepe0_.version AS version4_0_0_ FROM product lockmodepe0_ WHERE lockmodepe0_.id = ? ][1]} #Alice's transactions is committed [Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection#Bob can acquire the Product entity lock, only after Alice's transaction is committed [Bob]: Time:428 Query:{[ SELECT id FROM product WHERE id = ?AND version = ? FOR UPDATE ][1,0]} [Bob]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_WRITE acquired#Bob's transactions is committed [Bob]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

Bob的排他锁请求等待Alice的共享锁被释放。

情况4:PESSIMISTIC_READ阻止PESSIMISTIC_WRITE锁定请求,NO WAIT快速失败

Hibernate提供了一个PESSIMISTIC_NO_WAIT超时指令,该指令转换为特定于数据库的NO_WAIT锁获取策略。

PostgreSQL NO WAIT指令描述如下:

为防止该操作等待其他事务提交,请使用NOWAIT选项。 使用NOWAIT,如果无法立即锁定选定的行,该语句将报告错误,而不是等待。 注意,NOWAIT仅适用于行级锁-所需的ROW SHARE表级锁仍以常规方式获取(请参见第13章)。 如果需要无需等待就获取表级锁,则可以先将LOCK与NOWAIT选项一起使用。

@Test public void testPessimisticReadWithPessimisticWriteNoWait() throws InterruptedException {LOGGER.info("Test PESSIMISTIC_READ blocks PESSIMISTIC_WRITE, NO WAIT fails fast");testPessimisticLocking((session, product) -> {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);LOGGER.info("PESSIMISTIC_READ acquired");},(session, product) -> {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).setTimeOut(Session.LockRequest.PESSIMISTIC_NO_WAIT).lock(product);LOGGER.info("PESSIMISTIC_WRITE acquired");}); }

该测试生成以下输出:

[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Test PESSIMISTIC_READ blocks PESSIMISTIC_WRITE, NO WAIT fails fast#Alice selects the Product entity [Alice]: Time:1 Query:{[ SELECT lockmodepe0_.id AS id1_0_0_,lockmodepe0_.description AS descript2_0_0_,lockmodepe0_.price AS price3_0_0_,lockmodepe0_.version AS version4_0_0_ FROM product lockmodepe0_ WHERE lockmodepe0_.id = ? ][1]}#Alice acquires a SHARED lock on the Product entity [Alice]: Time:1 Query:{[ SELECT id FROM product WHERE id =? AND version =? FOR share ][1,0]} [Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_READ acquired#Alice waits for 500ms [Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Wait 500 ms!#Bob selects the Product entity [Bob]: Time:1 Query:{[ SELECT lockmodepe0_.id AS id1_0_0_,lockmodepe0_.description AS descript2_0_0_,lockmodepe0_.price AS price3_0_0_,lockmodepe0_.version AS version4_0_0_ FROM product lockmodepe0_ WHERE lockmodepe0_.id = ? ][1]}#Bob tries to acquire an EXCLUSIVE lock on the Product entity and fails because of the NO WAIT policy [Bob]: Time:0 Query:{[ SELECT id FROM product WHERE id = ?AND version = ? FOR UPDATE nowait ][1,0]} [Bob]: o.h.e.j.s.SqlExceptionHelper - SQL Error: 0, SQLState: 55P03 [Bob]: o.h.e.j.s.SqlExceptionHelper - ERROR: could not obtain lock on row in relation "product"#Bob's transactions is rolled back [Bob]: o.h.e.t.i.j.JdbcTransaction - rolled JDBC Connection#Alice's transactions is committed [Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

由于Alice已经在与产品实体相关联的数据库行上持有共享锁,因此Bob的排他锁请求立即失败。

情况5:PESSIMISTIC_WRITE阻止PESSIMISTIC_READ锁定请求

下一个测试证明排他锁将始终阻止共享锁获取尝试:

@Test public void testPessimisticWriteBlocksPessimisticRead() throws InterruptedException {LOGGER.info("Test PESSIMISTIC_WRITE blocks PESSIMISTIC_READ");testPessimisticLocking((session, product) -> {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(product);LOGGER.info("PESSIMISTIC_WRITE acquired");},(session, product) -> {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_READ)).lock(product);LOGGER.info("PESSIMISTIC_WRITE acquired");}); }

生成以下输出:

[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Test PESSIMISTIC_WRITE blocks PESSIMISTIC_READ#Alice selects the Product entity [Alice]: Time:1 Query:{[ SELECT lockmodepe0_.id AS id1_0_0_,lockmodepe0_.description AS descript2_0_0_,lockmodepe0_.price AS price3_0_0_,lockmodepe0_.version AS version4_0_0_ FROM product lockmodepe0_ WHERE lockmodepe0_.id = ? ][1]} #Alice acquires an EXCLUSIVE lock on the Product entity [Alice]: Time:0 Query:{[ SELECT id FROM product WHERE id = ?AND version = ? FOR UPDATE ][1,0]} [Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_WRITE acquired#Alice waits for 500ms [Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Wait 500 ms!#Bob selects the Product entity [Bob]: Time:1 Query:{[ SELECT lockmodepe0_.id AS id1_0_0_,lockmodepe0_.description AS descript2_0_0_,lockmodepe0_.price AS price3_0_0_,lockmodepe0_.version AS version4_0_0_ FROM product lockmodepe0_ WHERE lockmodepe0_.id = ? ][1]}#Alice's transactions is committed [Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection#Bob can acquire the Product entity SHARED lock, only after Alice's transaction is committed [Bob]: Time:428 Query:{[ SELECT id FROM product WHERE id =? AND version =? FOR share ][1,0]} [Bob]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_WRITE acquired#Bob's transactions is committed [Bob]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

Bob的共享锁请求等待Alice的事务结束,以便释放所有获得的锁。

情况6:PESSIMISTIC_WRITE阻止PESSIMISTIC_WRITE锁定请求

排他锁也将阻止排他锁:

@Test public void testPessimisticWriteBlocksPessimisticWrite() throws InterruptedException {LOGGER.info("Test PESSIMISTIC_WRITE blocks PESSIMISTIC_WRITE");testPessimisticLocking((session, product) -> {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(product);LOGGER.info("PESSIMISTIC_WRITE acquired");},(session, product) -> {session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(product);LOGGER.info("PESSIMISTIC_WRITE acquired");}); }

测试生成以下输出:

[Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Test PESSIMISTIC_WRITE blocks PESSIMISTIC_WRITE#Alice selects the Product entity [Alice]: Time:1 Query:{[ SELECT lockmodepe0_.id AS id1_0_0_,lockmodepe0_.description AS descript2_0_0_,lockmodepe0_.price AS price3_0_0_,lockmodepe0_.version AS version4_0_0_ FROM product lockmodepe0_ WHERE lockmodepe0_.id = ? ][1]} #Alice acquires an EXCLUSIVE lock on the Product entity [Alice]: Time:0 Query:{[ SELECT id FROM product WHERE id = ?AND version = ? FOR UPDATE ][1,0]} [Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_WRITE acquired#Alice waits for 500ms [Alice]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - Wait 500 ms!#Bob selects the Product entity [Bob]: Time:1 Query:{[ SELECT lockmodepe0_.id AS id1_0_0_,lockmodepe0_.description AS descript2_0_0_,lockmodepe0_.price AS price3_0_0_,lockmodepe0_.version AS version4_0_0_ FROM product lockmodepe0_ WHERE lockmodepe0_.id = ? ][1]}#Alice's transactions is committed [Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection#Bob can acquire the Product entity SHARED lock, only after Alice's transaction is committed [Bob]: Time:428 Query:{[ SELECT id FROM product WHERE id =? AND version =? FOR update ][1,0]} [Bob]: c.v.h.m.l.c.LockModePessimisticReadWriteIntegrationTest - PESSIMISTIC_WRITE acquired#Bob's transactions is committed [Bob]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection

Bob的排他锁请求必须等待Alice释放其锁。

结论

关系数据库系统使用锁来保留ACID保证 ,因此了解共享和排行级锁如何互操作非常重要。 显式悲观锁是一种非常强大的数据库并发控制机制,您甚至可以使用它来修复乐观锁竞争条件 。

  • 代码可在GitHub上获得 。

翻译自: https://www.javacodegeeks.com/2015/02/hibernate-locking-patterns-how-does-pessimistic_read-and-pessimistic_write-work.html

总结

以上是生活随笔为你收集整理的Hibernate锁定模式– PESSIMISTIC_READ和PESSIMISTIC_WRITE如何工作的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。