- 원칙 1: InnoDB의 기본 잠금 단위는 넥스트 키 락이며, 넥스트 키 락의 잠금 범위는 좌측으로는 개구간, 우측으로는 폐구간이다.
 ex) (R1, R10]
- InnoDB 잠금의 시작은 넥스트 키 락이다. 이후에 갭 락과 레코드 락을 따져보는 것이다.
 - 원칙 2: 잠금은 쿼리를 수행하는 과정에서 접근한 객체에만 걸린다.
 - 규칙 1: 인덱스(고유, 비고유)를 사용하는 동등 조건의 쿼리를 수행할 때 레코드 스캔 방향은 오른쪽이며, 마지막 레코드가 동등 조건을 만족하지 않으면 넥스트 키 락은 갭 락으로 강등된다.
 - 규칙 2: 고유 인덱스를 사용하는 동등 조건의 쿼리를 수행할 때, 레코드가 동등 조건을 만족하면 넥스트 키 락은 레코드 락으로 강등된다.
 
-- 테스트 테이블 및 데이터 준비
CREATE TABLE t (
  id int NOT NULL,
  a int NULL,
  b int NULL,
  PRIMARY KEY (id),
  KEY ix_a (a)
) ENGINE=InnoDB;
        🔐 잠금 상태 조회 쿼리
-- 잠금 상태 조회하기
SELECT 
    dl.object_name AS `table`,
    dl.lock_type,
    dl.index_name,
    dl.lock_mode,
    CASE 
        WHEN dl.lock_mode LIKE '%REC_NOT_GAP%' THEN 'Record Lock'
        WHEN dl.lock_mode LIKE '%GAP%' AND dl.lock_mode NOT LIKE '%REC_NOT_GAP%' THEN 'Gap Lock'
        WHEN dl.lock_mode NOT LIKE '%GAP%' AND dl.lock_mode NOT LIKE '%REC_NOT_GAP%' THEN 'Next-Key Lock'
        ELSE dl.lock_mode
    END AS lock_type_detail,
    dl.lock_data AS locked_data
FROM performance_schema.data_locks dl
ORDER BY dl.object_name, dl.lock_data;
        갭 락으로 인한 INSERT 실패
+----+------+------+
| id | a    | b    |
+----+------+------+
|  0 |    0 |    0 |
|  5 |    5 |    5 |
| 10 |   10 |   10 |
| 15 |   15 |   15 |
| 20 |   20 |   20 |
| 25 |   25 |   25 |
+----+------+------+
-- <A 세션> 존재하지 않는 id = 7를 업데이트 
BEGIN;
UPDATE t SET b=b+1 WHERE id=7;
+-------+-----------+------------+-----------+------------------+-------------+
| table | lock_type | index_name | lock_mode | lock_type_detail | locked_data |
+-------+-----------+------------+-----------+------------------+-------------+
| t     | TABLE     | NULL       | IX        | Next-Key Lock    | NULL        |
| t     | RECORD    | PRIMARY    | X,GAP     | Gap Lock         | 10          |
+-------+-----------+------------+-----------+------------------+-------------+
-- <B 세션>
INSERT INTO t VALUES (8, 8, 8);     -- BLOCKED
INSERT INTO t VALUES (9, 9, 9);     -- BLCOKED
INSERT INTO t VALUES (4, 4, 4);     -- 성공
INSERT INTO t VALUES (11, 11, 11);  -- 성공
UPDATE t SET b=b+1 WHERE id=5;      -- 성공
UPDATE t SET b=b+1 WHERE id=10;     -- 성공
        먼저 넥스트 키 락에 의해 (5, 10]으로 잠금되고, 마지막 레코드 10이 id=7 조건에 만족하지 않기 때문에 넥스트 키 락은 갭 락으로 강등되어 (5, 10)의 잠금이 발생한다.
그리하여 마지막 업데이트 쿼리는 둘 다 성공한다.
non-unique 세컨더리 인덱스 동등 잠금 (FOR SHARE)
+----+------+------+
| id | a    | b    |
+----+------+------+
|  0 |    0 |    0 |
|  5 |    5 |    5 |
| 10 |   10 |   10 |
| 15 |   15 |   15 |
| 20 |   20 |   20 |
| 25 |   25 |   25 |
+----+------+------+
-- <A 세션>
BEGIN;
select id from t where a = 5 for share;
+-------+-----------+------------+-----------+------------------+-------------+
| table | lock_type | index_name | lock_mode | lock_type_detail | locked_data |
+-------+-----------+------------+-----------+------------------+-------------+
| t     | TABLE     | NULL       | IS        | Next-Key Lock    | NULL        |
| t     | RECORD    | ix_a       | S,GAP     | Gap Lock         | 10, 10      | <- (5, 10)
| t     | RECORD    | ix_a       | S         | Next-Key Lock    | 5, 5        | <- (0, 5]
+-------+-----------+------------+-----------+------------------+-------------+
-- <B 세션>
INSERT INTO t VALUES (-1, -1, -1);  -- 성공
INSERT INTO t VALUES (0,0,0);   -- BLOCKED
INSERT INTO t VALUES (3,3,3);   -- BLOCKED
INSERT INTO t VALUES (7,7,7);   -- BLOCKED
UPDATE t SET b=b+1 WHERE id=5;  -- 성공
UPDATE t SET b=b+1 WHERE id=10; -- 성공
+----+------+------+
| id | a    | b    |
+----+------+------+
| -1 |   -1 |   -1 |
|  0 |    0 |    0 |
|  5 |    5 |    6 |
| 10 |   10 |   11 |
+----+------+------+
UPDATE t SET a=a+1 WHERE id=10; -- 성공
UPDATE t SET a=a+1 WHERE id=5;  -- BLOCKED
+----+------+------+
| id | a    | b    |
+----+------+------+
| -1 |   -1 |   -1 |
|  0 |    0 |    0 |
|  5 |    5 |    6 |
| 10 |   11 |   11 |
+----+------+------+
        - Q: 왜 5미만의 INSERT 쿼리는 실행하지 못할까?
- 원칙 1에 따라 기본 잠금 단위는 넥스트 키 락이기 때문에 
(0,5]에 넥스트 키 락이 설정된다. 
 - 원칙 1에 따라 기본 잠금 단위는 넥스트 키 락이기 때문에 
 - Q: 왜 
id = (5 < N < 10)의 INSERT 쿼리는 실행하지 못할까?- a 컬럼 인덱스는 유니크하지 않은 세컨더리 인덱스이기 때문에 
a = 5에 해당하는 레코드만 찾아서 바로 종료하는 것이 아니라 오른쪽으로 계속 스캔하여 레코드 10을 찾을 때까지 이동하기에(5,10]에 넥스트 키 락이 설정된다. 이 과정에서 접근한 모든 객체에 잠금을 걸게 되기 때문이다 
 - a 컬럼 인덱스는 유니크하지 않은 세컨더리 인덱스이기 때문에 
 - Q: 왜 
id=10에 대한 a,b 컬럼 UPDATE는 성공할까?- 10은 
a = 5조건을 만족하지 않기 때문에 넥스트 키 락이 갭 락으로 강등되어(5,10)으로 잠금 범위가 변경되기 때문이다. 
 - 10은 
 - Q: FOR SHARE로 
a = 5에 대한 명시적 공유 잠금을 걸었는데 b 컬럼에 대한 업데이트는 왜 실행될까?- A 세션의 FOR SHARE 쿼리는 커버링 인덱스를 활용하고 있고 조회 가능 잠금이기 때문에 프라이머리 키 인덱스에 어떠한 잠금도 설정되지 않는다.
 - 잠금에 대한 정보를 통해 
ix_a 인덱스의 (5,10) 구간에 걸린 Gap Lock으로 인해 a 컬럼은 UPDATE가 실행되지 않았고, b 컬럼에 대한 UPDATE는 실행 가능한 것이다. - 만약 
a = 5에 대한 조회 가능 잠금을 통해 레코드가 업데이트 되는 것을 원하지 않으면 FOR SHARE 쿼리를 수정하면 된다. 
 
select * from t where a = 5 for share; -- 또는
select id, a, b from t where a = 5 for share;
+-------+-----------+------------+---------------+------------------+-------------+
| table | lock_type | index_name | lock_mode     | lock_type_detail | locked_data |
+-------+-----------+------------+---------------+------------------+-------------+
| t     | TABLE     | NULL       | IS            | Next-Key Lock    | NULL        |
| t     | RECORD    | ix_a       | S,GAP         | Gap Lock         | 10, 10      |
| t     | RECORD    | PRIMARY    | S,REC_NOT_GAP | Record Lock      | 5           | -> 레코드 락을 확인할 수 있다!
| t     | RECORD    | ix_a       | S             | Next-Key Lock    | 5, 5        |
+-------+-----------+------------+---------------+------------------+-------------+
        non-unique 세컨더리 인덱스 동등 장금 (FOR UPDATE)
+----+------+------+
| id | a    | b    |
+----+------+------+
|  0 |    0 |    0 |
|  5 |    5 |    5 |
| 10 |   10 |   10 |
| 15 |   15 |   15 |
| 20 |   20 |   20 |
| 25 |   25 |   25 |
+----+------+------+
-- <A 세션>
BEGIN;
select id from t where a = 5 for update;
+-------+-----------+------------+---------------+------------------+-------------+
| table | lock_type | index_name | lock_mode     | lock_type_detail | locked_data |
+-------+-----------+------------+---------------+------------------+-------------+
| t     | TABLE     | NULL       | IX            | Next-Key Lock    | NULL        |
| t     | RECORD    | ix_a       | X,GAP         | Gap Lock         | 10, 10      |
| t     | RECORD    | PRIMARY    | X,REC_NOT_GAP | Record Lock      | 5           |
| t     | RECORD    | ix_a       | X             | Next-Key Lock    | 5, 5        |
+-------+-----------+------------+---------------+------------------+-------------+
-- <B 세션>
INSERT INTO t VALUES (7,7,7);   -- BLOCKED
UPDATE t SET b=b+1 WHERE id=5;  -- BLOCKED
UPDATE t SET b=b+1 WHERE id=10; -- 성공
UPDATE t SET a=a+1 WHERE id=5;   -- BLOCKED
UPDATE t SET a=a+1 WHERE id=10;  -- 성공
        프라이머리 키 인덱스 범위 잠금
조회 결과는 같지만 잠금 범위가 서로 다른 케이스를 확인해보자.
+----+------+------+
| id | a    | b    |
+----+------+------+
|  0 |    0 |    0 |
|  5 |    5 |    5 |
| 10 |   10 |   10 |
| 15 |   15 |   15 |
+----+------+------+
-- <A 세션>
BEGIN;
SELECT * FROM t WHERE id=10 FOR UPDATE;
+-------+-----------+------------+---------------+------------------+-------------+
| table | lock_type | index_name | lock_mode     | lock_type_detail | locked_data |
+-------+-----------+------------+---------------+------------------+-------------+
| t     | TABLE     | NULL       | IX            | Next-Key Lock    | NULL        |
| t     | RECORD    | PRIMARY    | X,REC_NOT_GAP | Record Lock      | 10          |
+-------+-----------+------------+---------------+------------------+-------------+
-- <B 세션>
UPDATE t SET b=b+1 WHERE id=5;  -- 성공
INSERT INTO t VALUES (7,7,7);   -- 성공
INSERT INTO t VALUES (11,11,11);    -- 성공
        처음엔 (5, 10] 넥스트 키 락이 설정되고, id는 프라이머리 키 인덱스이고 id = 10 조건에 해당하는 레코드가 존재하므로 레코드 락으로 강등된다.
-- <A 세션>
BEGIN;
SELECT * FROM t WHERE id >= 10 AND id < 11 FOR UPDATE;
+-------+-----------+------------+---------------+------------------+-------------+
| table | lock_type | index_name | lock_mode     | lock_type_detail | locked_data |
+-------+-----------+------------+---------------+------------------+-------------+
| t     | TABLE     | NULL       | IX            | Next-Key Lock    | NULL        |
| t     | RECORD    | PRIMARY    | X,REC_NOT_GAP | Record Lock      | 10          |
| t     | RECORD    | PRIMARY    | X,GAP         | Gap Lock         | 15          |
+-------+-----------+------------+---------------+------------------+-------------+
-- <B 세션>
INSERT INTO t VALUES (8, 8, 8);     -- 성공
INSERT INTO t VALUES (9, 9, 9);     -- 성공
INSERT INTO t VALUES (11, 11, 11);  -- BLOCKED
UPDATE t SET b=b+1 WHERE id = 15;   -- 성공
UPDATE t SET a=a+1 WHERE id = 15;   -- 성공
UPDATE t SET a=a+1 WHERE id = 10;   -- BLOCKED
        id >= 10 AND id < 11 조건에 해당하는 레코드를 찾게 되는데, 먼저 id >= 10에 대한 (5, 10] 넥스트 키 락이 설정된다. 하지만 id = 10인 레코드가 존재하므로 레코드 락으로 강등된다.
id < 11 같은 범위 검색은 조건을 만족하는지 안하는지 다음 첫 번째 레코드까지 접근해야만 알 수 있기 때문에 오른쪽으로 범위 검색을 계속해서 수행하며 id=15 레코드를 찾게된다.
이미 id=10레코드를 찾았으므로 (10, 15)에 갭락이 설정된다.
즉, 최종 잠금 범위는 10, (10, 15)가 된다.
non-unique 세컨더리 인덱스 범위 잠금
+----+------+------+
| id | a    | b    |
+----+------+------+
|  0 |    0 |    0 |
|  5 |    5 |    5 |
| 10 |   10 |   10 |
| 15 |   15 |   15 |
+----+------+------+
-- <A 세션>
BEGIN;
SELECT * FROM t WHERE a >= 10 AND a < 11 FOR UPDATE;
+-------+-----------+------------+---------------+------------------+-------------+
| table | lock_type | index_name | lock_mode     | lock_type_detail | locked_data |
+-------+-----------+------------+---------------+------------------+-------------+
| t     | TABLE     | NULL       | IX            | Next-Key Lock    | NULL        |
| t     | RECORD    | PRIMARY    | X,REC_NOT_GAP | Record Lock      | 10          |
| t     | RECORD    | ix_a       | X             | Next-Key Lock    | 10, 10      |
| t     | RECORD    | ix_a       | X             | Next-Key Lock    | 15, 15      |
+-------+-----------+------------+---------------+------------------+-------------+
-- <B 세션>
INSERT INTO t VALUES (6, 6, 6);     -- BLOCKED
INSERT INTO t VALUES (8, 8, 8);     -- BLOCKED
INSERT INTO t VALUES (4, 4, 4);     -- 성공
UPDATE t SET b=b+1 WHERE a = 15;    -- BLOCKED
UPDATE t SET b=b+1 WHERE a = 10;    -- BLOCKED
UPDATE t SET b=b+1 WHERE a = 5;     -- 성공
UPDATE t SET b=b+1 WHERE a = 4;     -- 성공
        먼저 a=10인 레코드를 찾아 (5, 10] 넥스트 키 락이 설정된다. ix_a 인덱스는 유니크 인덱스가 아니기 때문에 레코드 락으로 끝나지 않는다. (규칙 2를 적용할 수 없다.)
그렇기에 동등 조건을 만족하지 않는 다음 레코드까지 범위 검색을 수행하며, a=15에 해당하는 레코드를 찾아 검색이 중단되고 (10, 15]에 넥스트 키 락이 설정된다.
동등 조건이 아닌 범위 검색에 해당하기 때문에 갭 락으로 강등되지 않는다.
즉, 최종 잠금 범위는 (5, 10], (10, 15]이다.
non-unique 세컨더리 인덱스 중복된 행 잠금
+----+------+------+
| id | a    | b    |
+----+------+------+
|  0 |    0 |    0 |
|  5 |    5 |    5 |
| 10 |   10 |   10 |
| 15 |   15 |   15 |
| 20 |   20 |   20 |
| 25 |   25 |   25 |
| 30 |   10 |   30 |
+----+------+------+
-- <A 세션>
BEGIN;
SELECT * FROM t WHERE a=10 FOR UPDATE;
+-------+-----------+------------+---------------+------------------+-------------+
| table | lock_type | index_name | lock_mode     | lock_type_detail | locked_data |
+-------+-----------+------------+---------------+------------------+-------------+
| t     | TABLE     | NULL       | IX            | Next-Key Lock    | NULL        |
| t     | RECORD    | PRIMARY    | X,REC_NOT_GAP | Record Lock      | 10          |
| t     | RECORD    | PRIMARY    | X,REC_NOT_GAP | Record Lock      | 30          |
| t     | RECORD    | ix_a       | X             | Next-Key Lock    | 10, 10      |
| t     | RECORD    | ix_a       | X             | Next-Key Lock    | 10, 30      |
| t     | RECORD    | ix_a       | X,GAP         | Gap Lock         | 15, 15      |
+-------+-----------+------------+---------------+------------------+-------------+
-- <B 세션>
INSERT INTO t VALUES (4, 4, 4);      -- 성공
INSERT INTO t VALUES (6, 6, 6);      -- BLOCKED
INSERT INTO t VALUES (9, 9, 9);      -- BLOCKED
INSERT INTO t VALUES (12, 12, 12);   -- BLOCKED
INSERT INTO t VALUES (17, 17, 17);   -- 성공
INSERT INTO t VALUES (24, 24, 24);   -- 성공
INSERT INTO t VALUES (27, 27, 27);   -- 성공
UPDATE t SET b=b+1 WHERE a = 5;     -- 성공
UPDATE t SET b=b+1 WHERE a = 10;    -- BLOCKED
UPDATE t SET b=b+1 WHERE a = 15;    -- 성공
UPDATE t SET b=b+1 WHERE a = 20;    -- 성공
UPDATE t SET b=b+1 WHERE a = 25;    -- 성공
UPDATE t SET a=a+1 WHERE id = 0;
UPDATE t SET a=a+1 WHERE id = 5;     -- BLOCKED
UPDATE t SET a=a+1 WHERE id = 10;    -- BLOCKED
UPDATE t SET a=a+1 WHERE id = 15;
UPDATE t SET a=a+1 WHERE id = 20;
UPDATE t SET a=a+1 WHERE id = 25;
UPDATE t SET a=a+1 WHERE id = 30;    -- BLOCKED
UPDATE t SET b=b+1 WHERE id = 0;
UPDATE t SET b=b+1 WHERE id = 5;
UPDATE t SET b=b+1 WHERE id = 10;    -- BLOCKED
UPDATE t SET b=b+1 WHERE id = 15;
UPDATE t SET b=b+1 WHERE id = 20;
UPDATE t SET b=b+1 WHERE id = 25;
UPDATE t SET b=b+1 WHERE id = 30;    -- BLOCKED
        먼저 a=10 조건에 해당하는 레코드를 찾아 (5, 10] 넥스트 키 락을 설정한다.
정확히는 (a=5, id=5)부터 (a=10, id=10)까지 넥스트 키 락이 설정된다.
그리고 넌유니크 세컨더리 인덱스이기 때문에 다음 레코드 a=15를 찾을 때 까지 오른쪽으로 검색을 수행하며, 이 과정에서 a=10, id=30 레코드를 거치기 때문에 (a=10, id=10) ~ (a=10, id=30)까지 넥스트 키 락이 설정된다.
계속 오른쪽으로 검색하며 a=15, id=15 레코드를 만나면 검색 루프는 종료되고 (a=10, id=30) ~ (a=15, id=15) 까지 넥스트 키 락이 설정된다.
하지만 검색 조건 a=10 조건에 해당되지 않으므로 최종적으로 갭 락으로 강등된다.
즉 최종 잠금 범위는
-- ix_a, X (Next-Key Lock)
( (a=5, id=5), (a=10, id=10) ],
-- ix_a, X (Next-Key Lock)
( (a=10, id=10), (a=10, id=30) ], 
-- ix_a, X,GAP (Gap Lock)
( (a=10, id=30), (a=15, id=15) )
        non-unique 세컨더리 인덱스 중복된 행 잠금 (추가 케이스)
CREATE TABLE employees (
    id int NOT NULL AUTO_INCREMENT,
    first_name varchar(255) DEFAULT NULL,
    last_name varchar(255) DEFAULT NULL,
    PRIMARY KEY (id),
    KEY idx_first_name (first_name)
) ENGINE=InnoDB
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 34 | E          | E1        |
| 35 | E          | E2        |
| 36 | E          | E3        |
| 37 | B          | B1        |
| 38 | B          | B2        |
+----+------------+-----------+
        -- <A 세션>
BEGIN;
update employees SET last_name = 'Updated E' where first_name = 'E' and last_name = 'E2';
+-----------+-----------+----------------+---------------+------------------+------------------------+
| table     | lock_type | index_name     | lock_mode     | lock_type_detail | locked_data            |
+-----------+-----------+----------------+---------------+------------------+------------------------+
| employees | TABLE     | NULL           | IX            | Next-Key Lock    | NULL                   |
| employees | RECORD    | idx_first_name | X             | Next-Key Lock    | 'E', 34                |
| employees | RECORD    | idx_first_name | X             | Next-Key Lock    | 'E', 35                |
| employees | RECORD    | idx_first_name | X             | Next-Key Lock    | 'E', 36                |
| employees | RECORD    | PRIMARY        | X,REC_NOT_GAP | Record Lock      | 34                     |
| employees | RECORD    | PRIMARY        | X,REC_NOT_GAP | Record Lock      | 35                     |
| employees | RECORD    | PRIMARY        | X,REC_NOT_GAP | Record Lock      | 36                     |
| employees | RECORD    | idx_first_name | X             | Next-Key Lock    | supremum pseudo-record |
+-----------+-----------+----------------+---------------+------------------+------------------------+
-- <B 세션>
insert into employees (first_name, last_name) values ('A', 'A1');    -- 성공
insert into employees (first_name, last_name) values ('B', 'B1');    -- BLOCKED
insert into employees (first_name, last_name) values ('C', 'C1');    -- BLOCKED
insert into employees (first_name, last_name) values ('D', 'D1');    -- BLOCKED
insert into employees (first_name, last_name) values ('F', 'F1');    -- BLOCKED
insert into employees (first_name, last_name) values ('Z', 'Z1');    -- BLOCKED
update employees set last_name = 'updated B1' where id = 37;   -- 성공
update employees set last_name = 'updated B2' where id = 38;   -- 성공
update employees set last_name = 'updated B2' where first_name = 'B';    -- 성공
update employees set first_name = 'C' where id = 37;   -- BLOCKED
update employees set first_name = 'A' where id = 37;   -- 성공
        먼저 first_name = 'E'를 찾기 위해 오른쪽으로 검색하지만 E보다 큰 데이터는 없기에 supremum 락이 걸린다.
('B', 37)
('B', 38) ← 마지막 B 레코드
    ↓
  [갭1] ← ('B', 38) ~ ('E', 34)
    ↓
('E', 34) ← Next-Key Lock = 레코드 + 갭1
('E', 35) ← Next-Key Lock = 레코드 + 갭2
('E', 36) ← Next-Key Lock = 레코드 + 갭3
    ↓
  [갭4] ← ('E', 36) ~ supremum
    ↓
supremum  ← Next-Key Lock = supremum + 갭4
        실제 락 범위
('E', 34)Next-Key Lock- 레코드: 
('E', 34) - 갭: 
(('B', 38) ~ ('E', 34)) 
- 레코드: 
 ('E', 35)Next-Key Lock- 레코드: 
('E', 35) - 갭: 
(('E', 34) ~ ('E', 35)) 
- 레코드: 
 ('E', 36)Next-Key Lock- 레코드: 
('E', 36) - 갭: 
(('E', 35) ~ ('E', 36)) 
- 레코드: 
 - supremum Next-Key Lock
- supremum 자체
 - 갭: 
(('E', 36) ~ supremum) 
 
non-unique 세컨더리 인덱스 LIMIT 잠금
+----+------+------+
| id | a    | b    |
+----+------+------+
|  0 |    0 |    0 |
|  5 |    5 |    5 |
| 10 |   10 |   10 |
| 15 |   15 |   15 |
| 20 |   20 |   20 |
| 25 |   25 |   25 |
| 30 |   10 |   30 |
| 35 |   10 |   35 |
+----+------+------+
-- <A 세션>
BEGIN;
select * from t where a = 10 limit 2 FOR UPDATE;
+-------+-----------+------------+---------------+------------------+-------------+
| table | lock_type | index_name | lock_mode     | lock_type_detail | locked_data |
+-------+-----------+------------+---------------+------------------+-------------+
| t     | TABLE     | NULL       | IX            | Next-Key Lock    | NULL        |
| t     | RECORD    | PRIMARY    | X,REC_NOT_GAP | Record Lock      | 10          |
| t     | RECORD    | ix_a       | X             | Next-Key Lock    | 10, 10      |
| t     | RECORD    | ix_a       | X             | Next-Key Lock    | 10, 30      |
| t     | RECORD    | PRIMARY    | X,REC_NOT_GAP | Record Lock      | 30          |
+-------+-----------+------------+---------------+------------------+-------------+
-- <B 세션>
insert into t values (9, 9, 9);       -- BLOCKED
insert into t values (13, 13, 13);    -- 성공
insert into t values (14, 9, 14);     -- BLOCKED
insert into t values (16, 16, 16);    -- 성공
UPDATE t SET b=b+1 WHERE id = 15;     -- 성공
        - 처음으로 좌측 개구간 
(a=5, id=5)부터 (a=10, id=10)까지 넥스트 키 락이 설정된다. - 우측으로 탐색하며 
(a=10, id=30)레코드에 접근학데 되므로(a=10, id=10)부터 (a=10, id=30)까지 넥스트 키 락이 설정된다. - LIMIT 2에 대한 조건을 만족했기 때문에 더 이상 검색을 진행하지 않고 종료한다.