休眠锁定模式– PESSIMISTIC_READ和PESSIMISTIC_WRITE如何工作
介紹
Java Persistence API帶有完善的并發(fā)控制機(jī)制,支持隱式和顯式鎖定。 隱式鎖定機(jī)制很簡單,它依賴于:
- 樂觀鎖定:實(shí)體狀態(tài)更改可以觸發(fā)版本增加
- 行級(jí)鎖定:基于當(dāng)前運(yùn)行的事務(wù)隔離級(jí)別 ,INSERT / UPDATE / DELETE語句可能會(huì)獲取排他行鎖
雖然隱式鎖定適用于許多情況,但顯式鎖定機(jī)制可以利用更細(xì)粒度的并發(fā)控制。
在以前的文章中,我介紹了顯式的樂觀鎖定模式:
- 樂觀的
- OPTIMISTIC_FORCE_INCREMENT
- PESSIMISTIC_FORCE_INCREMENT
在這篇文章中,我將解開顯式的悲觀鎖模式:
- PESSIMISTIC_READ
- PESSIMISTIC_WRITE
讀寫器鎖
數(shù)據(jù)庫系統(tǒng)是高度并發(fā)的環(huán)境,因此許多并發(fā)理論習(xí)慣用法也適用于數(shù)據(jù)庫訪問。 必須將并發(fā)更改序列化以保留數(shù)據(jù)完整性,因此,即使通常通過Multiversion并發(fā)控制機(jī)制對(duì)其進(jìn)行補(bǔ)充,大多數(shù)數(shù)據(jù)庫系統(tǒng)仍使用兩階段鎖定策略。
因?yàn)榛コ怄i定會(huì)阻礙可伸縮性(平等地處理讀寫),所以大多數(shù)數(shù)據(jù)庫系統(tǒng)都使用讀寫器鎖定同步方案,因此:
- 共享(讀取)鎖會(huì)阻止作者,從而允許多個(gè)讀者繼續(xù)
- 排他(寫入)鎖同時(shí)阻止讀取器和寫入器,從而使所有寫入操作都按順序應(yīng)用
因?yàn)殒i定語法不是SQL標(biāo)準(zhǔn)的一部分,所以每個(gè)RDBMS都選擇了不同的語法:
| 甲骨文 | 更新 | 更新 |
| 的MySQL | 鎖定共享模式 | 更新 |
| Microsoft SQL服務(wù)器 | 帶(HOLDLOCK,ROWLOCK) | 帶(上鎖,上鎖) |
| PostgreSQL的 | 分享 | 更新 |
| DB2 | 只供RS閱讀 | 用于RS更新 |
Java持久性抽象層隱藏了特定于數(shù)據(jù)庫的鎖定語義,提供了僅需要兩個(gè)鎖定模式的通用API。 使用PESSIMISTIC_READ鎖定模式類型獲取共享/讀取鎖定,而使用PESSIMISTIC_WRITE請(qǐng)求排他/寫入鎖定。
PostgreSQL行級(jí)鎖定模式
對(duì)于下一個(gè)測試用例,我們將使用PostgreSQL,因?yàn)樗戎С知?dú)占鎖定 ,也支持共享顯式鎖定 。
以下所有測試將使用相同的并發(fā)實(shí)用程序,模擬兩個(gè)用戶:Alice和Bob。 每個(gè)測試方案將驗(yàn)證特定的讀/寫鎖定組合。
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鎖定請(qǐng)求
第一個(gè)測試將檢查兩個(gè)并發(fā)的PESSIMISTIC_READ鎖定請(qǐng)求如何交互:
@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");}); }運(yùn)行此測試,我們得到以下輸出:
[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在這種情況下,沒有任何爭用。 愛麗絲和鮑勃都可以獲取共享鎖,而不會(huì)遇到任何沖突。
情況2:PESSIMISTIC_READ阻止UPDATE隱式鎖定請(qǐng)求
第二種情況將演示共享鎖如何防止并發(fā)修改。 愛麗絲將獲得一個(gè)共享鎖,而鮑勃將嘗試修改該鎖定的實(shí)體:
@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實(shí)體,但UPDATE會(huì)一直延遲到提交Alice事務(wù)為止(這就是UPDATE花費(fèi)427ms運(yùn)行的原因)。
情況3:PESSIMISTIC_READ阻止PESSIMISTIC_WRITE鎖定請(qǐng)求
輔助PESSIMISTIC_WRITE鎖定請(qǐng)求也表現(xiàn)出相同的行為:
@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 ConnectionBob的排他鎖請(qǐng)求等待Alice的共享鎖被釋放。
情況4:PESSIMISTIC_READ阻止PESSIMISTIC_WRITE鎖定請(qǐng)求,NO WAIT快速失敗
Hibernate提供了一個(gè)PESSIMISTIC_NO_WAIT超時(shí)指令,該指令轉(zhuǎn)換為特定于數(shù)據(jù)庫的NO_WAIT鎖獲取策略。
PostgreSQL NO WAIT指令描述如下:
為防止該操作等待其他事務(wù)提交,請(qǐng)使用NOWAIT選項(xiàng)。 使用NOWAIT,如果無法立即鎖定選定的行,該語句將報(bào)告錯(cuò)誤,而不是等待。 注意,NOWAIT僅適用于行級(jí)鎖-所需的ROW SHARE表級(jí)鎖仍以常規(guī)方式獲取(請(qǐng)參見第13章)。 如果需要不等待就獲取表級(jí)鎖,則可以先將LOCK與NOWAIT選項(xiàng)一起使用。
@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已經(jīng)在與產(chǎn)品實(shí)體相關(guān)聯(lián)的數(shù)據(jù)庫行上持有共享鎖,因此Bob的排他鎖請(qǐng)求立即失敗。
情況5:PESSIMISTIC_WRITE阻止PESSIMISTIC_READ鎖定請(qǐng)求
下一個(gè)測試證明排他鎖將始終阻止共享鎖獲取嘗試:
@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 ConnectionBob的共享鎖請(qǐng)求等待Alice的事務(wù)結(jié)束,以便釋放所有獲得的鎖。
情況6:PESSIMISTIC_WRITE阻止PESSIMISTIC_WRITE鎖定請(qǐng)求
排他鎖也阻止排他鎖:
@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 ConnectionBob的排他鎖請(qǐng)求必須等待Alice釋放其鎖。
結(jié)論
關(guān)系數(shù)據(jù)庫系統(tǒng)使用鎖來保留ACID保證 ,因此了解共享和獨(dú)占行級(jí)鎖如何互操作非常重要。 顯式悲觀鎖是一種非常強(qiáng)大的數(shù)據(jù)庫并發(fā)控制機(jī)制,您甚至可以使用它來修復(fù)樂觀鎖競爭條件 。
- 代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2015/02/hibernate-locking-patterns-how-does-pessimistic_read-and-pessimistic_write-work.html
總結(jié)
以上是生活随笔為你收集整理的休眠锁定模式– PESSIMISTIC_READ和PESSIMISTIC_WRITE如何工作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (wait linux)
- 下一篇: 将WildFly绑定到其他IP地址或多宿