Transaction Isolation Level


์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœ ์ค‘ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋„์ž…ํ•˜๋ฉด ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์— ๋Œ€ํ•œ ์ดํ•ด๊ฐ€ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์š”๊ตฌ๋ฉ๋‹ˆ๋‹ค. ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ACID ์„ฑ์งˆ ์ค‘ Isolation ์— ํ•ด๋‹นํ•˜๋ฉฐ, ์—ฌ๋Ÿฌ ํŠธ๋žœ์žญ์…˜์ด ๋™์‹œ์— ๋ณ€๊ฒฝ์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ์„ฑ๋Šฅ๊ณผ ์•ˆ์ •์„ฑ, ์ผ๊ด€์„ฑ ๋ฐ ์žฌํ˜„์„ฑ ๊ฐ„์˜ ๊ทœํ˜•์„ ๋ฏธ์„ธํ•˜๊ฒŒ ์กฐ์ •ํ•˜๋Š” ์„ค์ •์ž…๋‹ˆ๋‹ค. SQL:1992 ํ‘œ์ค€์—์„œ ์ œ๊ณตํ•˜๋Š” 4๊ฐ€์ง€ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์˜ ์ข…๋ฅ˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE

MySQL ์˜ InnoDB ๋Š” MVCC ์™€ 2-Phase Locking ์˜ ์žฅ์ ์„ ์ ์ ˆํžˆ ์กฐํ•ฉํ•˜์—ฌ ํŠธ๋žœ์žญ์…˜ ๋ชจ๋ธ์„ ๊ตฌํ˜„ํ•˜์˜€๋Š”๋ฐ์š”. ์ด๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„  InnoDB ์˜ Locking, Consistent Non-Locking Read, Locking Read ๋“ฑ๊ณผ ๊ฐ™์€ ๊ฐœ๋…์ด ์„ ํ–‰๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

CREATE TABLE test (
  id BIGINT(20) NOT NULL AUTO_INCREMENT,
  name VARCHAR(255) NOT NULL,
  age BIGINT(20) NOT NULL,
  PRIMARY KEY (id)
);
 
insert into test (id, name, age) values (1, 'quaritch', 25);
insert into test (id, name, age) values (2, 'orca', 26);
insert into test (id, name, age) values (3, 'chan', 26);
insert into test (id, name, age) values (6, 'ori', 27);
insert into test (id, name, age) values (10, 'awesomeo', 26);

์ด ๊ธ€์—์„  ์—ฌ๋Ÿฌ ์˜ˆ์‹œ๋“ค์ด ์‚ฌ์šฉ๋  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ์˜ˆ์‹œ๋Š” ์œ„ ํ…Œ์ด๋ธ”์„ ๋ฐ”ํƒ•์œผ๋กœ ์ง„ํ–‰๋˜๋Š” ์ ์„ ์ฐธ๊ณ ํ•˜๋ฉฐ ์ฝ์œผ์‹œ๋ฉด ์ข€ ๋” ์›ํ™œํ•œ ์ดํ•ด๊ฐ€ ๊ฐ€๋Šฅํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

InnoDB Locking


MySQL ์˜ InnoDB ์—์„  ๋‹ค์–‘ํ•œ Locking ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์ค‘ InnoDB ์˜ ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๋Œ€ํ‘œ์ ์ธ 3๊ฐ€์ง€ Lock ์— ๋Œ€ํ•ด ์šฐ์„  ์‚ดํŽด๋ณด๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

Record Lock

Record Lock ์€ ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ์— ์„ค์ •๋˜๋Š” Lock ์ž…๋‹ˆ๋‹ค. ํ…Œ์ด๋ธ”์— ์ธ๋ฑ์Šค๊ฐ€ ์ •์˜๋˜์ง€ ์•Š์•˜์–ด๋„ InnoDB ์—์„  ์ˆจ๊ฒจ์ง„ Clustered Index ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ Record Lock ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, SELECT name FROM test WHERE id = 1 FOR UPDATE; ๋ผ๋Š” ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด id ๊ฐ€ 1 ์— ํ•ด๋‹นํ•˜๋Š” ๋ ˆ์ฝ”๋“œ์— Record Lock ์ด ์„ค์ •๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด id ๊ฐ€ 1 ์ธ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์•„๋ž˜๋Š” ํ•ด๋‹น ์ƒํ™ฉ์„ ์—ฐ์ถœํ•˜๊ณ  SHOW ENGINE INNODB STATUS ๋ฅผ ํ†ตํ•ด Record Lock ์ด ์„ค์ •๋œ ๊ฒƒ์„ ํ™•์ธํ•œ ๋กœ๊ทธ์ž…๋‹ˆ๋‹ค.

RECORD LOCKS space id 10 page no 4 n bits 72 index PRIMARY of table `transaction_test`.`test`
trx id 3162 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 8; hex 8000000000000001; asc         ;;
 1: len 6; hex 000000000c4b; asc      K;;
 2: len 7; hex 820000008e0110; asc        ;;
 3: len 8; hex 7175617269746368; asc quaritch;;
 4: len 8; hex 8000000000000019; asc         ;;

Gap Lock

Gap Lock ์€ ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ ๊ฐ„์˜ Gap ํ˜น์€ ์ฒซ ๋ฒˆ์งธ ๋˜๋Š” ๋งˆ์ง€๋ง‰ ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ ์ „/ํ›„์˜ Gap ์— ์„ค์ •๋˜๋Š” Lock ์ž…๋‹ˆ๋‹ค. Gap ์ž์ฒด์— Lock ์ด ์„ค์ •๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฝ์ž…ํ•˜๋ ค๋Š” ๊ณณ์˜ ๊ฐ’ ์กด์žฌ ์—ฌ๋ถ€์™€ ๊ด€๊ณ„ ์—†์ด ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, SELECT name FROM test WHERE id >= 1 FOR UPDATE; ๋ผ๋Š” ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด id ๊ฐ€ 8 ์ธ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•˜๋ ค๊ณ  ํ•  ๊ฒฝ์šฐ ํ•ด๋‹น id ๊ฐ’์€ Gap Lock ์ด ์„ค์ •๋œ ์ƒํƒœ์ด๊ธฐ ๋•Œ๋ฌธ์— ์‚ฝ์ž… ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์•„๋ž˜๋Š” ํ•ด๋‹น ์ƒํ™ฉ์„ ์—ฐ์ถœํ•˜๊ณ  Gap Lock ์ด ์„ค์ •๋œ ๊ฒƒ์„ ํ™•์ธํ•œ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค.

RECORD LOCKS space id 11 page no 4 n bits 72 index PRIMARY of table `transaction_test`.`test`
trx id 3194 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 5 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 8; hex 800000000000000a; asc         ;;
 1: len 6; hex 000000000c74; asc      t;;
 2: len 7; hex 82000000ab0110; asc        ;;
 3: len 4; hex 6f726361; asc orca;;
 4: len 8; hex 800000000000001a; asc         ;;

Next-Key Lock

Next-Key Lock ์€ Record Lock ๊ณผ ํ•ด๋‹น ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ ์•ž์˜ Gap ์— ๋Œ€ํ•œ Gap Lock ์ด ์กฐํ•ฉ๋œ Lock ์ž…๋‹ˆ๋‹ค. ์˜ˆ์‹œ์— ํ•ด๋‹นํ•˜๋Š” Next-Key Lock ์€ ์•„๋ž˜ ๋ช…์‹œ๋œ ๊ฒƒ๊ณผ ๊ฐ™์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

(negative infinity, 1]
(1, 2]
(2, 3]
(3, 6]
(6, 10]
(10, positive infinity)

๋งˆ์ง€๋ง‰ ๋ฒ”์œ„์— ๋Œ€ํ•œ Next-Key Lock ์€ ์‹ค์ œ ์ธ๋ฑ์Šค์˜ ๊ฐ€์žฅ ํฐ ๊ฐ’๊ณผ ๊ฐ€์žฅ ๋†’์€ ๊ฐ’์„ ๊ฐ–๋Š” ํ—ˆ์œ„ ์ธ๋ฑ์Šค, supremum pseudo-record ๋ฅผ ํ†ตํ•ด Lock ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ๋กœ SELECT โ€ฆ FROM performance_schema.data_locks; ๋ฅผ ํ†ตํ•ด ํ™•์ธํ•ด๋ณด๋ฉด supremum pseudo-record ๊ฐ€ LOCK_DATA ๋กœ ์žกํ˜€์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Consistent Non-Locking Reads


MySQL ์˜ InnoDB ๋Š” ๋™์‹œ์„ฑ ์„ฑ๋Šฅ์„ ์ตœ๋Œ€ํ™”ํ•˜๊ธฐ ์œ„ํ•ด Multiversion Concurrency Control, MVCC ๋ผ๋Š” ๊ฐœ๋…์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์— ๋”ฐ๋ผ ์ƒ์ดํ•˜์ง€๋งŒ, ํŠน์ • ์‹œ์ ์˜ Snapshot ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ Locking ์ด ํ•„์š”ํ•˜์ง€ ์•Š์€ Consistent Read ๋ฅผ ์ œ๊ณตํ•œ๋‹ค๋Š” ๊ณตํ†ต์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ MySQL ์—์„  Consistent Non-Locking Read ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

  • REPEATALBE READ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„  ์ตœ์ดˆ SELECT ๋ฌธ์ด ์ˆ˜ํ–‰๋œ ์‹œ์ ์„ ๊ธฐ์ค€์œผ๋กœ Snapshot ์ด ์ƒ์„ฑ๋˜์–ด ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์— ์˜ํ•ด ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์–ด๋„ Undo Log ์— ์ €์žฅ๋œ ๋‚ด์šฉ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • READ COMMITTED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„  ๊ฐ SELECT ๋ฌธ ๋งˆ๋‹ค Snapshot ์ด ์ดˆ๊ธฐํ™”๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์˜ ์ปค๋ฐ‹์— ์˜ํ•ด ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ๊ฐ™์€ ์ฝ๊ธฐ ์ž‘์—…์ด๋ผ๋„ ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Locking Reads


๊ธฐ๋ณธ์ ์ธ SELECT ๋ฌธ์„ ํ†ตํ•ด ์กฐํšŒํ•œ ๋ฐ์ดํ„ฐ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Non-Locking Read ์ž…๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์— ์˜ํ•ด ๋ณ€๊ฒฝ๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค. InnoDB ๋Š” ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‘ ๊ฐ€์ง€ Locking Read ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  • SELECT โ€ฆ FOR SHARE ๋Š” ์ฝ์€ Row ์— S-Lock ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด Row ๋ฅผ ์ฝ์„ ์ˆœ ์žˆ์ง€๋งŒ, S-Lock ์„ ์„ค์ •ํ•œ ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹๋˜๊ธฐ ์ „๊นŒ์ง€ Row ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์•„์ง ์ปค๋ฐ‹๋˜์ง€ ์•Š์€ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์— ์˜ํ•ด ํ•ด๋‹น Row ๊ฐ€ ๋ณ€๊ฒฝ๋  ๊ฒฝ์šฐ, ํŠธ๋žœ์žญ์…˜์ด ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฐ ๋’ค ๊ฐ€์žฅ ์ตœ์‹ ํ™”๋œ ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • SELECT โ€ฆ FOR UPDATE ๋Š” ๊ฒ€์ƒ‰์—์„œ ๋ฐœ๊ฒฌํ•œ ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ์— ๋Œ€ํ•ด UPDATE ๋ฌธ์„ ์‹คํ–‰ํ•œ ๊ฒƒ๊ณผ ๋™์ผํ•˜๊ฒŒ Row ๋ฐ ๊ด€๋ จ ์ธ๋ฑ์Šค์— Lock ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์€ ํ•ด๋‹น Row ๋ฅผ UPDATE ๋˜๋Š” SELECT ... FOR SHARE ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ œํ•œ๋ฉ๋‹ˆ๋‹ค.

FOR SHARE ํ˜น์€ FOR UPDATE ๋กœ ์„ค์ •๋œ ๋ชจ๋“  Lock ์€ ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹ ๋˜๋Š” ๋กค๋ฐฑ๋  ๋•Œ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€ InnoDB ์˜ Locking, Consistent Non-Locking Read, Locking Read ๋“ฑ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ด์ œ InnoDB ๊ฐ€ 4๊ฐ€์ง€ ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ ์ด๋ฅผ ํ†ตํ•ด ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

READ UNCOMMITTED


๋จผ์ € READ UNCOMMITTED ์ž…๋‹ˆ๋‹ค. ํŠธ๋žœ์žญ์…˜์˜ ๋ณ€๊ฒฝ ๋‚ด์šฉ์ด ์ปค๋ฐ‹์ด๋‚˜ ๋กค๋ฐฑ ์—ฌ๋ถ€์™€ ๊ด€๊ณ„ ์—†์ด ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์— ๋ณด์ด๋Š” ๊ฐ€์žฅ ๋‚ฎ์€ ์ˆ˜์ค€์˜ ๊ฒฉ๋ฆฌ ๋ ˆ๋ฒจ์ž…๋‹ˆ๋‹ค. READ UNCOMMITTED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ ์ผ๋ฐ˜์ ์ธ SELECT ๋ฌธ์€ Non-Locking Read ๋กœ ์ˆ˜ํ–‰๋˜์ง€๋งŒ MVCC ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„ Consistent Read ๋ฅผ ๋ณด์žฅํ•˜์ง€์•Š์Šต๋‹ˆ๋‹ค.

Dirty Read

READ UNCOMMITTED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ๊ธฐ ๋ถ€์ •ํ•ฉ ํ˜„์ƒ์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์— ์˜ํ•ด ์˜ํ–ฅ ๋ฐ›์€ Row ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•˜๋Š” ํ˜„์ƒ์ž…๋‹ˆ๋‹ค. ์ปค๋ฐ‹ ๋˜๋Š” ๋กค๋ฐฑ์— ๊ด€๊ณ„ ์—†์ด ๋ณด์—ฌ์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋ถ€์ •ํ™•ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์œ„ํ—˜ํ•œ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ACID ์„ฑ์งˆ์„ ์ค€์ˆ˜ํ•˜์ง€ ๋ชปํ•˜๊ธฐ์— ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด InnoDB ์—์„  MVCC ๋ฅผ ํ†ตํ•œ Consistent Read ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

READ COMMITTED


READ COMMITTED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€๋ถ€ํ„ฐ MVCC ๊ฐ€ ์‚ฌ์šฉ๋˜์–ด Consistent Read ๋ฅผ ์ง€์›ํ•˜์ง€๋งŒ ๋™์ผํ•œ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ๊ฐ SELECT ๋ฌธ์€ ์ž์ฒด์ ์œผ๋กœ ์ƒˆ๋กœ์šด Snapshot ์„ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์™„์ „ํ•œ Consistent Read ๋ฅผ ๋ณด์žฅํ•˜์ง„ ์•Š์Šต๋‹ˆ๋‹ค.

Locking Read UPDATE DELETE ๋ฌธ์˜ ๊ฒฝ์šฐ Record Lock ๋งŒ์„ ์„ค์ •ํ•˜๊ณ  Gap Lock ์€ ์„ค์ •ํ•˜์ง€ ์•Š๊ธฐ์— ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ ์ž์œ ๋กญ๊ฒŒ Gap ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Gap Lock ์„ ์„ค์ •ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ฒ”์œ„ ๊ฒ€์ƒ‰๊ณผ ๊ฐ™์€ ์ฟผ๋ฆฌ์—์„œ Phantom Read ํ˜„์ƒ ์—ญ์‹œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
UPDATE test SET name = 'meatsby' WHERE name = 'quaritch';

์ถ”๊ฐ€์ ์œผ๋กœ ์œ„ ๊ทธ๋Ÿผ์ฒ˜๋Ÿผ UPDATE ์ฟผ๋ฆฌ๋ฅผ ์œ„ํ•œ ํ’€ ํ…Œ์ด๋ธ” ์Šค์บ” ์‹œ READ COMMITTED ๋Š” ์Šค์บ” ์ค‘ ๋งˆ์ฃผ์น˜๋Š” ๋ชจ๋“  ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ์— X-Lock ์„ ์„ค์ •ํ•˜๊ณ  WHERE ์ ˆ์ด ์ˆ˜ํ–‰๋  ๋•Œ ์กฐ๊ฑด์— ๋ถ€ํ•ฉํ•˜์ง€ ์•Š๋Š” ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ์˜ X-Lock ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณ€๊ฒฝํ•  ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ๋ฅผ ์ œ์™ธํ•œ ๋‹ค๋ฅธ ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ์˜ Lock ์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜๊ณผ์˜ ์ถฉ๋Œ๋กœ ์ธํ•œ ๋ฐ๋“œ๋ฝ ๋ฐœ์ƒ ํ™•๋ฅ ์„ ํ˜„์ €ํžˆ ์ค„์˜€๋‹ค๊ณ  MySQL ์€ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
UPDATE test SET name = 'meatsby' WHERE name = 'quaritch';

๋ฐ˜๋ฉด REPEATABLE READ ์—์„  ์Šค์บ” ์ค‘ ๋งˆ์ฃผ์น˜๋Š” ๋ชจ๋“  ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ์— X-Lock ์„ ์„ค์ •ํ•˜์—ฌ ์ปค๋ฐ‹ ๋˜๋Š” ๋กค๋ฐฑ๊นŒ์ง€ Lock ์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Non-Repeatable Read

READ COMMITTED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€๊นŒ์ง€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ๊ธฐ ๋ถ€์ •ํ•ฉ ํ˜„์ƒ์ž…๋‹ˆ๋‹ค. ์•ž์„œ ์„ค๋ช…ํ•œ ๊ฒƒ ์ฒ˜๋Ÿผ READ COMMITTED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ SELECT ๋ฌธ์€ ํ•ญ์ƒ Snapshot ์„ ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•œ ํŠธ๋žœ์žญ์…˜์—์„œ ๋™์ผํ•œ SELECT ๋ฌธ์„ ์ˆ˜ํ–‰ํ•ด๋„ ์ผ๊ด€๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

REPEATABLE READ


InnoDB ์—์„œ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๊ฒฉ๋ฆฌ ๋ ˆ๋ฒจ๋กœ, READ COMMITTED ๊ฒฉ๋ฆฌ ์ˆ˜์ค€๊ณผ๋Š” ๋‹ฌ๋ฆฌ ์ฒ˜์Œ ์ƒ์„ฑ๋œ Snapshot ์„ ํ†ตํ•ด Consistent Read ๋ฅผ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋™์ผํ•œ ํŠธ๋žœ์žญ์…˜ ๋‚ด์˜ ์ผ๋ฐ˜์ ์ธ SELECT ๋ฌธ์˜ ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

Locking Read UPDATE DELETE ๋ฌธ์˜ ๊ฒฝ์šฐ ๊ฒ€์ƒ‰ ์กฐ๊ฑด์— ๋”ฐ๋ผ ์‚ฌ์šฉํ•˜๋Š” Lock ์ด ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

  • ๊ณ ์œ ํ•œ ๊ฒ€์ƒ‰ ์กฐ๊ฑด์ด ์žˆ๋Š” ๊ณ ์œ  ์ธ๋ฑ์Šค์— ๋Œ€ํ•œ ์ฟผ๋ฆฌ๋Š” Record Lock ์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ๋ฒ”์œ„ ๊ฒ€์ƒ‰ ์กฐ๊ฑด๊ณผ ๊ฐ™์€ ๋‹ค๋ฅธ ๊ฒ€์ƒ‰ ์กฐ๊ฑด์˜ ๊ฒฝ์šฐ ์Šค์บ”ํ•œ ์ธ๋ฑ์Šค ๋ฒ”์œ„์— Gap Lock ๋˜๋Š” Next-Key Lock ์„ ์„ค์ •ํ•˜์—ฌ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ํ•ด๋‹น ๋ฒ”์œ„์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์„ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜ ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ์œ„ ๋‘ ์ƒํ™ฉ์„ ์ข€ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

# Session A
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT * FROM test WHERE id = 6 FOR UPDATE;

ํ˜„์žฌ ์˜ˆ์‹œ์—์„  id ๊ฐ€ ์ธ๋ฑ์Šค๋กœ ์ž‘์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ณ ์œ ํ•œ id ์กฐ๊ฑด์„ ํ†ตํ•ด FOR UPDATE ๋ฅผ ์ˆ˜ํ–‰ํ•  ๊ฒฝ์šฐ ํ•ด๋‹นํ•˜๋Š” ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ์—๋งŒ Record Lock ์ด ์„ค์ •๋ฉ๋‹ˆ๋‹ค.

# Session B
INSERT INTO test (id, name, age) VALUES (5, 'test', 26);

๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด Gap ์— ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•  ๋•Œ ์•„๋ฌด ์ œํ•œ ์—†์ด ์ •์ƒ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ์‚ฝ์ž…๋ฉ๋‹ˆ๋‹ค.

# Session A
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
SELECT * FROM test WHERE age = 25 FOR UPDATE;

๋ฐ˜๋ฉด age ์™€ ๊ฐ™์ด ํ˜„์žฌ ํ…Œ์ด๋ธ” ์ƒํƒœ์—์„  ๊ณ ์œ ํ•˜์ง€๋งŒ ์ธ๋ฑ์Šค๊ฐ€ ์•„๋‹Œ ์กฐ๊ฑด์„ ํ†ตํ•ด FOR UPDATE ๋ฅผ ์ˆ˜ํ–‰ํ•  ๊ฒฝ์šฐ ํ’€ ํ…Œ์ด๋ธ” ์Šค์บ”์„ ํ†ตํ•ด ํ•ด๋‹น ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ๋ฅผ ํƒ์ƒ‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ ๊ณผ์ •์—์„œ ๋งˆ์ฃผ์นœ ๋ชจ๋“  ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ์— X-Lock ์ด ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

# Session B
INSERT INTO test (id, name, age) VALUES (5, 'test', 26);

์ด ์ƒํƒœ์—์„œ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด Gap ์— id ๊ฐ€ 5 ์ธ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•˜๋ ค๊ณ  ํ•  ๊ฒฝ์šฐ id ๊ฐ€ 6 ์ธ ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ์˜ Gap Lock ์œผ๋กœ ์ธํ•ด ๋ฐ์ดํ„ฐ ์‚ฝ์ž…์ด ์ œํ•œ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด InnoDB ์—์„  REPEATABLE READ ์ˆ˜์ค€์—์„œ๋„ Phantom Read ํ˜„์ƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Phantom Read

A : START TRANSACTION;
A : SELECT * FROM test; # ์ตœ์ดˆ Snapshot ์ƒ์„ฑ
 
B : INSERT INTO test (id, name, age) VALUES (5, 'test', 26); # Lock ์ด ๊ฑธ๋ ค์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— Gap ์— ๋ฐ์ดํ„ฐ ์‚ฝ์ž… ๊ฐ€๋Šฅ
 
A : SELECT * FROM test; # ์ตœ์ดˆ Snapshot ์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉ
A : UPDATE test SET name = 'twenty-six' WHERE age = 26; # Locking Read ๋ฅผ ํ†ตํ•ด ์ƒˆ๋กญ๊ฒŒ ์ฝ์€ ํ›„ UPDATE
A : SELECT * FROM test; # Snapshot ์ด ์ดˆ๊ธฐํ™”๋˜์–ด Phantom Read ๋ฐœ์ƒ

์—ฌ์ „ํžˆ ์ด๋Ÿฐ ๊ตฌ์กฐ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  REPEATABLE READ ์—์„œ Phantom Read ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„ ์ƒํ™ฉ์ฒ˜๋Ÿผ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ์ถ”๊ฐ€ํ•œ ๋ ˆ์ฝ”๋“œ์— UPDATE ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ฒŒ ๋  ๊ฒฝ์šฐ, ์ฒซ SELECT ์ฟผ๋ฆฌ๋กœ ์ƒ์„ฑ๋œ Snapshot ์—๋Š” ์กด์žฌํ•˜์ง€ ์•Š์ง€๋งŒ ์‹ค์ œ ๋””์Šคํฌ์—๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ๋ ˆ์ฝ”๋“œ๊ฐ€ ์˜ํ–ฅ์„ ๋ฐ›์•„ ์ดํ›„ SELECT ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•  ๋•Œ Snapshot ์ด ์ดˆ๊ธฐํ™”๋˜๋Š” ์‹œ์ ์— ํ•ด๋‹น ํ˜„์ƒ์„ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, Consistent Non-Locking Read ์™€ Locking Read ์˜ ์กฐํ•ฉ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ํ˜„์ƒ์ž…๋‹ˆ๋‹ค.

์•ž์„œ ์„ค๋ช…ํ•œ ๊ฒƒ ์ฒ˜๋Ÿผ ์ผ๋ฐ˜์ ์ธ SELECT ๋ฌธ์€ Non-Locking Read ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— UPDATE ๋ฅผ ์œ„ํ•œ SELECT ๋Š” SELECT โ€ฆ FOR UPDATE ์™€ ๊ฐ™์ด Locking Read ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ, ์œ„ ์˜ˆ์‹œ์—์„œ ์—ฐ์ถœํ•˜๋Š” ์ƒํ™ฉ ์ž์ฒด๊ฐ€ ๋ถ€์ ์ ˆํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ, ์—ฌ์ „ํžˆ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ํŠน์ • ์ƒํ™ฉ์ด ์กด์žฌํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ๊ณ ์žˆ๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

SERIALIZABLE


๋งˆ์ง€๋ง‰ SERIALIZABLE ์€ ๊ฐ€์žฅ ๋†’์€ ์ˆ˜์ค€์˜ ๊ฒฉ๋ฆฌ ๋ ˆ๋ฒจ์ž…๋‹ˆ๋‹ค. ์ด๋ฆ„์ฒ˜๋Ÿผ ์ง๋ ฌํ™”๋œ ๋ฐฉ์‹์ฒ˜๋Ÿผ ์ž‘๋™ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. InnoDB ์—์„  ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์ผ๋ฐ˜์ ์ธ SELECT ๋ฌธ์„ ๋ชจ๋‘ SELECT โ€ฆ FOR SHARE ๋ฌธ์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ S-Lock ์„ ํ†ตํ•œ Locking Read ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ฒŒ๋” ํ•ฉ๋‹ˆ๋‹ค.

๊ธ€ ์ดˆ๋ฐ˜๋ถ€ ์„ค๋ช…๋˜์–ด์žˆ๋“ฏ์ด S-Lock ์€ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜๋„ ๋…๋ฆฝ์ ์œผ๋กœ ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜์—ฌ S-Lock ์ด ์„ค์ •๋œ ๋ ˆ์ฝ”๋“œ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ์ตœ์ดˆ๋กœ S-Lock ์„ ์„ค์ •ํ•œ ํŠธ๋žœ์žญ์…˜์ด ์ข…๋ฃŒ๋˜๊ธฐ ์ „๊นŒ์ง„ ๋ณ€๊ฒฝ์„ ๊ฐ€ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
SELECT * FROM test;

์œ„ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ๋‹จ์ˆœํžˆ SELECT ๋ฌธ์„ ์ˆ˜ํ–‰ํ•ด๋„ ๋ชจ๋“  ๋ ˆ์ฝ”๋“œ์— S-Lock ์ด ์„ค์ •๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งˆ๋ฌด๋ฆฌ


์—ฌ๊ธฐ๊นŒ์ง€ MySQL ์˜ InnoDB ๊ฐ€ 4๊ฐ€์ง€ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ ๊ตฌํ˜„ํ•œ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์€ ๋™์‹œ์„ฑ๊ณผ ๊ฒฉ๋ฆฌ์„ฑ์˜ Trade-Off ์ธ ๋งŒํผ ์ž์„ธํžˆ ์•Œ๊ณ  ์‚ฌ์šฉํ•˜๋ฉด ๋” ์ข‹์€ ํšจ๊ณผ๋ฅผ ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

MySQL ์€ ๋‹ค๋ฅธ DBMS ๋ฒค๋”๋“ค๊ณผ๋Š” ๋‹ฌ๋ฆฌ InnoDB ๋ฅผ ํ†ตํ•ด REPEATABLE READ ์—์„œ๋„ ์ถฉ๋ถ„ํ•œ ๊ฒฉ๋ฆฌ์„ฑ์„ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ๋Ÿฌ๊ฐ€์ง€ Locking ๊ธฐ๋ฒ•์„ ํ™œ์šฉํ•˜์—ฌ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ ๊ตฌํ˜„ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— Locking ์œผ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์—ฌ๋Ÿฌ ๋ฌธ์ œ์  ์—ญ์‹œ ์ถฉ๋ถ„ํžˆ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

References