did_story
[Database] ๋์์ฑ ์ ์ด(Concurrency Control) - MVCC ๋ณธ๋ฌธ
[Database] ๋์์ฑ ์ ์ด(Concurrency Control) - MVCC
์ด์ ์์ 2025. 7. 14. 14:56์ฌ๋ฌ ํธ๋์ญ์ ์ด ๋์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ์ฌ ์์ ์ ์ํํ๋ ค๊ณ ํ ๋, ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ์ ์ ์งํ๊ณ ์ค๋ฅ๋ฅผ ๋ฐฉ์งํ๋ ๋ฐฉ๋ฒ (๋์์ ์์ฉํ๋ ๋ค์ค ํธ๋์ญ์ ์ ์ํธ๊ฐ์ญ ์์ฉ์์ Database๋ฅผ ๋ณดํธํ๋ ๊ฒ) ์ ๋์์ฑ ์ ์ด(Concurrency Control) ๋ผ๊ณ ํฉ๋๋ค. ๊ทธ๋ฌ๋ฉด ์ด๋ฌํ ๋ฐฉ๋ฒ์ ๋ํ ์ข ๋ฅ๋ ์ด๋ค ๊ฒ์ด ์์๊น?! ์ง๊ธ๋ถํฐ ๊ทธ๊ฒ์ ์์๋ณด๋ฌ ๋ ๋๋ด ์๋ค.
์ฃผ๋ก ์ฌ์ฉ๋๋ ๋ฐฉ๋ฒ์ ๋ค์ค ๋ฒ์ ๋์์ฑ ์ ์ด (MVCC, Multi-Version Concurrency Control)๊ณผ ์ ๊ธ ๊ธฐ๋ฐ ๋์์ฑ ์ ์ด (Lock-based Concurrency Control)์ด ์์ต๋๋ค. ๊ทธ๋ฌ๋ฉด ์ด์ ํ๋ํ๋ ์์ธํ๊ฒ ์์๋ณผ๊น์??
1. ๋์์ฑ ์ ์ด์ ๋ชฉ์
์ ์: ์ฌ๋ฌ ํธ๋์ญ์ ์ด ๋์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํด๋ ๋ฐ์ดํฐ์ ์ผ๊ด์ฑ๊ณผ ๋ฌด๊ฒฐ์ฑ์ ๋ณด์ฅํ๋ฉด์, ์ฑ๋ฅ๊ณผ ํจ์จ์ฑ์ ์ ์งํ๋ ๊ฒ์ด ๋ชฉ์ ์ ๋๋ค.
- ๋ถ์ค๋ ๊ฐฑ์ (Lost Update)
- ๋ฌธ์ ์ํฉ: ๋ ํธ๋์ญ์
์ด ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ์์ ํ ํ ์ ์ฅํ๋ฉด, ๋จผ์ ์ ์ฅ๋ ๊ฒฐ๊ณผ๊ฐ ๋์ค ์ ์ฅ์ผ๋ก ๋ฎ์ฌ์ง.
- T1: A = 100 ์ฝ์ → A = A+10 → ์ ์ฅ
- T2: A = 100 ์ฝ์ → A = A+20 → ์ ์ฅ
→ ๊ฒฐ๊ณผ: A=120 (T1์ 10์ ์ฌ๋ผ์ง)
- ๋ฌธ์ ์ํฉ: ๋ ํธ๋์ญ์
์ด ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ์์ ํ ํ ์ ์ฅํ๋ฉด, ๋จผ์ ์ ์ฅ๋ ๊ฒฐ๊ณผ๊ฐ ๋์ค ์ ์ฅ์ผ๋ก ๋ฎ์ฌ์ง.
- ๋ชจ์์ฑ(Inconsistency)
- ๋ฌธ์ ์ํฉ: ๋ ํธ๋์ญ์
์ด ๋์์ ์คํ๋์ด ๋ฐ์ดํฐ๊ฐ ๋ถ์ผ์นํ๊ฑฐ๋ ์ค๋ฅ ์ํ์ ๋น ์ง๋ ๊ฒฝ์ฐ.
- T1์ ์๊ณ ๋ฅผ ์ฐจ๊ฐํ๊ณ , T2๋ ๋์์ ์ฐจ๊ฐํ์ฌ ์ต์ข ์๊ณ ๊ฐ ์๋ชป๋จ
- ๋ฌธ์ ์ํฉ: ๋ ํธ๋์ญ์
์ด ๋์์ ์คํ๋์ด ๋ฐ์ดํฐ๊ฐ ๋ถ์ผ์นํ๊ฑฐ๋ ์ค๋ฅ ์ํ์ ๋น ์ง๋ ๊ฒฝ์ฐ.
- ์ฐ์๋ณต๊ท(Cascading Rollback)
- ๋ฌธ์ ์ํฉ: ํ ํธ๋์ญ์
์ด ์คํจํ์ฌ ๋กค๋ฐฑ๋๋ฉด, ๊ทธ ํธ๋์ญ์
๊ฒฐ๊ณผ๋ฅผ ์ฐธ์กฐํ ๋ค๋ฅธ ํธ๋์ญ์
๋ ์ฐ์์ ์ผ๋ก ๋กค๋ฐฑ๋๋ ์ํฉ.
- T1์ด A๊ฐ์ ๋ณ๊ฒฝ (์ปค๋ฐ ์๋จ)
- T2๊ฐ A๋ฅผ ์ฐธ์กฐ → T1 ์คํจ๋ก ๋กค๋ฐฑ
- T2๋ ์ํฅ์ ๋ฐ์ ๋กค๋ฐฑ
- ๋ฌธ์ ์ํฉ: ํ ํธ๋์ญ์
์ด ์คํจํ์ฌ ๋กค๋ฐฑ๋๋ฉด, ๊ทธ ํธ๋์ญ์
๊ฒฐ๊ณผ๋ฅผ ์ฐธ์กฐํ ๋ค๋ฅธ ํธ๋์ญ์
๋ ์ฐ์์ ์ผ๋ก ๋กค๋ฐฑ๋๋ ์ํฉ.
- ๋น์๋ฃ ์์กด์ฑ(Uncommitted Dependency)
- ๋ฌธ์ ์ํฉ: ์ปค๋ฐ๋์ง ์์ ๋ฐ์ดํฐ(์ ์ ๊ฐ)๋ฅผ ๋ค๋ฅธ ํธ๋์ญ์
์ด ์ฝ๊ณ , ๊ทธ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ์ฒ๋ฆฌํ๋ฉด ์ค๋ฅ ๋ฐ์ ๊ฐ๋ฅ.
- T1์ด A=500 → 600์ผ๋ก ๋ณ๊ฒฝํ์ง๋ง ์์ง ์ปค๋ฐ ์ ๋จ
- T2๊ฐ A=600 ์ฌ์ฉ
- T1์ด ๋กค๋ฐฑ → T2๋ ์๋ชป๋ A ์ฌ์ฉ
- ๋ฌธ์ ์ํฉ: ์ปค๋ฐ๋์ง ์์ ๋ฐ์ดํฐ(์ ์ ๊ฐ)๋ฅผ ๋ค๋ฅธ ํธ๋์ญ์
์ด ์ฝ๊ณ , ๊ทธ ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ์ฒ๋ฆฌํ๋ฉด ์ค๋ฅ ๋ฐ์ ๊ฐ๋ฅ.
2. MVCC (Multi-Version Concurrency Control)
MVCC๋ ์๋ณธ์ ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ ์ค์ธ ๋ฐ์ดํฐ๋ฅผ ๋์์ ์ ์งํ๋ ๋ฐฉ๋ฒ์ผ๋ก, ์๋ณธ ๋ฐ์ดํฐ์ ๋ํ Snapshot์ ๋ฐฑ์ ํ์ฌ ๋ณด๊ดํ๊ณ , ๋ ๊ฐ์ง ๋ฒ์ ์ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๋ ์ํฉ์์ ์ฌ์ฉ์๊ฐ ๋ฐ์ดํฐ์ ์ ๊ทผํ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ Snapshot์ ์ฝ์ต๋๋ค. ์ฆ ๊ธฐ์กด์ ๋ฐ์ดํฐ๋ฅผ ๋ฎ์ด์์ฐ๋ ๊ฒ์ด ์๋ ๊ธฐ์กด์ ๋ฐ์ดํฐ๋ฅผ ๋ฐํ์ผ๋ก ์ด์ ๋ฒ์ ์ ๋ฐ์ดํฐ์ ๋น๊ตํ์ฌ ๋ณ๊ฒฝ๋ ๋ด์ฉ์ ๊ธฐ๋กํ๋ ๋ฐฉ๋ฒ์ผ๋ก, ์ฌ๋ฌ ๋ฒ์ ์ ๋ฐ์ดํฐ๊ฐ ์กด์ฌํ๊ฒ ๋๊ฑฐ๋ ์ฌ์ฉ์๋ ๋ง์ง๋ง ๋ฒ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฒ ๋ฉ๋๋ค.
MVCC ์ ํน์ง
| ํน์ง | |
| Snapshot Isolation | ๊ฐ ํธ๋์ญ์ ์ ์์ ์์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ๊ณ ์ ๋ ์ค๋ ์ท์ ์ฝ์ต๋๋ค. ์ดํ ๋ค๋ฅธ ํธ๋์ญ์ ์ ๋ณ๊ฒฝ ์ฌํญ์ ์ํฅ์ ์ฃผ์ง ์์, ์ผ๊ด๋ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ์ ์์ต๋๋ค. |
| Non-blocking Read | ์ฝ๊ธฐ ์์ ์ ์ฐ๊ธฐ ์์ ์ ๋ง์ง ์๊ณ , ์ฐ๊ธฐ ์์ ๋ ์ฝ๊ธฐ ์์ ์ ๋ฐฉํด๋ฐ์ง ์์ต๋๋ค. ๋ฐ๋ผ์ ๋์์ฑ์ด ๋์์ง๊ณ , ๋ฝ ๊ฒฝํฉ์ด ์ค์ด๋ญ๋๋ค. |
| ํธ๋์ญ์ ๋ด ์ฝ๊ธฐ ์ผ๊ด์ฑ ๋ณด์ฅ | ํธ๋์ญ์ ์ด ์คํ๋๋ ๋์ ๋์ผํ ์ง์ ๊ฒฐ๊ณผ๋ ํญ์ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํฉ๋๋ค. ๋จ, Read Committed ์์ค์์๋ ์ปค๋ฐ๋ ์ต์ ๋ฐ์ดํฐ๋ง ๋ฐ์๋์ด ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ์ง ์ ์์ต๋๋ค. |
PostgreSQL์์ MVCC ์ ์์ ๋ฐฉ์ ์์

- Alice์ Bob์ ๋ชจ๋ ์ ํธ๋์ญ์ ์ ์์ํ๊ณ , txid_current() PostgreSQL ํจ์๋ฅผ ํธ์ถํ์ฌ ํธ๋์ญ์ ID๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
- Alice๊ฐ ์ ๊ธ ํ์ ์ฝ์ ํ๋ฉด xmin ์ด ๊ฐ์ด Alice์ ํธ๋์ญ์ ID๋ก ์ค์ ๋ฉ๋๋ค.
- ๊ธฐ๋ณธ ์ฝ๊ธฐ ์ปค๋ฐ ๊ฒฉ๋ฆฌ ์์ค์์, Bob์ ์จ๋ฆฌ์ค๊ฐ ํธ๋์ญ์ ์ ์ปค๋ฐํ ๋๊น์ง ์จ๋ฆฌ์ค๊ฐ ์๋ก ์ฝ์ ํ ๋ ์ฝ๋๋ฅผ ๋ณผ ์ ์์ต๋๋ค.
- Alice๊ฐ ์ปค๋ฐํ ํ, Bob์ ์ด์ Alice๊ฐ ์๋ก ์ฝ์ ํ ํ์ ๋ณผ ์ ์์ต๋๋ค
⇒ ์ฆ ์ด์ฒ๋ผ MVCC๋ ๊ฐ ํธ๋์ญ์ ์ด ์์๋ ๋, ๊ทธ ์์ ์ ์ค๋ ์ท(Snapshot)์ ์์ฑํฉ๋๋ค. ์ด ์ค๋ ์ท์ ๋ค์์ ๋ณด์ฅํฉ๋๋ค:
- ์ฝ๊ธฐ ์ผ๊ด์ฑ (Read Consistency): ํธ๋์ญ์ ์ ์์ ์ด ์์๋ ์์ ์ ๋ฐ์ดํฐ ๋ฒ์ ๋ง ๋ณผ ์ ์๊ณ , ์ดํ ๋ค๋ฅธ ํธ๋์ญ์ ์ด ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ฑฐ๋ ์ญ์ ํด๋ ๊ทธ ํธ๋์ญ์ ์๋ ์ํฅ ์์.
- ์ฐ๊ธฐ ์ถฉ๋ ์ต์ํ: ๊ฐ ํธ๋์ญ์ ์ ์์ ๋ง์ ๋ฒ์ ์์ ์์ ํ๋ฉฐ, ์ปค๋ฐ ์์ ์๋ง ์ถฉ๋์ ์ฒดํฌํจ.
MySQL์์์ MVCC(Multi-Version Concurrency Controll)
MySQL์ InnoDB์์๋ Undo Log๋ฅผ ํ์ฉํด MVCC ๊ธฐ๋ฅ์ ๊ตฌํํด๋ณด์.
CREATE TABLE member (
id INT NOT NULL,
name VARCHAR(20) NOT NULL,
area VARCHAR(100) NOT NULL,
PRIMARY KEY(m_id),
INDEX idx_area(area)
)
INSERT INTO member(id, name, area) VALUES (1, "Dohyun", "Pohang");
์ด๋ฌ๋ฉด ๋ฐ์ดํฐ๋ ๋ฉ๋ชจ๋ฆฌ์ ๋์คํฌ์ ํด๋น ๋ฐ์ดํฐ๊ฐ ๋์ผํ๊ฒ ์ ์ฅ์ด ๋๋ค.

๊ทธ๋ฆฌ๊ณ ๋ค์๊ณผ ๊ฐ์ด ์์ํ๋ค๊ณ ๊ฐ์กํด๋ณด์,
UPDATE member SET area = "Seoul" WHERE id = 1;
ํด๋น Updata ๋ฌธ์ ์คํํ ๊ฒฐ๊ณผ๋ ์ฌ์ง๊ณผ ๊ฐ๋ค. ๋จผ์ COMMIT ์คํ ์ฌ๋ถ์ ๋ฌด๊ดํ๊ฒ InnoDB ๋ฒํผ ํ์ ์๋ก์ด ๊ฐ์ผ๋ก ๊ฐฑ์ ๋๊ณ , Undo Log์๋ ๋ณ๊ฒฝ ์ ์ ๊ฐ๋ง ๋ณต์ฌ๋๋ค. InnoDB ๋ฒํผ ํ์ ๋ด์ฉ์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ฐ๋ ๋๋ฅผ ํตํด์ ๋์คํฌ์ ๊ธฐ๋ก์ด ๋๋ค. ํ์ง๋ง ๋ฐ์ ์ฌ๋ถ๋ ์์ ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ์ ์๋ค.

๋ฐ๋ผ์,
SELECT * FROM member WHERE id = 1;
ํ๊ฒ ๋๋ฉด,
- READ_UNCOMMITTED์์๋ ๋ฒํผ ํ์ ๋ณ๊ฒฝ๋ ๋ฐ์ดํฐ ์ฝ๊ธฐ → Dirty Read ๋ฐ์
- READ_COMMITTED ์ด์(REPEATABLE_READ, SERIALIZABLE) → Undo Log ๊ธฐ๋ฐ์ผ๋ก ์ปค๋ฐ ์ ๋ฐ์ดํฐ ๋ณต์
- Undo Log๋ ์ฐธ์กฐ๋์ง ์์ผ๋ฉด ์ญ์ ๊ฐ ๋์ด ์ง๋๋ค.
๊ทธ๋ผ ๋ค์์ ์ ๊ธ ๊ธฐ๋ฐ ๋์์ฑ ์ ์ด (Lock-based Concurrency Control)์ ๋ํด์ ๋ฐฐ์๋ณด๋๋ก ํฉ์๋ค!
๋ค์ ๊ธ์ด ๊ถ๊ธํ๋ค๋ฉด?
2025.07.15 - [BackEnd๐/DB & SQL] - [Database] ๋์์ฑ ์ ์ด(Concurrency Control) - Locking
[Database] ๋์์ฑ ์ ์ด(Concurrency Control) - Locking
์์ ๊ธ์ ์ฝ๊ณ ์จ ๋น์ , ๋์์ฑ ์ ์ด์ ๋ํด์ ์กฐ๊ธ์ ๋ฐฐ์๋ณด์์ ๊ฒ์ด๋ค. ์ด์ ๋ค์ ์ ์ด ๋ฐฉ๋ฒ์ธ Locking์ ๋ํด์ ๊ฐ์ด ์์๋ด ์๋ค~!LockingLocking์ ํ๋์ ํธ๋์ญ์ ์ด ๋ฐ์ดํฐ์ ์ ๊ทผํ๋ ๋์,
didcheck.tistory.com
'BackEnd๐ > DB & SQL' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [MySQL] InnoDB์์ PHANTOM READ ๋ฅผ ๋ฐฉ์งํ๋ ๋ฐฉ๋ฒ. (0) | 2025.07.16 |
|---|---|
| [Database] ๋์์ฑ ์ ์ด(Concurrency Control) - Locking (6) | 2025.07.15 |
| [MySQL / DDL] ์ ์ฝ์กฐ๊ฑด(Constraints)์ ๊ดํ์ฌ ! (4) | 2025.01.17 |
| [MySQL / DDL] CREAET ๋ถํฐ MODIFY ๊น์ง! (์ ์ฝ ์กฐ๊ฑด x) (8) | 2025.01.13 |
| [MySQL / DML] SELECT, ๋ฐ์ดํฐ ์กฐํ ๋ฐฉ๋ฒ (6) | 2025.01.07 |
