did_story
[MySQL] InnoDB์์ PHANTOM READ ๋ฅผ ๋ฐฉ์งํ๋ ๋ฐฉ๋ฒ. ๋ณธ๋ฌธ
[MySQL] InnoDB์์ PHANTOM READ ๋ฅผ ๋ฐฉ์งํ๋ ๋ฐฉ๋ฒ.
์ด์ ์์ 2025. 7. 16. 19:53MySQL์ REPEATBLE READ ๊ฒฉ๋ฆฌ ์์ค์์๋ PHANTOM READ ํ์์ด ๋ฐ์ํ์ง ์๋๋ค๊ณ ๋ง์ด ๋ณธ๊ฑฐ ๊ฐ์๋ฐ ๊ณผ์ฐ ์ง์ง์ผ๊น? ๊ทธ๋ฐ๋ฐ PHANTOM READ๊ฐ ๋ฌด์์ด๊ธธ๋ ์ค์ํ๊ฒ ๋ค๋ฃจ๋ ๊ฒ์ผ๊น?
REPEATABLE READ ์ด๋?
REPEATABLE READ ๋ ๋์ผ ํธ๋์ ์ ๋ด์์ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฅํ๋๋ก ํ๋ ๊ฒฉ๋ฆฌ ์์ค์ ๋ปํฉ๋๋ค. MySql์ InnoDB ์คํ ๋ฆฌ์ง ์์ง์์ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉ๋๋ ๊ฒฉ๋ฆฌ ์์ค์ธ๋ฐ, ์ด๋ป๊ฒ ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฅํด์ค ์ ์๋ ๊ฑธ๊น์? ์ด๋ MVCC(Multi Version Concurrency Control, ๋ค์ค ๋ฒ์ ๋์์ฑ ์ ์ด)์ ๋ฉ์ปค๋์ฆ์ ์ด์ฉํด ์ด์ ๋ฒ์ ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐฑ์ ํด๋๊ณ ์ค์ ๋ ์ฝ๋ ๊ฐ์ ๋ณ๊ฒฝํ๋ ๋ฐฉ๋ฒ์ผ๋ก ๋ณด์ฅํ ์ ์์ต๋๋ค.
MVCC: ๊ธฐ์กด์ ๋ฐ์ดํฐ๋ฅผ ๋ฎ์ด์์ฐ๋ ๊ฒ์ด ์๋ ๊ธฐ์กด์ ๋ฐ์ดํฐ๋ฅผ ๋ฐํ์ผ๋ก ์ด์ ๋ฒ์ ์ ๋ฐ์ดํฐ์ ๋น๊ตํ์ฌ ๋ณ๊ฒฝ๋ ๋ด์ฉ์ ๊ธฐ๋กํ๋ ๋ฐฉ๋ฒ์ผ๋ก, ์ฌ๋ฌ ๋ฒ์ ์ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๊ฒ ๋๊ฑฐ๋ ์ฌ์ฉ์๋ ๋ง์ง๋ง ๋ฒ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฒ ๋ฉ๋๋ค.
์์ ์ ํจ๊ป InnoDB์ PHANTOM READ ๋ฌธ์

- Transaction 1
1. ์ฒซ๋ฒ์งธ ์กฐํ:
→ ๊ฒฐ๊ณผ: 2๊ฐ์ ํ ๋ฐํSELECT * FROM Employees WHERE Gender = 'Male';
2. ๋ค์ ๋์ผํ ์ฟผ๋ฆฌ ์คํ:
3. ์ค๊ฐ ์์ ์ํ (Transaction 1์ ์์ง ๋๋์ง ์์์)SELECT * FROM Employees WHERE Gender = 'Male';
→ ๊ฒฐ๊ณผ: 3๊ฐ์ ํ์ ๋ฐํํ๋ค. - Transaction 2
1.์ค๊ฐ์ INSERT ์ฃผ์
2. ๊ทธ๋ฆฌ๊ณ ์ปค๋ฐ!INSERT INTO Employees (Name, Gender, Age) VALUES ('DOHYUN', 'Male', 20);
⇒ ๊ฒฐ๊ณผ
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/
'BackEnd๐ > DB & SQL' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [Database] ๋์์ฑ ์ ์ด(Concurrency Control) - Locking (6) | 2025.07.15 |
|---|---|
| [Database] ๋์์ฑ ์ ์ด(Concurrency Control) - MVCC (1) | 2025.07.14 |
| [MySQL / DDL] ์ ์ฝ์กฐ๊ฑด(Constraints)์ ๊ดํ์ฌ ! (4) | 2025.01.17 |
| [MySQL / DDL] CREAET ๋ถํฐ MODIFY ๊น์ง! (์ ์ฝ ์กฐ๊ฑด x) (8) | 2025.01.13 |
| [MySQL / DML] SELECT, ๋ฐ์ดํฐ ์กฐํ ๋ฐฉ๋ฒ (6) | 2025.01.07 |
