MVCC 2

์ •๋ฏผ๊ตยท2023๋…„ 6์›” 24์ผ
0

DB

๋ชฉ๋ก ๋ณด๊ธฐ
11/12
post-thumbnail

๐Ÿ“’

์ด์ „ ํฌ์ŠคํŒ…์—์„œ๋Š” MVCC๋ž€ ๋ฌด์—‡์ธ์ง€, MVCC๊ฐ€ ์ ์šฉ๋œ RDBMS์—์„œ isolation level๋งˆ๋‹ค ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š” ์ง€์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด์ „ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ•œ ํŠธ๋žœ์žญ์…˜์—์„œ ๊ฐ™์€ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ read๋ฅผ ๋‘ ๋ฒˆ ํ–ˆ์„ ๋•Œ read committed, repeatable read level์—์„œ MySQL, PostgreSQL์ด ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š” ์ง€๋ฅผ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

์˜ค๋Š˜์€ MVCC๊ฐ€ ์ ์šฉ๋œ MySQL, PostgreSQL์—์„œ ํŠธ๋žœ์žญ์…˜์ด ๊ฐ™์€ ๋ฐ์ดํ„ฐ์— write๋ฅผ ํ•˜๋Š” ๋™์ž‘์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋ฅผ ๊ฐ๊ฐ isolation level๋งˆ๋‹ค ๊ทธ๋ฆฌ๊ณ  RDBMS๋งˆ๋‹ค ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š”์ง€ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

โœ”๏ธ MVCC์—์„œ lost update ๋ฌธ์ œ

DB ์‹œ๋ฆฌ์ฆˆ ํฌ์ŠคํŒ…์—์„œ lost update์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณธ ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

lost update๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ด์œ ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๋‘ ํŠธ๋žœ์žญ์…˜์ด ๊ฐ™์€ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด write ์ž‘์—…์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•œ ํŠธ๋žœ์žญ์…˜์ด updateํ•œ ๊ฒฐ๊ณผ๊ฐ€ ์‚ฌ๋ผ์ง€๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

x=50, y=10

tx1. x๊ฐ€ y์— 40์„ ์ด์ฒดํ•œ๋‹ค. read committed

tx2. x์— 30์„ ์ž…๊ธˆํ•œ๋‹ค. read commited

read(tx1,x) - write(tx1,x=10) - read(tx2,x) - write(tx2,x=80)(block) - read(tx1,y) - write(tx1,y=50) - commit1 - write(tx2,x=80) - commit2

tx1์—์„œ x์˜ ๊ฐ’์„ ์ฝ์Šต๋‹ˆ๋‹ค. commit๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์•ผ ํ•˜๋‹ˆ๊นŒ x=50์„ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  40์„ ๋นผ์•ผํ•˜๋‹ˆ๊นŒ x=10 write ์ž‘์—…์„ ํ•ฉ๋‹ˆ๋‹ค. ๋‹น์—ฐํžˆ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์ „์— write-lock์„ ํš๋“ํ•ฉ๋‹ˆ๋‹ค.

tx1์—์„œ x=10 write ์ž‘์—…์€ ๋ฐ”๋กœ ๋””์Šคํฌ์— ์ ์šฉ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ snapshot์œผ๋กœ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์ด ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

tx2์—์„œ x์˜ ๊ฐ’์„ ์ฝ์Šต๋‹ˆ๋‹ค. commit๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์•ผ ํ•˜๋‹ˆ๊นŒ x=50์„ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  30์„ ๋”ํ•ด์•ผํ•˜๋‹ˆ๊นŒ x=80 write ์ž‘์—…์„ ํ•˜๋ ค๊ณ  ํ•˜์ง€๋งŒ, ์ด๋ฏธ tx1์ด x์— ๋Œ€ํ•ด write-lock์„ ์ ์œ ํ•˜๊ณ  ์žˆ๋Š” ์ƒํƒœ์ด๊ธฐ ๋•Œ๋ฌธ์— write-lock์„ ํš๋“ํ•˜์ง€ ๋ชปํ•˜๊ณ  block ๋ฉ๋‹ˆ๋‹ค.

tx1์€ ๊ณ„์† ์ง„ํ–‰ํ•ด์„œ y=10์„ ์ฝ๊ณ  y์— 40์„ ๋”ํ•ด y=50 write๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด์„œ y์— ๋Œ€ํ•œ write-lock์„ ํš๋“ํ•˜๊ณ  write๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ snapshot์œผ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  tx1์€ commit์„ ํ•ฉ๋‹ˆ๋‹ค.

์ด ํ›„์—์•ผ tx2๋Š” x์— ๋Œ€ํ•œ write-lock์„ ํš๋“ํ•˜์—ฌ x=80์„ snapshot์œผ๋กœ ์ €์žฅํ•˜๊ณ  commitํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ๊ฒฐ๊ณผ์ ์œผ๋กœ tx1์ด ์ €์žฅํ•œ x=10, y=50์ด ์•„๋‹Œ x=80, y=50์ด ๋””์Šคํฌ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.

tx1์ด ์ง„ํ–‰ํ•œ update๊ฐ€ ์‚ฌ๋ผ์ง„ lost update๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”?

๐Ÿ“ŒPostgreSQL์—์„œ lost update ํ•ด๊ฒฐ

๐Ÿ‘‰tx2์˜ isolation level์„ repeatable read๋กœ ๋ณ€๊ฒฝ

tx2์˜ isolation level์„ repeatable read๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

์•ž์˜ schedule๊ณผ ๋˜‘๊ฐ™์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ tx1์ด ๋จผ์ € commitํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— tx2๊ฐ€ write(x=80)์„ ํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์‹คํŒจํ•˜๊ณ  tx2๋Š” rollback๋ฉ๋‹ˆ๋‹ค.

์™œ๋ƒํ•˜๋ฉด PostgreSQL์—์„œ๋Š” ๊ฐ™์€ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด์„œ ๋จผ์ € updateํ•œ ํŠธ๋žœ์žญ์…˜์ด commit ๋˜๋ฉด ๋‚˜์ค‘ ํŠธ๋žœ์žญ์…˜์€ rollbackํ•˜๋Š” ๊ทœ์น™์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ํŠน์ง•์„ first-updater-win์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

๋จผ์ € updateํ•œ ํŠธ๋žœ์žญ์…˜์ด rollbackํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ์ƒ๊ด€ ์—†์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๊ฒฐ๊ตญ tx1๋งŒ ์„ฑ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— x=10, y=50์ด ๋””์Šคํฌ์— ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

๐Ÿšจ๊ทธ๋Ÿผ tx1์€ read committed๋กœ ๋‘ฌ๋„ ๊ดœ์ฐฎ์„๊นŒ์š”?

๊ฒฐ๋ก ๋งŒ ๋งํ•˜๋ฉด ์•„๋‹™๋‹ˆ๋‹ค. tx1๋„ repeatable read๋กœ ๋ฐ”๊ฟ”์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

tx2๊ฐ€ ๋จผ์ € ์‹œ์ž‘ํ•˜๋Š” schedule์„ ๊ฐ€์ •ํ•ด๋ด…์‹œ๋‹ค. ํ˜„์žฌ tx1์€ read committed์ž…๋‹ˆ๋‹ค.

read(tx2,x) - read(tx1,x) - write(tx2,x=80) - write(tx1,x=10)(block) - commit2 - write(tx1,x=10) - read(tx1,y) - write(tx1,y=50) - commit1

read committed์—์„œ๋Š” ๋จผ์ € updateํ•œ ํŠธ๋žœ์žญ์…˜์ด commit์‹œ ๋‚˜์ค‘ ํŠธ๋žœ์žญ์…˜์€ rollbackํ•œ๋‹ค๋Š” ๊ทœ์น™์ด ์ ์šฉ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋˜‘๊ฐ™์ด lost update๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฆ‰, PostgreSQL์—์„œ lost update๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ด€๋ จ์žˆ๋Š” ํŠธ๋žœ์žญ์…˜์˜ isolation level์„ ๋ชจ๋‘ repeatable read๋กœ ๋ฐ”๊ฟ”์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ŒMySQL์—์„œ lost update ํ•ด๊ฒฐ

MySQL์—์„œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ MVCC๊ฐ€ ์ ์šฉ๋˜์—ˆ๋”๋ผ๋„ read committed level์ผ ๋•Œ lost update๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

MySQL๋„ PostgreSQL์ฒ˜๋Ÿผ ๋‘ ํŠธ๋žœ์žญ์…˜์„ ๋ชจ๋‘ repeatable read๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด ํ•ด๊ฒฐ๋ ๊นŒ์š”?

์•ˆํƒ€๊น๊ฒŒ๋„ MySQL์—์„œ๋Š” repeatable read์— first-updater-win์ด๋ผ๋Š” ๊ฐœ๋…์ด ์—†์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ repeatable read๋กœ ๋ณ€๊ฒฝํ•ด๋„ ํŠธ๋žœ์žญ์…˜์€ ์‹คํŒจํ•˜์ง€ ์•Š๊ณ  ์ญˆ์šฑ ์ง„ํ–‰ํ•ด lost update๊ฐ€ ๊ฒฐ๊ตญ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, MySQL์˜ MVCC๋Š” lost update๋ฅผ repeatable read๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” lost update๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ• ๊นŒ์š”?

๋‘ ํŠธ๋žœ์žญ์…˜์€ ์ง€๊ธˆ repeatable read level์ž…๋‹ˆ๋‹ค.

read(tx2,x) - read(tx1,x)(block) - write(tx2,x=80) - commit2 - read(tx1,x) - write(tx1,x=10) - read(tx1,y) - write(tx1,y=50) - commit1

MySQL์—์„œ๋Š” read ์ž‘์—…์„ ํ•  ๋•Œ lock์„ ์ทจ๋“ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ MySQL์—์„œ๋Š” locking read๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ tx1, tx2 ๋ชจ๋‘ x์— ๋Œ€ํ•œ read ์ž‘์—…์„ ํ•  ๋•Œ write-lock์„ ์ทจ๋“ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

read ์ž‘์—…์„ ํ•  ๋•Œ wirte-lock์„ ์ทจ๋“ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” select SQL๋ฌธ์— FOR UPDATE๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด read๋ฅผ ํ•˜๋”๋ผ๋„ ํ•ด๋‹น ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ write-lock์„ ์ทจ๋“ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ schedule์„ ์‚ดํŽด๋ณด๋ฉด tx2๊ฐ€ x๋ฅผ ์ฝ๊ธฐ ์œ„ํ•ด write-lock์„ ์ทจ๋“ํ•˜๊ณ  read๋ฅผ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.

tx1๋„ x๋ฅผ ์ฝ๊ธฐ ์œ„ํ•ด write-lock์„ ์ทจ๋“ํ•˜๋ ค ํ•˜์ง€๋งŒ ์ด๋ฏธ tx2๊ฐ€ ์ ์œ ํ•˜๊ณ  ์žˆ์–ด์„œ ์ทจ๋“ํ•  ์ˆ˜ ์—†๊ณ  ๊ธฐ๋‹ค๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค.

tx2๋Š” ๊ณ„์† ์ง„ํ–‰ํ•ด์„œ x=80์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๊ณ  commitํ•ฉ๋‹ˆ๋‹ค.

tx1์€ ์ด์ œ lock์„ ํš๋“ํ•  ์ˆ˜ ์žˆ๊ณ  read ์ž‘์—…์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ tx1์€ repeatable read level์ด๋ผ์„œ ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘ํ•œ ์‹œ์ ์—์„œ ๊ฐ€์žฅ ์ตœ๊ทผ์— commit๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ x=50์„ ์ฝ์–ด์•ผ ์ •์ƒ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ x=80์„ ์ฝ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” locking read๋Š” ๊ฐ€์žฅ ์ตœ๊ทผ์— commit๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ธฐ ๋•Œ๋ฌธ์— read ์‹œ์ ์—์„œ ๊ฐ€์ • ์ตœ๊ทผ์— commit๋œ ๋ฐ์ดํ„ฐ๋Š” tx2๊ฐ€ commitํ•œ ๋ฐ์ดํ„ฐ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

MVCC๊ฐ€ ์ ์šฉ๋œ MySQL์˜ locking read๋Š” isolation level๊ณผ ์ƒ๊ด€ ์—†์ด read๋ฅผ ์ด๋ ‡๊ฒŒ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๊ผญ ๊ธฐ์–ตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ ํ›„์— tx1๊ฐ€ x์— 40์„ ๋นผ์•ผํ•˜๋ฏ€๋กœ x=50 write ์ž‘์—…์„ ํ•˜๊ณ  read(y) ์ž‘์—…์„ ํ•˜๋Š”๋ฐ ์ด ๋•Œ๋„ locking read๋กœ ์ฝ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์ด ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  write ์ž‘์—…์„ ํ•  ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์–ด์ฐŒ๋๋“  ์ด๋ ‡๊ฒŒ ์ง„ํ–‰์ด ๋˜๋ฉด ๊ฒฐ๊ตญ x=40, y=50์œผ๋กœ ์ •์ƒ์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ๋””์Šคํฌ์— ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๊ฒฐ๋ก ์ ์œผ๋กœ MySQL์—์„œ๋Š” repeatable read๋ฅผ ์“ฐ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ํ•ด๊ฒฐ๋˜์ง€ ์•Š๊ณ  locking read๋ฅผ ์‚ฌ์šฉํ•ด์•ผ lost update๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  serializable level์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด repeatable level ๋งŒ์œผ๋กœ๋„ ๊ฐ€๋Šฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

PostgreSQL์—์„œ ์„œ๋กœ ๋‹ค๋ฅธ ๋‘ ํŠธ๋žœ์žญ์…˜ ์ค‘ ํ•˜๋‚˜์˜ ํŠธ๋žœ์žญ์…˜๋งŒ repeatable read๋กœ ๋ฐ”๊พธ๋Š” ๊ฒƒ์ด ์•„๋‹Œ ๊ด€๋ จ์žˆ๋Š” ํŠธ๋žœ์žญ์…˜๋„ repeatable read๋ฅผ ์‚ฌ์šฉํ•ด์•ผ lost update๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ MySQL์—์„œ๋„ ๊ด€๋ จ์žˆ๋Š” ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜์—์„œ๋„ ๊ฐ™์€ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด locking read(write-lock)์„ ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ‘‰locking read ์ข…๋ฅ˜

MySQL์— locking read๋„ ์ข…๋ฅ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

select ... for update;
select ... for share;

for update์˜ ๊ฒฝ์šฐ์—๋Š” exclusive lock์„ ํš๋“ํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๊ณ , for share์˜ ๊ฒฝ์šฐ์—๋Š” shared lock์„ ํš๋“ํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฐ for update, for share ๊ฐ™์€ ๋ฌธ๋ฒ•์€ MySQL ๋ง๊ณ  PostgreSQL์—์„œ๋„ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋™์ž‘๋ฐฉ์‹์ด ์กฐ๊ธˆ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

โœ”๏ธMVCC์—์„œ write skew ๋ฌธ์ œ

x=10, y=10

tx1. x์™€ y๋ฅผ ๋”ํ•ด์„œ x์— ์“ด๋‹ค.

tx2. x์™€ y๋ฅผ ๋”ํ•ด์„œ y์— ์“ด๋‹ค.

์ด ๋‘ ๊ฐœ์˜ ํŠธ๋žœ์žญ์…˜์ด ๊ฐ๊ฐ ๋™์ž‘ํ•˜๋„๋ก ์ˆ˜ํ–‰๋˜๋ฉด ์ •์ƒ์ ์ธ ๊ฒฐ๊ณผ๋Š” x=20,y=30 ํ˜น์€ x=30,y=20์ด ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ MVCC์—์„œ repeatable read level๋กœ ํŠธ๋žœ์žญ์…˜์ด ๋™์ž‘ํ•˜๊ฒŒ ๋˜๋ฉด ์ด์ƒํ•œ ํ˜„์ƒ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ schedule๋กœ ์ง„ํ–‰๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ด…์‹œ๋‹ค

read(tx1,x) - read(tx2,x) - read(tx1,y) - read(tx2,y) - write(tx1,x=20) - write(tx2,y=20) - commit1 - commit2

์ˆœ์„œ๋Œ€๋กœ ๋”ฐ๋ผ๊ฐ€๋ณด๋ฉด ๊ฒฐ๊ณผ๊ฐ€ x=20, y=20์ด ๋””์Šคํฌ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ •์ƒ์ ์ธ ๊ฒฐ๊ณผ๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค๋ฉด์„œ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์ด ๊นจ์กŒ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์„œ๋กœ ๊ด€๋ จ์žˆ๋Š” ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด ์“ฐ๊ธฐ ์ž‘์—…์„ ํ•˜๊ณ  ๋‚˜์„œ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์ด ๊นจ์ง€๋Š” ํ˜„์ƒ์„ write skew๋ผ๊ณ  ํ•œ๋‹ค๊ณ  ์ด์ „ ํฌ์ŠคํŒ…์—์„œ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๐Ÿ“ŒMySQL์—์„œ write skew ํ•ด๊ฒฐ

์•„๊นŒ lost update๋ฅผ ํ•ด๊ฒฐํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ locking read๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

read(tx1,x) - read(tx2,x)(block) - read(tx1,y) - write(tx1,x=20) - commit1 - read(tx2,x) - read(tx2,y) - write(tx2,y=20) - commit2

tx1์—์„œ x๋ฅผ ์ฝ๊ธฐ ์œ„ํ•ด locking read๋กœ write-lock์„ ์ทจ๋“ํ•œ ํ›„ ์ฝ์Šต๋‹ˆ๋‹ค. tx2์—์„œ๋„ x์— ๋Œ€ํ•ด์„œ readํ•˜๋ ค๊ณ  ํ•˜๋ฉด lock์„ ํš๋“ํ•˜์ง€ ๋ชปํ•ด์„œ ๊ธฐ๋‹ค๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ tx2๋Š” block๋ฉ๋‹ˆ๋‹ค.

tx1์€ ๋งˆ์ € ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค. tx1์ด y๋ฅผ ์ฝ์œผ๋ ค๊ณ  ํ•  ๋•Œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ locking read๋กœ y์— ๋Œ€ํ•œ write-lock์„ ์ทจ๋“ํ•˜๊ณ  read ์ž‘์—…์„ ํ•ฉ๋‹ˆ๋‹ค.

tx1์ด x=20 write ์ž‘์—…์„ ํ•˜๊ณ  commit์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

tx2๋Š” ์ด์ œ x์— ๋Œ€ํ•œ write-lock์„ ์ฅ˜ ์ˆ˜ ์žˆ๊ฒŒ ๋˜๊ณ  read(x)๋ฅผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์•ž์—์„œ ๋งํ–ˆ๋“ฏ์ด repeatable read์—์„œ locking read๋กœ ๋™์ž‘ํ•˜๋Š” ๊ฒฝ์šฐ read ์‹œ์ ์—์„œ ๊ฐ€์žฅ ์ตœ๊ทผ์— commit๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ธฐ ๋•Œ๋ฌธ์— x=10์ด ์•„๋‹Œ x=20์„ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค.

tx2๊ฐ€ y๋ฅผ ์ฝ์„ ๋•Œ๋„ locking read๋ฅผ ์ง„ํ–‰ํ•˜๊ณ  y=20 write ์ž‘์—…๊นŒ์ง€ ๋งˆ์นœ ํ›„ ํŠธ๋žœ์žญ์…˜์ด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.

x=20,y=30์œผ๋กœ ์ •์ƒ์ ์ธ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ MySQL์—์„œ write skew๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด lost update์ฒ˜๋Ÿผ locking read๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด write-skew๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“ŒPostgreSQL์—์„œ write skew ํ•ด๊ฒฐ

PostgreSQL์—์„œ๋„ for update, for share ๋ฌธ๋ฒ•์ด ์กด์žฌํ•œ๋‹ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ PostgreSQL์—๋„ ์ด ๋ฐฉ๋ฒ•์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ MySQL๊ณผ ๋™์ž‘๋ฐฉ์‹์ด ์ข€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

read(tx1,x) - read(tx2,x)(block) - read(tx1,y) - write(tx1,x=20) - commit1 - read(tx2,x) - read(tx2,y) - write(tx2,y=20) - commit2

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๋˜‘๊ฐ™์€ schedule์ž…๋‹ˆ๋‹ค.

PostgreSQL์—์„œ๋„ for update๋ฅผ ํ†ตํ•ด์„œ ์ฝ๊ธฐ ์ž‘์—…์„ ํ•˜๊ธฐ ์œ„ํ•ด write-lock์„ ํš๋“ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

PostgreSQL์—์„œ๋Š” repeatable read leve์ผ ๋•Œ ๋จผ์ € updateํ•œ ํŠธ๋žœ์žญ์…˜์ด commit ๋˜๋ฉด ๋‚˜์ค‘ tx๋Š” rollback ๋œ๋‹ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค.(first-updater-win)

๋”ฐ๋ผ์„œ commit1 ์ดํ›„ tx2๊ฐ€ read(x)ํ•˜๋Š” ์ž‘์—…์€ ์‹คํŒจํ•˜๊ณ  tx2๋Š” rollbackํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๊ฒฐ๊ตญ ์ตœ์ข… ๊ฒฐ๊ณผ๋Š” x=20,y=10์œผ๋กœ ๋‚จ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์–ด์ฐŒ๋๋“  tx2๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„ ๊ฒฐ๊ณผ๋Š” ์ •์ƒ์ ์ธ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‹คํŒจํ•œ tx2๋Š” ๋‚˜์ค‘์— ๋‹ค์‹œ ์‹คํ–‰์„ ํ•ด์ฃผ๋˜๊ฐ€ ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

for update, for share ๋‘˜ ๋‹ค first-updater-win ๊ทœ์น™์ด repeatable read์—์„œ ์ ์šฉ์ด ๋ฉ๋‹ˆ๋‹ค.

์ด๋ฒˆ์—” for share๋ฅผ ์‚ฌ์šฉํ•ด์„œ read-lock์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์— ๋Œ€ํ•ด์„œ ๊ฐ„๋‹จํžˆ ์‚ดํŽด๋ด…์‹œ๋‹ค.

write(tx1,x=20) - read(tx2,x)(block) - ... - commit1 - read(tx2,x)(abort) - rollback

tx1์ด x์— ๋Œ€ํ•ด wrtie ์ž‘์—…์„ ํ•œ ์ดํ›„์— tx2๊ฐ€ x์— ๋Œ€ํ•ด read ์ž‘์—…์„ ์œ„ํ•ด read-lock์„ ํš๋“ํ•˜๋ ค๊ณ  ํ•˜๋ฉด tx2๋Š” block ๋ฉ๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ MVCC๋Š” write-write operation์— ๋Œ€ํ•ด์„œ๋งŒ block์ด ์ด๋ฃจ์–ด์ง€์ง€๋งŒ, read-lock์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ๋„ ์ƒ๊ธฐ๋Š”๋ฐ ์ด ๋•Œ for share ๋ฌธ๋ฒ•์œผ๋กœ read-lock์„ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋ฉด read-read๋งŒ ํ—ˆ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด ๋™์ž‘ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ tx2๋Š” block๋˜๊ณ  tx1์ด ์ง„ํ–‰๋˜๋‹ค commit์ด ์ด๋ฃจ์–ด์ง€๋ฉด, tx2๋Š” x์— ๋Œ€ํ•œ read-lock์„ ์ฅ๊ณ  read(x)๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ first-updater-win ๊ทœ์น™ ๋•Œ๋ฌธ์— read ์ž‘์—…์€ ์‹คํŒจํ•˜๊ณ  tx2๋Š” rollbackํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๐Ÿ“Œrepeatable read level์—์„œ write-skew ๊ฒฐ๋ก 

select...for update;
select...for share;

์œ„ ๋ฌธ๋ฒ•์œผ๋กœ repeatable read level์ผ ๋•Œ๋„ write skewํ˜„์ƒ์„ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฐฉ๋ฒ•์€ MySQL, PostgreSQL์—์„œ ๋ชจ๋‘ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๋‘˜์˜ ๋™์ž‘ ๋ฐฉ์‹์— ์•ฝ๊ฐ„ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œserializable level๋กœ ์˜ฌ๋ ค์„œ write-skew ํ•ด๊ฒฐ

๐Ÿ‘‰serializable

tx1์ด serializable๋กœ ๋™์ž‘ํ•  ๋•Œ ๊ฒฐ๊ณผ๋Š” repeatable read์ผ ๋•Œ์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ serializable level์ผ ๋•Œ MySQL๊ณผ PostgreSQL ๋™์ž‘์—์„œ ์ฐจ์ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๐ŸšจMySQL

MVCC๋กœ ๋™์ž‘ํ•˜๊ธฐ ๋ณด๋‹ค๋Š” lock์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

repeatable read์™€ ์œ ์‚ฌํ•˜๊ฒŒ ๋™์ž‘ํ•˜์ง€๋งŒ, serializable level ํŠธ๋žœ์žญ์…˜์˜ ๋ชจ๋“  ํ‰๋ฒ”ํ•œ select ๋ฌธ์€ ์•”๋ฌต์ ์œผ๋กœ select ... for share ์ฒ˜๋Ÿผ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ์ž๊ฐ€ ํ‰๋ฒ”ํ•œ select๋ฌธ์„ ์ผ๋‹ค ํ•˜๋”๋ผ๋„ DBMS๊ฐ€ ์•Œ์•„์„œ for share๋ฅผ ๋ถ™์—ฌ์ฃผ๋Š” ์˜๋ฏธ๋‹ค.

exclusive lock์ด ์•„๋‹Œ shared lock์ด๊ธฐ ๋•Œ๋ฌธ์— deadlock์ด ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋” ๋†’์Šต๋‹ˆ๋‹ค.

๐ŸšจPostgreSQL

Serializable Snapshot Isolation ๊ธฐ๋ฒ•์ด ์ ์šฉ๋œ MVCC๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. MVCC๋กœ ๋™์ž‘ํ•˜๋ฉด์„œ๋„ ๋ชจ๋“  ์ด์ƒํ˜„์ƒ์„ ๋ง‰์•„์ฃผ๋Š” isolation ๊ธฐ๋ฒ•์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ถ€๋ถ„์€ ์ถ”๊ฐ€์ ์œผ๋กœ ๊ณต๋ถ€๊ฐ€ ํ•„์š”ํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

profile
๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž

0๊ฐœ์˜ ๋Œ“๊ธ€