TL;DR

  • ACID ๋„ค ๊ธ€์ž ์ค‘ ๋“ฑ๊ธ‰์ด ์žˆ๋Š” ๊ฒƒ์€ I๋ฟ. ๋‚˜๋จธ์ง€ ์…‹์€ ์ผœ๊ณ  ๋„๋Š” ๋Œ€์ƒ์ด ์•„๋‹˜
  • ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์€ ์ด๋ฆ„์ด ์•„๋‹ˆ๋ผ ์–ด๋–ค ์ด์ƒ ํ˜„์ƒ์ด ๋ง‰ํžˆ๋Š”์ง€๋กœ ํŒ๋‹จ. DB๋งˆ๋‹ค ์‹ค์ œ ๋™์ž‘์ด ๋‹ค๋ฆ„
  • Serializable์€ ๊ฐ์ง€๋งŒ ์ž๋™. ์žฌ์‹œ๋„์™€ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€ ํ†ต์ผ์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ชซ

AI-assisted


1. ํŠธ๋žœ์žญ์…˜์ด ํ‘ธ๋Š” ๋ฌธ์ œ

๊ณ„์ขŒ ์ด์ฒด๋Š” ์ถœ๊ธˆ๊ณผ ์ž…๊ธˆ ๋‘ ๋ฒˆ์˜ ์“ฐ๊ธฐ๋ฅผ ๋ฌถ์–ด์•ผ ํ•œ๋‹ค.

UPDATE accounts SET balance = balance - 10000 WHERE id = 1;  -- ์ถœ๊ธˆ
UPDATE accounts SET balance = balance + 10000 WHERE id = 2;  -- ์ž…๊ธˆ

๋‘ ๋ฌธ์žฅ ์‚ฌ์ด์—์„œ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ฃฝ์œผ๋ฉด ๋ˆ์ด ์‚ฌ๋ผ์ง„๋‹ค. ์ถœ๊ธˆ์€ ๋ฐ˜์˜๋๊ณ  ์ž…๊ธˆ์€ ์•ˆ ๋๋Š”๋ฐ ๋˜๋Œ๋ฆด ๋ฐฉ๋ฒ•์ด ์—†๋‹ค.

ํŠธ๋žœ์žญ์…˜์€ ์—ฌ๋Ÿฌ ๋ฌธ์žฅ์„ ํ•˜๋‚˜์˜ ๋‹จ์œ„๋กœ ๋ฌถ์–ด ์ „๋ถ€ ๋ฐ˜์˜๋˜๊ฑฐ๋‚˜ ์ „๋ถ€ ์ทจ์†Œ๋˜๊ฒŒ ํ•˜๋Š” ์žฅ์น˜๋‹ค. ์ ˆ๋ฐ˜๋งŒ ์ ์šฉ๋œ ์ค‘๊ฐ„ ์ƒํƒœ๋ฅผ ๋ฐ–์—์„œ ๋ณผ ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ ๋‹ค.

BEGIN;                                   -- ์—ฌ๊ธฐ์„œ ์—ด๋ฆฐ๋‹ค
UPDATE accounts SET balance = balance - 10000 WHERE id = 1;
UPDATE accounts SET balance = balance + 10000 WHERE id = 2;
COMMIT;                                  -- ์—ฌ๊ธฐ์„œ ํ™•์ •๋œ๋‹ค

๊ฒฝ๊ณ„๋ฅผ ๋งŒ๋“œ๋Š” ๋ฌธ์žฅ์€ ์…‹์ด๋‹ค. BEGIN์œผ๋กœ ์—ด๊ณ  COMMIT์œผ๋กœ ํ™•์ •ํ•˜๊ณ  ROLLBACK์œผ๋กœ ๋˜๋Œ๋ฆฐ๋‹ค.

ํ™•์ •์€ ์ปค๋ฐ‹๋ฟ์ด๋‹ค

ROLLBACK์„ ์ง์ ‘ ์น˜๋Š” ๊ฒฝ์šฐ๋Š” ์˜คํžˆ๋ ค ๋“œ๋ฌผ๋‹ค. ์ปค๋ฐ‹ํ•˜์ง€ ์•Š์€ ํŠธ๋žœ์žญ์…˜์€ ์–ด๋–ค ๊ฒฝ๋กœ๋กœ ๋๋‚˜๋“  ๋กค๋ฐฑ๋œ๋‹ค.

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๊ณ  ๋น ์ ธ๋‚˜๊ฐ€๋ฉด ์ปค๋ฐ‹์— ๋„๋‹ฌํ•˜์ง€ ๋ชปํ•˜๋ฏ€๋กœ ๋กค๋ฐฑ๋œ๋‹ค
  • ์ปค๋„ฅ์…˜์ด ๋Š๊ธฐ๋ฉด ์„œ๋ฒ„๊ฐ€ ๊ทธ ์„ธ์…˜์˜ ์—ด๋ฆฐ ํŠธ๋žœ์žญ์…˜์„ ๋กค๋ฐฑํ•œ๋‹ค
  • ์„œ๋ฒ„๊ฐ€ ์ฃฝ์œผ๋ฉด ์žฌ์‹œ์ž‘ํ•  ๋•Œ WAL์„ ๋ณด๊ณ  ์ปค๋ฐ‹ ์•ˆ ๋œ ๊ฒƒ์„ ๋˜๋Œ๋ฆฐ๋‹ค

๊ทธ๋ž˜์„œ ๊ธฐ๋ณธ ์ƒํƒœ๋Š” โ€œ์ทจ์†Œโ€์ด๊ณ  ์ปค๋ฐ‹๋งŒ์ด ํ™•์ •์ด๋‹ค. ๊ฒฝ๊ณ„ ์„ค๊ณ„์™€ ์žฌ์‹œ๋„๋Š” ์ด ์„ฑ์งˆ์„ ์ „์ œ๋กœ ํ•œ๋‹ค.

์ฃผ์˜: ๋กค๋ฐฑ์€ DB ์•ˆ๊นŒ์ง€๋งŒ ๋˜๋Œ๋ฆฐ๋‹ค

  • ์™ธ๋ถ€ API ํ˜ธ์ถœ, ํ ๋ฐœํ–‰, ๋ฉ”์ผ ๋ฐœ์†ก, ํŒŒ์ผ ์“ฐ๊ธฐ๋Š” ๋กค๋ฐฑ์„ ๋”ฐ๋ฅด์ง€ ์•Š๋Š”๋‹ค
  • ์‹œํ€€์Šค๋กœ ๋ฝ‘์€ ๋ฒˆํ˜ธ๋„ ๋Œ์•„์˜ค์ง€ ์•Š๋Š”๋‹ค. ROLLBACK ํ›„์—๋„ ๋‹ค์Œ ๊ฐ’์€ ๊ทธ๋‹ค์Œ ๋ฒˆํ˜ธ๋‹ค
  • ํŠธ๋žœ์žญ์…˜ ์•ˆ์— DB ์ž‘์—…๋งŒ ๋‘์–ด์•ผ ํ•˜๋Š” ์ด์œ ๊ฐ€ ์—ฌ๊ธฐ ์žˆ๋‹ค

ACID๋Š” ํŠธ๋žœ์žญ์…˜์ด ์ง€์ผœ์ฃผ๋Š” ์„ฑ์งˆ ๋„ค ๊ฐ€์ง€๋ฅผ ๋ฌถ์€ ์•ฝ์ž๋‹ค.


2. A ยท C ยท I ยท D

๊ฐ ๊ธ€์ž๊ฐ€ ๋ง‰๋Š” ์ƒํ™ฉ์ด ๋‹ค๋ฅด๋‹ค.

์ด๋ฆ„๋ง‰๋Š” ์ƒํ™ฉ๋‹ด๋‹น
AAtomicity์ ˆ๋ฐ˜๋งŒ ๋ฐ˜์˜๋œ ์ƒํƒœDB
CConsistency์ œ์•ฝ์„ ์–ด๊ธด ์ƒํƒœDB + ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜
IIsolation๋‚จ์˜ ์ž‘์—… ์ค‘๊ฐ„์ด ๋ณด์ด๋Š” ์ƒํƒœDB
DDurability์ปค๋ฐ‹ํ–ˆ๋Š”๋ฐ ์‚ฌ๋ผ์ง€๋Š” ์ƒํƒœDB
  • Atomicity: ํŠธ๋žœ์žญ์…˜ ์•ˆ์˜ ๋ฌธ์žฅ์ด ์ „๋ถ€ ์„ฑ๊ณตํ•˜๊ฑฐ๋‚˜ ์ „๋ถ€ ์ทจ์†Œ๋œ๋‹ค. ์ค‘๊ฐ„ ์ง€์ ์ด ๋‚จ์ง€ ์•Š๋Š”๋‹ค
  • Consistency: ํŠธ๋žœ์žญ์…˜ ์ „ํ›„๋กœ ์ œ์•ฝ ์กฐ๊ฑด์ด ๊นจ์ง€์ง€ ์•Š๋Š”๋‹ค. NOT NULL, FOREIGN KEY, CHECK๊ฐ€ ์—ฌ๊ธฐ ํ•ด๋‹นํ•œ๋‹ค
  • Isolation: ๋™์‹œ์— ๋„๋Š” ํŠธ๋žœ์žญ์…˜์ด ์„œ๋กœ์˜ ์ค‘๊ฐ„ ์ƒํƒœ๋ฅผ ๋ณด์ง€ ์•Š๋Š”๋‹ค
  • Durability: ์ปค๋ฐ‹ํ–ˆ๋‹ค๊ณ  ์‘๋‹ตํ–ˆ์œผ๋ฉด ๊ทธ ์งํ›„ ์ •์ „์ด ๋‚˜๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚จ๋Š”๋‹ค

C๋Š” ๋‚˜๋จธ์ง€ ์…‹๊ณผ ์„ฑ๊ฒฉ์ด ๋‹ค๋ฅด๋‹ค

  • AยทIยทD๋Š” DB๊ฐ€ ์•Œ์•„์„œ ์ง€ํ‚จ๋‹ค. C๋Š” ์ ˆ๋ฐ˜์ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ชซ์ด๋‹ค
  • โ€œ๊ณ„์ขŒ ์ž”์•ก์˜ ์ดํ•ฉ์ด ๋ณ€ํ•˜์ง€ ์•Š๋Š”๋‹คโ€ ๊ฐ™์€ ๊ทœ์น™์€ DB๊ฐ€ ๋ชจ๋ฅธ๋‹ค. ๊ฐœ๋ฐœ์ž๊ฐ€ ๋‘ UPDATE๋ฅผ ํ•œ ํŠธ๋žœ์žญ์…˜์— ๋„ฃ์–ด์•ผ ์ง€์ผœ์ง„๋‹ค
  • DB๊ฐ€ ๋ณด์žฅํ•˜๋Š” C๋Š” ์„ ์–ธํ•œ ์ œ์•ฝ ์กฐ๊ฑด๊นŒ์ง€๋‹ค
  • ๊ทธ๋ž˜์„œ C๋ฅผ ๋‘๊ณ  โ€œ์•ฝ์ž(ACID)๋ฅผ ๋งŒ๋“ค๋ ค๊ณ  ๋ผ์›Œ ๋„ฃ์€ ๊ธ€์žโ€๋ผ๋Š” ํ‰์ด ๋”ฐ๋ผ๋‹ค๋‹Œ๋‹ค. ์‹ค์ œ๋กœ A์™€ D๊ฐ€ ์žˆ์œผ๋ฉด ๋Œ€๋ถ€๋ถ„ ๋”ฐ๋ผ์˜ค๋Š” ์„ฑ์งˆ์ด๋‹ค

Durability: ์–ด๋””๊นŒ์ง€ ์ผ์„ ๋•Œ ์ปค๋ฐ‹์ธ๊ฐ€

  • ์ปค๋ฐ‹ ์‹œ์ ์— ๋ฐ์ดํ„ฐ ํŒŒ์ผ์„ ๊ณ ์น˜์ง€ ์•Š๋Š”๋‹ค. WAL(Write-Ahead Log)์— ๋ณ€๊ฒฝ ๋‚ด์—ญ์„ ๋จผ์ € ์ ๊ณ  fsync๋กœ ๋””์Šคํฌ์— ๋ฐ€์–ด ๋„ฃ๋Š”๋‹ค
  • ๋ฐ์ดํ„ฐ ํŒŒ์ผ ๋ฐ˜์˜์€ ๋‚˜์ค‘์— ๋ชฐ์•„์„œ ํ•œ๋‹ค. ์ˆœ์ฐจ ์“ฐ๊ธฐ ํ•œ ๋ฒˆ์ด ์ž„์˜ ์œ„์น˜ ์“ฐ๊ธฐ ์—ฌ๋Ÿฌ ๋ฒˆ๋ณด๋‹ค ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค
  • ์ฃฝ์—ˆ๋‹ค ์‚ด์•„๋‚˜๋ฉด WAL์„ ์žฌ์ƒํ•ด ๋ณต๊ตฌํ•œ๋‹ค
  • fsync = off๋กœ ๋‘๋ฉด ์ปค๋ฐ‹์ด ๋นจ๋ผ์ง€๋Š” ๋Œ€์‹  D๋ฅผ ํฌ๊ธฐํ•œ๋‹ค. ์žฌํ˜„ ๊ฐ€๋Šฅํ•œ ๋ฐฐ์น˜ ์ ์žฌ๊ฐ€ ์•„๋‹ˆ๋ฉด ์ผœ๋‘”๋‹ค

3. I๋งŒ ๋“ฑ๊ธ‰์ด ์žˆ๋Š” ์ด์œ 

A์™€ D๋Š” ์ง€ํ‚ค๊ฑฐ๋‚˜ ์•ˆ ์ง€ํ‚ค๊ฑฐ๋‚˜ ๋‘˜ ์ค‘ ํ•˜๋‚˜๋‹ค. ์ ˆ๋ฐ˜๋งŒ ์›์ž์ ์ธ ํŠธ๋žœ์žญ์…˜์€ ์—†๋‹ค.

I๋Š” ๋‹ค๋ฅด๋‹ค. ์™„๋ฒฝํ•œ ๊ฒฉ๋ฆฌ๋Š” ํŠธ๋žœ์žญ์…˜์„ ํ•œ ์ค„๋กœ ์„ธ์›Œ ํ•˜๋‚˜์”ฉ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ธ๋ฐ ๊ทธ๋Ÿฌ๋ฉด ๋™์‹œ ์ฒ˜๋ฆฌ๋Ÿ‰์ด ์‚ฌ๋ผ์ง„๋‹ค.

  • ์™„๋ฒฝํ•œ ๊ฒฉ๋ฆฌ: T1 ๋ โ†’ T2 ์‹œ์ž‘ โ†’ T3 ์‹œ์ž‘. ์•ˆ์ „ํ•˜์ง€๋งŒ ๋А๋ฆฌ๋‹ค
  • ๋А์Šจํ•œ ๊ฒฉ๋ฆฌ: T1 T2 T3 ๋™์‹œ ์ง„ํ–‰. ๋น ๋ฅด์ง€๋งŒ ์ด์ƒํ•œ ๊ฐ’์ด ๋ณด์ธ๋‹ค

๊ทธ๋ž˜์„œ ๊ฒฉ๋ฆฌ๋งŒ ์–ด๋””๊นŒ์ง€ ํ—ˆ์šฉํ• ์ง€ ๊ณ ๋ฅด๋Š” ์†์žก์ด๊ฐ€ ๋๋‹ค.


4. ์„ธ ๊ฐ€์ง€ ์ด์ƒ ํ˜„์ƒ

๊ฒฉ๋ฆฌ๋ฅผ ๋А์Šจํ•˜๊ฒŒ ํ•˜๋ฉด ๋‚˜ํƒ€๋‚˜๋Š” ํ˜„์ƒ์— ์ด๋ฆ„์ด ๋ถ™์–ด ์žˆ๋‹ค. ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์€ ์ด ์…‹ ์ค‘ ๋ฌด์—‡์„ ๋ง‰๋А๋ƒ๋กœ ์ •์˜๋œ๋‹ค.

Dirty Read: ์ปค๋ฐ‹ ์•ˆ ๋œ ๊ฐ’์„ ์ฝ๋Š”๋‹ค

T1                        T2
------------------------  ------------------------
UPDATE balance = 500
                          SELECT balance  --> 500
ROLLBACK

T2๊ฐ€ ์ฝ์€ 500์€ ํ•œ ๋ฒˆ๋„ ํ™•์ •๋œ ์  ์—†๋Š” ๊ฐ’์ด๋‹ค.

Non-Repeatable Read: ๊ฐ™์€ ํ–‰์„ ๋‘ ๋ฒˆ ์ฝ์—ˆ๋Š”๋ฐ ๊ฐ’์ด ๋‹ค๋ฅด๋‹ค

T1                        T2
------------------------  ------------------------
SELECT balance  --> 1000
                          UPDATE balance = 500
                          COMMIT
SELECT balance  --> 500

T1์€ ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ํ–ˆ๋Š”๋ฐ ๊ฐ™์€ ์งˆ๋ฌธ์˜ ๋‹ต์ด ๋ฐ”๋€Œ์—ˆ๋‹ค.

Phantom Read: ๊ฐ™์€ ์กฐ๊ฑด์œผ๋กœ ๋‘ ๋ฒˆ ์กฐํšŒํ–ˆ๋Š”๋ฐ ํ–‰ ๊ฐœ์ˆ˜๊ฐ€ ๋‹ค๋ฅด๋‹ค

T1                             T2
-----------------------------  -----------------------------
SELECT count(*) WHERE age > 30
  --> 10
                               INSERT age = 35
                               COMMIT
SELECT count(*) WHERE age > 30
  --> 11

์—†๋˜ ํ–‰์ด ๋‚˜ํƒ€๋‚ฌ๋‹ค. ๋Œ€์ƒ์€ ํŠน์ • ํ–‰์ด ์•„๋‹ˆ๋ผ ์กฐ๊ฑด์— ๋งž๋Š” ์ง‘ํ•ฉ์ด๋‹ค.

Non-Repeatable Read์™€ Phantom์˜ ์ฐจ์ด

  • Non-repeatable read: ์ด๋ฏธ ์ฝ์€ ํ–‰์˜ ๊ฐ’์ด ๋ฐ”๋€๋‹ค. ๋Œ€์ƒ์ด ํŠน์ • ํ–‰์ด๋‹ค
  • Phantom: ์กฐ๊ฑด์— ๋งž๋Š” ํ–‰์˜ ์ง‘ํ•ฉ์ด ๋ฐ”๋€๋‹ค. ๋Œ€์ƒ์ด ๋ฒ”์œ„๋‹ค
  • ๋ง‰๋Š” ๋ฐฉ๋ฒ•๋„ ๋‹ค๋ฅด๋‹ค. ์•ž์€ ์ฝ์€ ํ–‰์— ๋ฝ์„ ๊ฑธ๋ฉด ๋˜์ง€๋งŒ ๋’ค๋Š” ์•„์ง ์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ–‰์ด๋ผ ๊ฑธ ๋Œ€์ƒ์ด ์—†๋‹ค. ๊ทธ๋ž˜์„œ ๋ฒ”์œ„ ์ž์ฒด์— ๋ฝ์„ ๊ฑธ๊ฑฐ๋‚˜ ์Šค๋ƒ…์ƒท์„ ๊ณ ์ •ํ•ด์•ผ ํ•œ๋‹ค

5. ๊ฒฉ๋ฆฌ ์ˆ˜์ค€ ๋„ค ๋‹จ๊ณ„

ANSI SQL-92๊ฐ€ ๋„ค ๋‹จ๊ณ„๋ฅผ ์ •์˜ํ–ˆ๋‹ค. ์œ„๋กœ ๊ฐˆ์ˆ˜๋ก ๋А์Šจํ•˜๊ณ  ์•„๋ž˜๋กœ ๊ฐˆ์ˆ˜๋ก ์—„๊ฒฉํ•˜๋‹ค.

์ˆ˜์ค€Dirty ReadNon-Repeatable ReadPhantom
Read Uncommittedํ—ˆ์šฉํ—ˆ์šฉํ—ˆ์šฉ
Read Committed๋ฐฉ์ง€ํ—ˆ์šฉํ—ˆ์šฉ
Repeatable Read๋ฐฉ์ง€๋ฐฉ์ง€ํ—ˆ์šฉ
Serializable๋ฐฉ์ง€๋ฐฉ์ง€๋ฐฉ์ง€
  • Read Uncommitted: ์ปค๋ฐ‹ ์ „ ๊ฐ’๊นŒ์ง€ ๋ณด์ธ๋‹ค. ์“ฐ๋Š” ๊ณณ์ด ๊ฑฐ์˜ ์—†๋‹ค
  • Read Committed: ์ปค๋ฐ‹๋œ ๊ฐ’๋งŒ ์ฝ๋Š”๋‹ค. ๋ฌธ์žฅ ํ•˜๋‚˜๋งˆ๋‹ค ์ตœ์‹  ์Šค๋ƒ…์ƒท์„ ์ƒˆ๋กœ ๋œฌ๋‹ค
  • Repeatable Read: ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘ ์‹œ์ ์˜ ์Šค๋ƒ…์ƒท์„ ๋๊นŒ์ง€ ์œ ์ง€ํ•œ๋‹ค
  • Serializable: ํŠธ๋žœ์žญ์…˜์„ ํ•˜๋‚˜์”ฉ ์‹คํ–‰ํ•œ ๊ฒƒ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์žฅํ•œ๋‹ค

ํ‘œ๋Š” ๊ธˆ์ง€ ๋ชฉ๋ก์ด ์•„๋‹ˆ๋ผ ํ—ˆ์šฉ ๋ชฉ๋ก์ด๋‹ค. ํ‘œ์ค€์€ โ€œ์ด ์ˆ˜์ค€์—์„œ ์ด ํ˜„์ƒ์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๋‹คโ€๊ณ  ์ •ํ•  ๋ฟ์ด๋ผ ๋” ์—„๊ฒฉํ•˜๊ฒŒ ๊ตฌํ˜„ํ•ด๋„ ๊ทœ๊ฒฉ ์œ„๋ฐ˜์ด ์•„๋‹ˆ๋‹ค. ์‹ค์ œ DB๊ฐ€ ์ด ์—ฌ์ง€๋ฅผ ์“ด๋‹ค.


6. ์ด๋ฆ„๊ณผ ์‹ค์ œ ๋™์ž‘์ด ๋‹ค๋ฅด๋‹ค

๊ฐ™์€ REPEATABLE READ๋ฅผ ๊ฑธ์–ด๋„ DB๋งˆ๋‹ค ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค๋ฅด๋‹ค.

๊ธฐ๋ณธ๊ฐ’ํŠน์ด์‚ฌํ•ญ
PostgreSQLRead CommittedRead Uncommitted๋ฅผ ์š”์ฒญํ•ด๋„ Read Committed๋กœ ๋™์ž‘. Repeatable Read์—์„œ phantom๋„ ๋ง‰ํž˜
MySQL InnoDBRepeatable Readgap lock์œผ๋กœ phantom์„ ๋Œ€์ฒด๋กœ ๋ง‰์Œ
OracleRead CommittedRead Uncommitted์™€ Repeatable Read๋ฅผ ์•„์˜ˆ ์ง€์›ํ•˜์ง€ ์•Š์Œ
SQL ServerRead Committed์˜ต์…˜์œผ๋กœ ์Šค๋ƒ…์ƒท ๊ธฐ๋ฐ˜ ๋™์ž‘ ์ „ํ™˜

PostgreSQL์˜ Repeatable Read๋Š” ํ‘œ์ค€์ด ์š”๊ตฌํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์—„๊ฒฉํ•˜๋‹ค. ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘ ์‹œ์  ์Šค๋ƒ…์ƒท์„ ๋๊นŒ์ง€ ์œ ์ง€ํ•˜๋Š” ๋ฐฉ์‹์ด๋ผ phantom๊นŒ์ง€ ํ•จ๊ป˜ ๋ง‰ํžŒ๋‹ค.

-- ์„ธ์…˜ ๋‹จ์œ„๋กœ ์ง€์ •
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ;
 
-- ํŠธ๋žœ์žญ์…˜ ํ•˜๋‚˜์—๋งŒ ์ง€์ •
BEGIN ISOLATION LEVEL SERIALIZABLE;
SELECT ...;
COMMIT;

์ฃผ์˜: ์ˆ˜์ค€ ์ด๋ฆ„์œผ๋กœ ํŒ๋‹จํ•˜์ง€ ์•Š๋Š”๋‹ค

  • ์ด์‹์„ฑ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ์“ฐ๋ ค๋ฉด ์ด๋ฆ„์ด ์•„๋‹ˆ๋ผ โ€œ์–ด๋–ค ์ด์ƒ ํ˜„์ƒ์ด ๋ง‰ํžˆ๋Š”๊ฐ€โ€๋ฅผ ํ™•์ธํ•œ๋‹ค
  • MySQL์—์„œ ๋Œ๋˜ ๋ฐฐ์น˜๋ฅผ PostgreSQL๋กœ ์˜ฎ๊ฒผ๋Š”๋ฐ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๋Š” ์ผ์ด ์—ฌ๊ธฐ์„œ ์ƒ๊ธด๋‹ค
  • ํŠนํžˆ Repeatable Read๋Š” ๋‘ DB์˜ ๋™์ž‘ ์ฐจ์ด๊ฐ€ ํฌ๋‹ค

7. ํ‘œ์ค€์— ์—†๋Š” ์ด์ƒ ํ˜„์ƒ

ANSI๊ฐ€ ์ •์˜ํ•œ ์…‹์œผ๋กœ๋Š” ๋ถ€์กฑํ•˜๋‹ค๋Š” ์ง€์ ์ด 1995๋…„ ๋…ผ๋ฌธ์—์„œ ๋‚˜์™”๋‹ค. ๋Œ€ํ‘œ๊ฐ€ write skew๋‹ค.

๋ณ‘์›์— ๋‹น์ง ์˜์‚ฌ๊ฐ€ ์ตœ์†Œ ํ•œ ๋ช…์€ ์žˆ์–ด์•ผ ํ•œ๋‹ค๊ณ  ํ•˜์ž. ์ง€๊ธˆ ๋‘ ๋ช…์ด ๋‹น์ง์ด๋‹ค.

T1 (doctor A)                  T2 (doctor B)
-----------------------------  -----------------------------
SELECT count(*) WHERE on_call  SELECT count(*) WHERE on_call
  --> 2, so I can leave          --> 2, so I can leave
UPDATE a SET on_call = false
                               UPDATE b SET on_call = false
COMMIT
                               COMMIT

๋‹น์ง์ด 0๋ช…์ด ๋๋‹ค. ๋‘ ํŠธ๋žœ์žญ์…˜ ๋ชจ๋‘ ๊ทœ์น™์„ ํ™•์ธํ•˜๊ณ  ์–ด๊ฒผ๋‹ค.

๊ฐ™์€ ํ–‰์„ ๊ณ ์น˜์ง€ ์•Š์•„์„œ ์ถฉ๋Œ๋กœ ๊ฐ์ง€๋˜์ง€ ์•Š๋Š”๋‹ค. Repeatable Read์—์„œ๋„ ๊ทธ๋Œ€๋กœ ์ผ์–ด๋‚œ๋‹ค.

PostgreSQL์€ Serializable์—์„œ SSI(Serializable Snapshot Isolation)๋กœ ์ด๋ฅผ ์žก๋Š”๋‹ค.

ERROR:  could not serialize access due to read/write dependencies among transactions
HINT:  The transaction might succeed if retried.

Serializable์ด ํ•„์š”ํ•œ ์ž๋ฆฌ

  • โ€œ์กฐ๊ฑด์„ ํ™•์ธํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ์“ด๋‹คโ€๊ฐ€ ํ•œ ํŠธ๋žœ์žญ์…˜ ์•ˆ์— ์žˆ์„ ๋•Œ๋‹ค. ๋‹น์ง ์˜ˆ์‹œ, ์žฌ๊ณ  ํ™•์ธ ํ›„ ์ฐจ๊ฐ, ์ค‘๋ณต ์˜ˆ์•ฝ ๋ฐฉ์ง€๊ฐ€ ์—ฌ๊ธฐ ํ•ด๋‹นํ•œ๋‹ค
  • ์กฐ๊ฑด ๊ฒ€์‚ฌ ์—†์ด ๊ฐ’๋งŒ ๊ฐฑ์‹ ํ•˜๋Š” ์ž‘์—…์€ Read Committed๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค

8. Serializable์€ ๊ฑธ์–ด๋‘๋Š” ๊ฒƒ์œผ๋กœ ๋๋‚˜์ง€ ์•Š๋Š”๋‹ค

๊ฐ์ง€๋Š” DB๊ฐ€ ์ „๋ถ€ ํ•˜๊ณ  ์‹คํŒจ ์ฒ˜๋ฆฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ „๋ถ€ ํ•œ๋‹ค.

SELECT FOR UPDATE๋Š” ๊ฑธ์–ด๋‘๋ฉด ๋์ด๋‹ค. ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ๊ฒฐ๊ตญ ์„ฑ๊ณตํ•œ๋‹ค. Serializable์€ ๋ฐ˜๋Œ€๋กœ ๊ฑธ์–ด๋‘๋Š” ๊ฒƒ์ด ์‹œ์ž‘์ด๊ณ  ์—๋Ÿฌ๋ฅผ ๋ฐ›์•„์ค„ ์ฝ”๋“œ๊ฐ€ ์—†์œผ๋ฉด ๊ฐฑ์‹ ์ด ์กฐ์šฉํžˆ ์‚ฌ๋ผ์ง„๋‹ค.

์ฝ์€ ์ž๋ฆฌ๋ฅผ ํ‘œ์‹œํ•ด๋‘๊ณ  ๋‚จ์ด ์ผ๋Š”์ง€๋งŒ ๋ณธ๋‹ค

SELECT count(*) FROM doctors WHERE on_call = true;
-- ์กฐ๊ฑด์— ๊ฑธ๋ฆฐ ์ธ๋ฑ์Šค ํŽ˜์ด์ง€์™€ ํŠœํ”Œ์— SIReadLock ํ‘œ์‹œ๊ฐ€ ๋‚จ๋Š”๋‹ค

SIReadLock์€ ์ด๋ฆ„์ด ๋ฝ์ด์ง€๋งŒ ์•„๋ฌด๋„ ๋ง‰์ง€ ์•Š๋Š”๋‹ค. โ€œ๋‚ด๊ฐ€ ์ด๊ฑธ ์ฝ์—ˆ๋‹คโ€๋Š” ํ”์ ์ผ ๋ฟ์ด๋‹ค. pg_locks์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

SELECT locktype, relation::regclass, mode
FROM pg_locks WHERE mode = 'SIReadLock';

์ดํ›„ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ๊ทธ ์ž๋ฆฌ์— ์“ฐ๋ฉด ์ฝ๊ธฐยท์“ฐ๊ธฐ ์˜์กด์ด ํ•˜๋‚˜ ๊ธฐ๋ก๋œ๋‹ค. ์ด ์˜์กด์ด ํŠน์ • ํ˜•ํƒœ๋กœ ๋งž๋ฌผ๋ฆฌ๋ฉด ์ง๋ ฌ ์ˆœ์„œ๊ฐ€ ์กด์žฌํ•  ์ˆ˜ ์—†๋‹ค๊ณ  ํŒ์ •ํ•ด ํ•œ์ชฝ์„ ์ทจ์†Œํ•œ๋‹ค.

๊ฐœ๋ฐœ์ž ๋ชซ ์„ธ ๊ฐ€์ง€

์žฌ์‹œ๋„ ์ฝ”๋“œ๋ฅผ ๋ถ™์ธ๋‹ค

ํŠธ๋žœ์žญ์…˜ ์ „์ฒด๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•ด์•ผ ํ•œ๋‹ค. ์ปค๋ฐ‹๋งŒ ๋‹ค์‹œ ์‹œ๋„ํ•˜๋Š” ๊ฒƒ์€ ์˜๋ฏธ๊ฐ€ ์—†๋‹ค. ์ฝ์€ ๊ฐ’ ์ž์ฒด๊ฐ€ ๋ฌดํšจ๋ผ์„œ๋‹ค.

for _ in range(3):
    try:
        with conn.transaction():          # ISOLATION LEVEL SERIALIZABLE
            if count_on_call() >= 2:
                set_off_duty(me)
        break
    except psycopg.errors.SerializationFailure:   # SQLSTATE 40001
        continue
else:
    raise RuntimeError("์žฌ์‹œ๋„ ํ•œ๋„ ์ดˆ๊ณผ")

์žฌ์‹œ๋„ ๋Œ€์ƒ ์—๋Ÿฌ ๊ตฌ๋ถ„๊ณผ ๋ถ€์ˆ˜ ํšจ๊ณผ ์ฒ˜๋ฆฌ๋Š” ์žฌ์‹œ๋„ ๊ฐ€๋Šฅํ•œ ํŠธ๋žœ์žญ์…˜ ์„ค๊ณ„์—์„œ ๋‹ค๋ฃฌ๋‹ค.

์ฐธ์—ฌํ•˜๋Š” ํŠธ๋žœ์žญ์…˜์„ ์ „๋ถ€ Serializable๋กœ ๋งž์ถ˜๋‹ค

๊ฐ€์žฅ ์ž์ฃผ ๋†“์น˜๋Š” ๋ถ€๋ถ„์ด๋‹ค. ๋ณด์žฅ ๋ฒ”์œ„๊ฐ€ โ€œ์ปค๋ฐ‹์— ์„ฑ๊ณตํ•œ Serializable ํŠธ๋žœ์žญ์…˜ ์‚ฌ์ดโ€๋กœ ํ•œ์ •๋œ๋‹ค.

T1  SERIALIZABLE      SIReadLock์„ ๋‚จ๊ธด๋‹ค   --> ์ถ”์  ๋Œ€์ƒ
T2  READ COMMITTED    ํ”์ ์„ ์•ˆ ๋‚จ๊ธด๋‹ค      --> ์ถ”์  ๋Œ€์ƒ ์•„๋‹˜

T2๊ฐ€ ๊ทœ์น™์„ ๊นจ๋„ T1์€ ์•Œ์ง€ ๋ชปํ•œ๋‹ค. ๊ฐ™์€ ํ…Œ์ด๋ธ”์„ ๊ฑด๋“œ๋ฆฌ๋Š” ๊ฒฝ๋กœ๊ฐ€ ์—ฌ๋Ÿฟ์ด๋ฉด ๋ฐฐ์น˜ ํ•˜๋‚˜๊นŒ์ง€ ์ „๋ถ€ ๋งž์ถฐ์•ผ ํ•œ๋‹ค.

์ปค๋ฐ‹ ์ „์— ์ฝ์€ ๊ฐ’์„ ๋ฐ–์œผ๋กœ ๋‚ด๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค

Serializable์—์„œ๋Š” ์ทจ์†Œ์™€ ์žฌ์‹œ๋„๊ฐ€ ์ •์ƒ ํ๋ฆ„์ด๋‹ค. ํŠธ๋žœ์žญ์…˜ ์ค‘๊ฐ„์— ์ฝ์€ ๊ฐ’์œผ๋กœ ์™ธ๋ถ€ API๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์ทจ์†Œ๋œ ์‹œ๋„์˜ ํ˜ธ์ถœ๋„ ๋ฐ–์— ๋‚จ๊ณ  ์žฌ์‹œ๋„ ํšŸ์ˆ˜๋งŒํผ ๊ฐ™์€ ํ˜ธ์ถœ์ด ๋ฐ˜๋ณต๋œ๋‹ค. ์™ธ๋ถ€๋กœ ๋‚˜๊ฐ€๋Š” ๋™์ž‘์€ ์ปค๋ฐ‹ ์ดํ›„๋กœ ๋ฏธ๋ฃฌ๋‹ค.

์ž๋™์ด๋ผ์„œ ์น˜๋ฅด๋Š” ๋Œ€๊ฐ€

SSI๋Š” ๋ณด์ˆ˜์ ์ด๋‹ค. ์ง„์งœ ์ถฉ๋Œ์ธ์ง€ ๋๊นŒ์ง€ ๋”ฐ์ง€๋Š” ๋Œ€์‹  ์œ„ํ—˜ํ•ด ๋ณด์ด๋ฉด ์ž๋ฅธ๋‹ค. ์‹ค์ œ๋กœ๋Š” ๋ฌธ์ œ์—†์—ˆ์„ ํŠธ๋žœ์žญ์…˜๋„ ์ทจ์†Œ๋œ๋‹ค.

์ฃผ์˜: ์ˆœ์ฐจ ์Šค์บ”์ด ๊ฑฐ์ง“ ์–‘์„ฑ์„ ํ‚ค์šด๋‹ค

  • ์ธ๋ฑ์Šค๋ฅผ ๋ชป ํƒ€๋ฉด ํ…Œ์ด๋ธ” ์ „์ฒด์— ๊ด€๊ณ„ ์ˆ˜์ค€ predicate lock์ด ๊ฑธ๋ ค ๋ฌด๊ด€ํ•œ ํŠธ๋žœ์žญ์…˜๊นŒ์ง€ ์ถฉ๋Œํ•œ๋‹ค. ์ธ๋ฑ์Šค๋ฅผ ํƒ€๋ฉด ํŽ˜์ด์ง€๋‚˜ ํŠœํ”Œ ๋‹จ์œ„๋กœ ์ขํ˜€์ง„๋‹ค
  • predicate lock ํ…Œ์ด๋ธ”์ด ๋ฉ”๋ชจ๋ฆฌ์— ๋ชจ์ž๋ผ๋„ ๊ฐ™์€ ์ผ์ด ์ƒ๊ธด๋‹ค. ํŽ˜์ด์ง€ ๋‹จ์œ„ ๋ฝ์„ ๊ด€๊ณ„ ๋‹จ์œ„๋กœ ํ•ฉ์น˜๋ฉด์„œ ์ถฉ๋Œ ๋ฒ”์œ„๊ฐ€ ๋„“์–ด์ง„๋‹ค
  • max_pred_locks_per_transaction, max_pred_locks_per_relation, max_pred_locks_per_page๋กœ ํ•œ๋„๋ฅผ ์˜ฌ๋ฆฐ๋‹ค
  • ์œ ๋‹ˆํฌ ์ œ์•ฝ์„ ๋ฏธ๋ฆฌ ํ™•์ธํ•˜๊ณ  ๋„ฃ์—ˆ๋Š”๋ฐ๋„ ์œ„๋ฐ˜์ด ๋‚˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค. PostgreSQL ๋ฌธ์„œ์— ์ ํžŒ ๊ฑฐ์ง“ ์–‘์„ฑ์ด๋‹ค

์ฝ๊ธฐ๋งŒ ํ•˜๋Š” ํŠธ๋žœ์žญ์…˜์€ READ ONLY๋ฅผ ๋ถ™์ธ๋‹ค. predicate lock์„ ๋Œ€๋ถ€๋ถ„ ๋‚จ๊ธฐ์ง€ ์•Š์•„ ๋ถ€๋‹ด์ด ์ค„์–ด๋“ ๋‹ค.

BEGIN ISOLATION LEVEL SERIALIZABLE READ ONLY;

9. ๊ฒฉ๋ฆฌ๋ฅผ ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•

์ง€๊ธˆ๊นŒ์ง€๋Š” ๋ฌด์—‡์„ ๋ณด์žฅํ•˜๋Š”๊ฐ€์˜€๋‹ค. ์–ด๋–ป๊ฒŒ ๋งŒ๋“ค์–ด๋‚ด๋Š”๊ฐ€๋Š” ๋ณ„๊ฐœ ๋ฌธ์ œ๋‹ค.

๊ฒฉ๋ฆฌ ์ˆ˜์ค€        ๋ฌด์—‡์„ ๋ณด์žฅํ• ์ง€ ์ •ํ•˜๋Š” ๊ณ„์•ฝ
    โ†“
๋™์‹œ์„ฑ ์ œ์–ด      ๊ทธ ๊ณ„์•ฝ์„ ์ง€ํ‚ค๋Š” ์ˆ˜๋‹จ
                 locking ยท MVCC ยท OCC

Read Committed ํ•˜๋‚˜๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ๋„ ๋ฐฉ๋ฒ•์ด ๊ฐˆ๋ฆฐ๋‹ค. ์ฝ๋Š” ํ–‰๋งˆ๋‹ค ๋ฝ์„ ๊ฑธ์–ด ์ปค๋ฐ‹ ์ „ ๊ฐ’์— ์ ‘๊ทผ์„ ๋ง‰๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๊ณ  ๋ฒ„์ „์„ ์—ฌ๋Ÿฌ ๊ฐœ ์œ ์ง€ํ•ด ์ปค๋ฐ‹๋œ ๋ฒ„์ „๋งŒ ๋ณด์—ฌ์ฃผ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. ์•ž์ด locking์ด๊ณ  ๋’ค๊ฐ€ MVCC๋‹ค.

๋™์‹œ์„ฑ ์ œ์–ด์—์„œ ์„ธ ๋ฐฉ์‹์„ ๋‹ค๋ฃฌ๋‹ค.