Статьи

Шаблоны блокировки гибернации — как работают PESSIMISTIC_READ и PESSIMISTIC_WRITE

Вступление

Java Persistence API поставляется с полным механизмом управления параллелизмом, поддерживающим как явную, так и явную блокировку Механизм неявной блокировки прост и основан на:

Хотя неявная блокировка подходит для многих сценариев, механизм явной блокировки может использовать более детальное управление параллелизмом.

В моих предыдущих постах я рассмотрел явные оптимистические режимы блокировки:

В этом посте я собираюсь раскрыть явные пессимистичные режимы блокировки:

Читатели-писательский замок

Система баз данных — это среда с высокой степенью параллелизма, поэтому многие идиомы теории параллелизма применимы и к доступу к базе данных. Одновременные изменения должны быть сериализованы для сохранения целостности данных, поэтому большинство систем баз данных используют двухфазную стратегию блокировки , даже если она обычно дополняется механизмом управления параллелизмом Multiversion .

Поскольку взаимная исключающая блокировка будет препятствовать масштабируемости (обрабатывать чтение и запись одинаково), большинство систем баз данных используют схему синхронизации блокировки чтения-записи-записи , так что:

  • Общая (читаемая) блокировка блокирует писателей, позволяя нескольким читателям продолжить
  • Эксклюзивная (запись) блокировка блокирует как читателей, так и писателей, заставляя все операции записи применяться последовательно

Поскольку синтаксис блокировки не является частью стандарта SQL, каждая СУБД выбрала свой синтаксис:

Имя базы данных Оператор общей блокировки Эксклюзивная блокировка
оракул ДЛЯ ОБНОВЛЕНИЯ ДЛЯ ОБНОВЛЕНИЯ
MySQL LOCK IN SHARE MODE ДЛЯ ОБНОВЛЕНИЯ
Microsoft SQL Server С (ЗАДЕРЖКА, ТЯГА) С (UPDLOCK, ROWLOCK)
PostgreSQL ДЛЯ ПОДЕЛИТЬСЯ ДЛЯ ОБНОВЛЕНИЯ
DB2 ТОЛЬКО ДЛЯ ЧТЕНИЯ С RS ДЛЯ ОБНОВЛЕНИЯ С RS

Уровень абстракции Java Persistence скрывает специфическую семантику блокировки базы данных, предлагая общий API, который требует только двух режимов блокировки. Блокировка совместного использования / чтения получается с использованием типа режима блокировки PESSIMISTIC_READ, и вместо этого запрашивается блокировка эксклюзивного доступа / записи с использованием PESSIMISTIC_WRITE .

Режимы блокировки на уровне строк в PostgreSQL

В следующих тестах мы будем использовать PostgreSQL, поскольку он поддерживает как эксклюзивную, так и разделяемую явную блокировку .

Во всех следующих тестах будет использоваться одна и та же утилита параллелизма, эмулирующая двух пользователей: Алису и Боба. Каждый тестовый сценарий будет проверять определенную комбинацию блокировки чтения / записи.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
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:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@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");
        }
    );
}

Запустив этот тест, мы получим следующий вывод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
[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 запросы неявной блокировки

Второй сценарий продемонстрирует, как общая блокировка предотвращает одновременную модификацию. Алиса получит общую блокировку, а Боб попытается изменить заблокированную сущность:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@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");
        }
    );
}

Тест генерирует этот вывод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[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

Хотя Боб мог выбрать сущность Product, UPDATE откладывается до тех пор, пока транзакция Алисы не будет зафиксирована (поэтому для выполнения UPDATE потребовалось 427 мс ).

Случай 3: PESSIMISTIC_READ блокирует запросы блокировки PESSIMISTIC_WRITE

Такое же поведение демонстрируется вторичным запросом блокировки PESSIMISTIC_WRITE:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@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");
        }
    );
}

Дать следующий вывод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
[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

Запрос эксклюзивной блокировки Боба ждет, пока будет снята общая блокировка Алисы.

Случай 4: PESSIMISTIC_READ блокирует запросы блокировки PESSIMISTIC_WRITE, NO WAIT быстро завершается неудачно

Hibernate предоставляет директиву тайм-аута PESSIMISTIC_NO_WAIT , которая переводит в специфическую для базы данных политику получения блокировки NO_WAIT.

Директива PostgreSQL NO WAIT описывается следующим образом:

Чтобы операция не ожидала принятия других транзакций, используйте параметр NOWAIT. С NOWAIT оператор сообщает об ошибке, а не об ожидании, если выбранная строка не может быть заблокирована немедленно. Обратите внимание, что NOWAIT применяется только к блокировкам на уровне строк — требуемая блокировка на уровне таблиц ROW SHARE по-прежнему выполняется обычным способом (см. Главу 13). Сначала вы можете использовать LOCK с опцией NOWAIT, если вам нужно получить блокировку на уровне таблицы без ожидания.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@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");
        }
    );
}

Этот тест генерирует следующий вывод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[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

Поскольку Алиса уже имеет общую блокировку в строке базы данных, связанной с объектом Product, запрос исключительной блокировки Боба немедленно завершается неудачей.

Случай 5: PESSIMISTIC_WRITE блокирует запросы блокировки PESSIMISTIC_READ

Следующий тест доказывает, что исключительная блокировка всегда блокирует попытку получения общей блокировки:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@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");
        }
    );
}

Генерация следующего вывода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
[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

Запрос общей блокировки Боба ожидает завершения транзакции Алисы, поэтому все полученные блокировки снимаются.

Случай 6: PESSIMISTIC_WRITE блокирует запросы блокировки PESSIMISTIC_WRITE

Эксклюзивный замок также блокирует эксклюзивный замок:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
@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");
            }
    );
}

Тест генерирует этот вывод:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
[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

Запрос эксклюзивной блокировки Боба должен подождать, пока Алиса снимет блокировку.

Вывод

Реляционные системы баз данных используют блокировки для сохранения гарантий ACID , поэтому важно понимать, как взаимодействуют общие и эксклюзивные блокировки на уровне строк. Явная пессимистическая блокировка — это очень мощный механизм управления параллелизмом базы данных, и вы можете даже использовать его для исправления состояния гонки оптимистичной блокировки .

  • Код доступен на GitHub .