BackEnd๐Ÿƒ/DB & SQL

[MySQL] InnoDB์—์„œ PHANTOM READ ๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•.

์–ด์ œ์‹œ์ž‘ 2025. 7. 16. 19:53

MySQL์˜ REPEATBLE READ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์—์„œ๋Š” PHANTOM READ ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ๋งŽ์ด ๋ณธ๊ฑฐ ๊ฐ™์€๋ฐ ๊ณผ์—ฐ ์ง„์งœ์ผ๊นŒ? ๊ทธ๋Ÿฐ๋ฐ PHANTOM READ๊ฐ€ ๋ฌด์—‡์ด๊ธธ๋ž˜ ์ค‘์š”ํ•˜๊ฒŒ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ผ๊นŒ?

REPEATABLE READ ์ด๋ž€?

REPEATABLE READ ๋Š” ๋™์ผ ํŠธ๋žœ์ ์…˜ ๋‚ด์—์„œ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์žฅํ•˜๋„๋ก ํ•˜๋Š” ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์„ ๋œปํ•ฉ๋‹ˆ๋‹ค. MySql์˜ InnoDB ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„์—์„œ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์ธ๋ฐ, ์–ด๋–ป๊ฒŒ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์žฅํ•ด์ค„ ์ˆ˜ ์žˆ๋Š” ๊ฑธ๊นŒ์š”? ์ด๋Š” MVCC(Multi Version Concurrency Control, ๋‹ค์ค‘ ๋ฒ„์ „ ๋™์‹œ์„ฑ ์ œ์–ด)์˜ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ด์šฉํ•ด ์ด์ „ ๋ฒ„์ „์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฑ์—…ํ•ด๋‘๊ณ  ์‹ค์ œ ๋ ˆ์ฝ”๋“œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

MVCC: ๊ธฐ์กด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฎ์–ด์”Œ์šฐ๋Š” ๊ฒƒ์ด ์•„๋‹Œ ๊ธฐ์กด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ด์ „ ๋ฒ„์ „์˜ ๋ฐ์ดํ„ฐ์™€ ๋น„๊ตํ•˜์—ฌ ๋ณ€๊ฒฝ๋œ ๋‚ด์šฉ์„ ๊ธฐ๋กํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ, ์—ฌ๋Ÿฌ ๋ฒ„์ „์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์กด์žฌํ•˜๊ฒŒ ๋˜๊ฑฐ๋‚˜ ์‚ฌ์šฉ์ž๋Š” ๋งˆ์ง€๋ง‰ ๋ฒ„์ „์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

 

์˜ˆ์ œ์™€ ํ•จ๊ป˜ InnoDB์˜ PHANTOM READ ๋ฌธ์ œ

https://dotnettutorials.net/lesson/phantom-read-concurrency-problem-sql-server/

  • Transaction 1
    1. ์ฒซ๋ฒˆ์งธ ์กฐํšŒ:
    SELECT * FROM Employees WHERE Gender = 'Male';
    
    → ๊ฒฐ๊ณผ: 2๊ฐœ์˜ ํ–‰ ๋ฐ˜ํ™˜
    2. ๋‹ค์‹œ ๋™์ผํ•œ ์ฟผ๋ฆฌ ์‹คํ–‰:
    SELECT * FROM Employees WHERE Gender = 'Male';
    
    3. ์ค‘๊ฐ„ ์ž‘์—… ์ˆ˜ํ–‰ (Transaction 1์€ ์•„์ง ๋๋‚˜์ง€ ์•Š์•˜์Œ)
    → ๊ฒฐ๊ณผ: 3๊ฐœ์˜ ํ–‰์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • Transaction 2
    1.์ค‘๊ฐ„์— INSERT ์ฃผ์ž…
    INSERT INTO Employees (Name, Gender, Age)
    VALUES ('DOHYUN', 'Male', 20);
    
    2. ๊ทธ๋ฆฌ๊ณ  ์ปค๋ฐ‹!

⇒ ๊ฒฐ๊ณผ

Transaction 1์€ ๋™์ผํ•œ ์ฟผ๋ฆฌ๋ฅผ 2๋ฒˆ ์‹คํ–‰ํ•˜์˜€์„ ๋•Œ, ์ฒซ ๋ฒˆ์จฐ ์ฟผ๋ฆฌ์—์„œ๋Š” 2๋ช…์˜ ์ง์›์„ ๋ฐ˜ํ™˜ ํ•˜์˜€์ง€๋งŒ ๋‘ ๋ฒˆ์งธ ์ฟผ๋ฆฌ์—์„œ 3๋ช…์˜ ์ง์›์„ ๋ฐ”๋กœ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํ˜„์ƒ์ด ๋‚˜ํƒ€๋‚˜๋Š” ๊ฒƒ์„ PHANTOM READ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

PHANTOM READ๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•œ ์ด์œ !

๊ทธ๋Ÿฌ๋ฉด ์ด๋Ÿฐ PHANTOM READ๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์™œ ๊ทธ๋ ‡๊ฒŒ ์ค‘์š”ํ•˜๋‹ค๊ณ  ํ•˜๋Š” ๊ฒƒ์ผ๊นŒ์š”? PHANTOM READ๊ฐ€ ๋‹จ์ˆœํ•œ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์˜ ๋ณ€ํ™”์ฒ˜๋Ÿผ ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” ๋™์‹œ์„ฑ ๋ฒ„๊ทธ๋ฅผ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ํŒฌํ…€ ๋ฆฌ๋“œ๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•œ ์ด์œ ๋ฅผ ์•Œ์•„๋ด…์‹œ๋‹ค.

1. ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ๊ณผ ์ •ํ•ฉ์„ฑ(Data Integrity)

๋ฌธ์ œ: ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ๋ ˆ์ฝ”๋“œ ์ˆ˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ค‘์š”ํ•œ ํŒ๋‹จ์„ ๋‚ด๋ฆฌ๋Š” ๊ฒฝ์šฐ, PHANTOM READ๋กœ ์ธํ•ด ๊ฒฐ๊ณผ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ž˜๋ชป๋œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์‹คํ–‰๋  ์ˆ˜ ์žˆ์Œ

 

์˜ˆ์‹œ: "๋‚จ์„ฑ ์ง์›์ด 2๋ช… ์ดํ•˜์ผ ๋•Œ๋งŒ ์‹ ๊ทœ ์ฑ„์šฉ ํ—ˆ์šฉ"์ด๋ผ๋Š” ์กฐ๊ฑด์ด ์žˆ๋Š” ๊ฒฝ์šฐ, ํŒฌํ…€ ๋ฆฌ๋“œ๋กœ ์ธํ•ด ์‹ค์ œ๋กœ๋Š” 3๋ช…์ธ๋ฐ๋„ ๋ถˆ๊ตฌํ•˜๊ณ  2๋ช…์ด๋ผ๊ณ  ํŒ๋‹จํ•ด ์ค‘๋ณต ์ฑ„์šฉ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ.


2. ํŠธ๋žœ์žญ์…˜ ์ผ๊ด€์„ฑ(Consistency)์˜ ๋ถ•๊ดด

๋ฌธ์ œ: ํŠธ๋žœ์žญ์…˜์˜ ๊ฐ€์žฅ ํ•ต์‹ฌ์ ์ธ ๋ชฉ์  ์ค‘ ํ•˜๋‚˜๋Š” ๋™์ผํ•œ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ๋Š” ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณธ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ PHANTOM READ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, ๊ฐ™์€ ์ฟผ๋ฆฌ๋ฅผ ๋‘ ๋ฒˆ ํ–ˆ๋Š”๋ฐ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๊ธฐ ๋•Œ๋ฌธ์— ํŠธ๋žœ์žญ์…˜์˜ ์ผ๊ด€์„ฑ์ด ๊นจ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค(๋…ธ์šฐ! )

 

⇒ ๊ฒฐ๊ณผ: ์ด๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ์ •์ฑ… ์œ„๋ฐ˜, ๋…ผ๋ฆฌ์  ์˜ค๋ฅ˜, ๋˜๋Š” ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒฐ๊ณผ๋กœ ์ด์–ด์งˆ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Šต๋‹ˆ๋‹ค.


3. ๋‹ค์ค‘ ํŠธ๋žœ์žญ์…˜ ํ™˜๊ฒฝ์—์„œ ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•œ ์ƒํƒœ ๋ฐœ์ƒ

๋ฌธ์ œ: PHANTOM READ๋Š” ์—ฌ๋Ÿฌ ํŠธ๋žœ์žญ์…˜์ด ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ํ™˜๊ฒฝ์—์„œ๋งŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ PHANTOM READ๋ฅผ ํ—ˆ์šฉํ•˜๋ฉด, ํŠธ๋žœ์žญ์…˜ ๊ฐ„ ๊ฐ„์„ญ์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚˜๋ฉฐ ํ…Œ์ŠคํŠธ๋กœ ๋ฐœ๊ฒฌํ•˜๊ธฐ ์–ด๋ ต๊ณ  ์žฌํ˜„์ด ํž˜๋“  ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

⇒ ๊ฒฐ๊ณผ: ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋งŒ ๋ฐœ์ƒํ•˜๋Š” ๊ฐ„ํ—์  ์˜ค๋ฅ˜(bug), ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜ ๋“ฑ์œผ๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค.

 

์ฆ‰! ํŒฌํ…€ ๋ฆฌ๋“œ๋ฅผ ๋ฐฉ์ง€ํ•˜์ง€ ์•Š์œผ๋ฉด, ํŠธ๋žœ์žญ์…˜ ๋‚ด ๋…ผ๋ฆฌ ์˜ค๋ฅ˜์™€ ๋น„์ฆˆ๋‹ˆ์Šค ์ •์ฑ… ์œ„๋ฐ˜์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํŠนํžˆ ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ ์‹œ์Šคํ…œ์—์„œ๋Š” ์‹ฌ๊ฐํ•œ ์šด์˜ ์žฅ์• ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


PHANTOM READ ๋ฐฉ์ง€ ๋ฐฉ๋ฒ• 1 - GAP LOCK(๊ฐญ๋ฝ)

GAP LOCK(๊ฐญ๋ฝ)์€ ํŠน์ • ์ธ๋ฑ์Šค ๊ฐ’ ์‚ฌ์ด์˜ ๊ณต๊ฐ„์„ ์ž ๊ทธ๋Š” ๋ฝ์ž…๋‹ˆ๋‹ค. ๊ธฐ์กด ๋ ˆ์ฝ”๋“œ ๊ฐ„์˜ ๊ฐ„๊ฒฉ์„ ๋ณดํ˜ธํ•˜์—ฌ ์ƒˆ๋กœ์šด ๋ ˆ์ฝ”๋“œ์˜ ์‚ฝ์ž…์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค. ๊ฐญ ๋ฝ์€ ๋ฒ”์œ„ ๋‚ด์— ํŠน์ • ๋ ˆ์ฝ”๋“œ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์„ ๋•Œ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ํŠธ๋žœ์žญ์…˜์ด ํŠน์ • ๋ฒ”์œ„ ๋‚ด์—์„œ ๋ฐ์ดํ„ฐ์˜ ์‚ฝ์ž…์„ ๋ง‰์•„ ํŒฌํ…€ ์ฝ๊ธฐ(Phantom Read) ํ˜„์ƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

  • ์˜ˆ์ œ
-- Transaction 1 (REPEATABLE READ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€)
START TRANSACTION;

SELECT * FROM Employees WHERE Age BETWEEN 20 AND 30 FOR UPDATE;
-- → ์ด ์ฟผ๋ฆฌ๋Š” (20, 30) ๋ฒ”์œ„์— Gap Lock์„ ๊ฑด๋‹ค

-- Transaction 2 (๋™์‹œ์— ์‹คํ–‰)
START TRANSACTION;

INSERT INTO Employees (Name, Gender, Age) VALUES ('DOHYUN', 'Male', 25);
-- Gap Lock ๋•Œ๋ฌธ์— ์ด ์‚ฝ์ž…์€ ๋Œ€๊ธฐ(block) ๋˜๋Š” ์‹คํŒจ

-- Transaction 1์ด COMMIT ํ•ด์•ผ Transaction 2๊ฐ€ ์ง„ํ–‰ ๊ฐ€๋Šฅ

⇒ ๊ฒฐ๊ณผ

1. ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์€ id=25 ๊ฐ™์€ ๊ฐ’์„ INSERTํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

2. ๊ธฐ์กด row์—๋Š” ๋ฝ์ด ๊ฑธ๋ฆฌ์ง€ ์•Š์ง€๋งŒ, ์ค‘๊ฐ„ gap์—๋Š” ๋ฝ์ด ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค.


PHANTOM READ ๋ฐฉ์ง€ ๋ฐฉ๋ฒ• 2 - Next-Key Lock(๋„ฅ์ŠคํŠธํ‚ค ๋ฝ).

์ŠคํŠธํ‚ค ๋ฝ์€ ๋ ˆ์ฝ”๋“œ ๋ฝ๊ณผ ๊ฐญ๋ฝ์„ ๊ฒฐํ•ฉํ•œ ํ˜•ํƒœ๋กœ, ํŠน์ • ์ธ๋ฑ์Šค ๋ ˆ์ฝ”๋“œ์™€ ๊ทธ ์ฃผ๋ณ€์˜ ๊ฐญ์„ ๋™์‹œ์— ์ž ๊ทธ๋Š” ๋ฝ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ ˆ์ฝ”๋“œ ์ž์ฒด์˜ ๋ณ€๊ฒฝ๊ณผ ํ•จ๊ป˜ ๊ทธ ์ฃผ๋ณ€ ๊ณต๊ฐ„์˜ ๋ณ€๊ฒฝ๋„ ๋™์‹œ์— ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋„ฅ์ŠคํŠธํ‚ค ๋ฝ์€ ํŠน์ • ๋ ˆ์ฝ”๋“œ์™€ ๊ทธ ์ฃผ๋ณ€ ๊ณต๊ฐ„์„ ์ž ๊ทธ๊ธฐ ๋•Œ๋ฌธ์—, ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ์ƒˆ๋กœ์šด ๋ ˆ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ ํŒฌํ…€ ๋ฆฌ๋“œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

  • ์˜ˆ์ œ
-- ํ˜„์žฌ Employees ํ…Œ์ด๋ธ” ์ƒํƒœ:
-- id: 10, 20, 30 ์ด ์กด์žฌ

-- Transaction 1
START TRANSACTION;

SELECT * FROM Employees WHERE id > 10 FOR UPDATE;
-- ์ด ์ฟผ๋ฆฌ๋Š” ๋‹ค์Œ ์˜์—ญ์— Next-Key Lock์„ ๊ฑด๋‹ค:
-- (10,20], (20,30], (30,∞)
-- ์ฆ‰, id=15๋‚˜ id=25 ๊ฐ™์€ ์‚ฝ์ž…์€ ๋ชจ๋‘ ์ฐจ๋‹จ๋จ

-- Transaction 2
START TRANSACTION;

INSERT INTO Employees (id, Name, Gender, Age) VALUES (25, 'DOHYUN', 'Male', 20);
-- ์œ„ next-key ๋ฝ์— ์˜ํ•ด ์‚ฝ์ž… ๋ถˆ๊ฐ€ ๋˜๋Š” ๋Œ€๊ธฐ

⇒ ๊ฒฐ๊ณผ

1. id > 10 ๋ฒ”์œ„์— ํฌํ•จ๋˜๋Š” ๋ชจ๋“  ๊ตฌ๊ฐ„๊ณผ ๋ ˆ์ฝ”๋“œ์— ๋ฝ์ด ๊ฑธ๋ฆฌ๊ณ 

2. ๊ธฐ์กด ๋ ˆ์ฝ”๋“œ + ์‚ฌ์ด gap ๋ชจ๋‘ ๋ณดํ˜ธ → ๋งค์šฐ ๊ฐ•๋ ฅํ•œ ํŒฌํ…€ ๋ฆฌ๋“œ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

๋งˆ๋ฌด๋ฆฌ

์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•๋“ค์„ ์•Œ์•„๋ณด๋ฉฐ InnoDB์—์„œ REPEATABLE READ ์ƒํƒœ์ผ ๋–„, PHANTOM READ๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‚˜์€ ๋งค์ปค๋‹ˆ์ฆ˜์„ ๋งŒ๋“œ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

 

์ธ์šฉ:
https://dotnettutorials.net/lesson/phantom-read-concurrency-problem-sql-server/
https://parkmuhyeun.github.io/woowacourse/2023-11-28-Repeatable-Read/