ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

๐Ÿ‘€ Overview

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

๊ทธ๋ž˜์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ํ–‰์‚ฌ ๊ฐœ์ตœ์ž๊ฐ€ ์ตœ๋Œ€ ์ •์›(์•„๋ž˜๋Š” 100๋ช…)์„ ์„ค์ •ํ•ด ๋†“์œผ๋ฉด, ์˜ˆ๋งค ์žฅ์ˆ˜๋ฅผ ์„ ํƒํ•ด ์‹ ์ฒญ์„ ํ•ด์•ผ ํ–ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ •์›์ด ์—†๋Š” ํ–‰์‚ฌ์ธ ๊ฒฝ์šฐ ์ตœ๋Œ€ 999๋ช…๊นŒ์ง€ ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ์ •์ฑ…์„ ์„ค์ •ํ–ˆ์—ˆ๋‹ค.

์ด๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ์ ์ด ๋™์‹œ์„ฑ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋”ฐ๋ผ์„œ ํ–‰์‚ฌ๋ฅผ ์‹ ์ฒญํ•˜๊ณ  ์ทจ์†Œํ• ๋•Œ ๋™์‹œ์„ฑ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก, ๋น„๊ด€์  ๋ฝ(Pessimistic lock)์„ ์‚ฌ์šฉํ•ด ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์˜€๋‹ค.

๊ทธ๋ฆฌ๊ณ  api ํ˜ธ์ถœ ์‹œ ์‹ ์ฒญ์ด ๋ถˆ๊ฐ€ํ•œ ๊ฒฝ์šฐ ๋ฐœ์ƒํ•œ ์Šคํ”„๋ง ์˜ˆ์™ธ๋ฅผ ๋ณ€ํ™˜ํ•ด์„œ ํ”„๋ก ํŠธ์—๊ฒŒ ์ „๋‹ฌํ•ด ์ฃผ์—ˆ๋‹ค.

 

ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๋•Œ๋Š”, ์ผ๋‹จ lock์˜ ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ, JPA์˜ ๋น„๊ด€์  ๋ฝ์„ ํ†ตํ•ด์„œ DB lock์„ ๊ฐ„๋‹จํžˆ ๊ฑธ ์ˆ˜ ์žˆ๊ณ , DB์˜ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•จ์— ์žˆ์–ด ๋™์‹œ์„ฑ ๋ณด์žฅ์ด ํ™•์‹คํžˆ ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉํ•˜์˜€๋‹ค.

 

ํ•˜์ง€๋งŒ ์ฐพ์•„๋ณด๋‹ˆ, ๋น„๊ด€์  ๋ฝ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ์ฝ๊ธฐ๊ฐ€ ๋นˆ๋ฒˆ ํ•  ๊ฒฝ์šฐ ์„ฑ๋Šฅ ์ €ํ•˜์˜ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  DB์™€ JPA์—์„œ ์ œ๊ณตํ•˜๋Š” lock์˜ ์ข…๋ฅ˜๋กœ๋Š” ๋ฌด์—‡์ด ์žˆ๊ณ , ์–ด๋– ๋ž€ lock์„ ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ์ด ์ข‹์€ ์„ ํƒ์ธ์ง€์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์ด ์ƒ๊ฒผ๋‹ค.

 

DB Lock์„ ์•Œ์•„๋ณด์ž!

1. ํŠธ๋žœ์žญ์…˜๊ณผ Lock์˜ ์ฐจ์ด์ !

๋จผ์ € DB์—์„œ ์ œ๊ณตํ•˜๋Š” Lock์€ ๋™์‹œ์„ฑ์„ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋Šฅ์ด๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ํŠธ๋žœ์žญ์…˜์€? ํŠธ๋žœ์žญ์…˜์€ ๋ฐ์ดํ„ฐ์˜ ์ •ํ•ฉ์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋Šฅ์ด๋‹ค. ํŠธ๋žœ์žญ์…˜์˜ Isolation level์„ ์„ค์ •ํ•˜๋Š”๋ฐ ์žˆ์–ด์„œ DB lock์„ ์‚ฌ์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ์˜ ์ •ํ•ฉ์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค.

ํŠธ๋žœ์žญ์…˜์˜ ACID ์›์น™์— ์žˆ์–ด Atomicity ์›์น™์œผ๋กœ ์ธํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์—์„œ ๊ณ ๋ฏผํ•ด์•ผ ํ•  ๋ฌธ์ œ๋ฅผ ์ค„์—ฌ์ฃผ๋Š” ์•„์ฃผ ํ•„์ˆ˜์ ์ธ DBMS ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. Partial Update๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์‹คํŒจํ•œ ์ฟผ๋ฆฌ๋กœ ์ธํ•ด ๋‚จ์€ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ์„ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ผ์ผ์ด ์ฑ™๊ฒจ์ค˜์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

 

2. MySQL ์—”์ง„์˜ Lock์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž!

MySQL ์—”์ง„๊ณผ InnoDB ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„์˜ ์ฐจ์ด๋ฅผ ๋ชจ๋ฅธ๋‹ค๋ฉด ์—ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•˜์ž.

MySQL ์—”์ง„ ๋ ˆ๋ฒจ Lock์€ ๋ชจ๋“  ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€๋งŒ ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„ ๋ ˆ๋ฒจ์˜ Lock์€ ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„ ๊ฐ„ ์ƒํ˜ธ ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€๋Š” ์•Š๋Š”๋‹ค.

 

(1) ๊ธ€๋กœ๋ฒŒ ๋ฝ (Global Lock)

FLUSH TABLES WITH READ LOCK ๋ช…๋ น์œผ๋กœ ํš๋“ ๊ฐ€๋Šฅํ•˜๋ฉฐ MySQL์—์„œ ์ œ๊ณตํ•˜๋Š” lock ๊ฐ€์šด๋ฐ ๊ฐ€์žฅ ๋ฒ”์œ„๊ฐ€ ํฌ๋‹ค.

MySQL ์„œ๋ฒ„ ์ „์ฒด์— lock์„ ๊ฑฐ๋Š” ๊ฒƒ์ด๋ฉฐ ์‹คํ–‰๊ณผ ๋™์‹œ์— MySQL ์„œ๋ฒ„์— ์กด์žฌํ•˜๋Š” ๋ชจ๋“  ํ…Œ์ด๋ธ”์„ ๋‹ซ๊ณ  lock์„ ๊ฑด๋‹ค. 

FLUSH TABLES WITH READ LOCK ์€ ํ…Œ์ด๋ธ” ์ฝ๊ธฐ ์ž ๊ธˆ ๊ฑธ๊ธฐ ์ „์— ๋จผ์ œ ํ…Œ์ด๋ธ”์„ ํ”Œ๋Ÿฌ์‹œ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ด๋ธ”์— ์‹คํ–‰์ค‘์ธ ๋ชจ๋“  ์ข…๋ฅ˜์˜ ์ฟผ๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋˜์–ด์•ผ ํ•œ๋‹ค.

๊ธ€๋กœ๋ฒŒ ๋ฝ์„ ํ†ตํ•ด ๋ฐฑ์—… ๋˜๋Š” ๋ฐ์ดํ„ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ž‘์—… ์™„๋ฃŒ ํ›„์—๋Š” UNLOCK TABLES ์‚ฌ์šฉํ•ด์„œ Lock์„ ํ•ด์ œํ•ด์•ผํ•œ๋‹ค.

์‚ฌ์šฉ์˜ˆ์‹œ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

START TRANSACTION;
FLUSH TABLES WITH READ LOCK;
mysqldump --all-databases > backup.sql;
UNLOCK TABLES;
COMMIT;

์–ธ๊ธ‰ํ–ˆ๋‹ค์‹œํ”ผ, ๋ชจ๋“  ํ…Œ์ด๋ธ”์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์น˜๊ธฐ์— ์›น์„œ๋น„์Šค์šฉ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” MySQL ์„œ๋ฒ„์—์„œ๋Š” ๊ฐ€๊ธ‰์  ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

 

(2) ํ…Œ์ด๋ธ” ๋ฝ (Table Lock)

ํ…Œ์ด๋ธ” ๋ฝ์€ ๊ฐœ๋ณ„ ํ…Œ์ด๋ธ” ๋‹จ์œ„๋กœ ์„ค์ •๋˜๋Š” ๋ฝ์ด๋‹ค.

LOCK TABLES table_name [ READ | WRITE ] ์‚ฌ์šฉํ•ด์„œ ๋ฝ ํš๋“์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์ด ๋ช…์‹œ์ ์œผ๋กœ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•œ๋ฐ, ํ…Œ์ด๋ธ” ๋ฝ๋„ ํŠน๋ณ„ํ•œ ์ƒํ™ฉ์ด ์•„๋‹ˆ๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ๊ฑฐ์˜ ์—†๋‹ค๊ณ  ํ•œ๋‹ค.

LOCK TABLES users WRITE;
# ์ž‘์—… ์ˆ˜ํ–‰
UNLOCK TABLES;

๋ช…์‹œ์ ์œผ๋กœ ํ…Œ์ด๋ธ”์„ ์ž ๊ทธ๋Š” ์ž‘์—…์€ ๊ธ€๋กœ๋ฒŒ ๋ฝ๊ณผ ๊ฐ™์ด ์˜จ๋ผ์ธ ์ž‘์—…์— ์ƒ๋‹นํ•œ ์˜ํ–ฅ์„ ๋ฏธ์น˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋ฌต์‹œ์ ์œผ๋กœ ํ…Œ์ด๋ธ”๋ฝ์€ MyISAM, MEMORY ํ…Œ์ด๋ธ”์— ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝํ•˜๋Š” ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋ฐœ์ƒํ•œ๋‹ค. ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜๋Š” ๋™์•ˆ ์ž๋™์œผ๋กœ ํš๋“๋˜์—ˆ๋‹ค๊ฐ€ ์ฟผ๋ฆฌ ์™„๋ฃŒ ํ›„ ์ž๋™ ํ•ด์ œ ๋œ๋‹ค.

ํ•˜์ง€๋งŒ InnoDB ํ…Œ์ด๋ธ” ๊ฒฝ์šฐ, ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„ ๋ ˆ๋ฒจ์—์„œ ๋ ˆ์ฝ”๋“œ ๊ธฐ๋ฐ˜ lock์„ ์ œ๊ณตํ•˜๊ธฐ์— ๋ฌต์‹œ์ ์ธ ํ…Œ์ด๋ธ” ๋ฝ ์„ค์ •๋˜์ง€ ์•Š๋Š”๋‹ค. (๋ช…ํ™•ํžˆ๋Š”  ํ…Œ์ด๋ธ” ๋ฝ์ด ๊ฑธ๋ฆฌ์ง€๋งŒ, DML ์ฟผ๋ฆฌ๋Š” ๋ฌด์‹œ๋˜๊ณ  DDL ๊ฒฝ์šฐ์—๋งŒ ์˜ํ–ฅ์„ ๋ฏธ์นจ)

 

(3) ๋„ค์ž„๋“œ ๋ฝ (Named Lock)

GET_LOCK() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด์„œ ์ž„์˜์˜ ๋ฌธ์ž์—ด์— ๋Œ€ํ•ด ์ž ๊ธˆ ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

๊ทธ๋ƒฅ ๋‹จ์ˆœํžˆ ์‚ฌ์šฉ์ž๊ฐ€ ์ง€์ •ํ•œ ๋ฌธ์ž์—ด์— ๋Œ€ํ•ด ํš๋“ํ•˜๊ณ  ๋ฐ˜๋‚ฉํ•˜๋Š” lock์ด๋‹ค.

์ด๋Š” ๋‚˜์ค‘์— ๋‹ค๋ฃฐ ๋ถ„์‚ฐ๋ฝ ๊ตฌํ˜„์— ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค. ๋ถ„์‚ฐ๋ฝ์ด๋ž€ ์—ฌ๋Ÿฌ์„œ๋ฒ„์—์„œ ๊ณต์œ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ์ˆ ์ธ๋ฐ, DB์„œ๋ฒ„๊ฐ€ 1๋Œ€์ธ๋ฐ ๋น„ํ•ด ์„œ๋ฒ„๊ฐ€ ์—ฌ๋Ÿฌ๋Œ€๋กœ ์„œ๋น„์Šคํ•˜๋Š” ์ƒํ™ฉ์—์„œ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™” ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” lock์„ ๋งํ•œ๋‹ค.

 

select get_lock(key, timeout)์„ ํ†ตํ•ด lock์„ ํš๋“ ํ•  ์ˆ˜ ์žˆ๋‹ค.

select release_lock(key)์„ ํ†ตํ•ด์„œ lock์„ ํ•ด์ œ ํ•œ๋‹ค.

 

์•„๋ž˜๋Š” ๋‚ด๊ฐ€ ๋™์‹œ์„ฑ์„ ํ•™์Šตํ•˜๋ฉด์„œ ์ž‘์„ฑํ•œ ์ฝ”๋“œ์ธ๋ฐ, ์‚ฌ์šฉ๋ฐฉ์‹์˜ ์˜ˆ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

์‹ค๋ฌด์—์„œ๋Š” lock์„ ํš๋“ํ•˜๊ณ  ๋ฐ˜๋‚ฉํ•˜๋Š”๋ฐ ์žˆ์–ด connection ๊ณ ๊ฐˆ์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๊ธฐ์— datasource๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š”๊ฒŒ ์ข‹๋‹ค๊ณ  ํ•œ๋‹ค.

 

๋จผ์ € named lock์„ ํš๋“ํ•˜๊ณ  ๋ฐ˜๋‚ฉ์„ ๋‹ด๋‹นํ•˜๋Š” repository๋ฅผ JdbcTemplate์„ ์ด์šฉํ•ด ์ƒ์„ฑํ•ด ์ฃผ์—ˆ๋‹ค. 

๊ทธ ์ด์œ ๋Š” ํ•ด๋‹น lock ์„ค์ •์€ ํŠน์ • entity์— ์ข…์†๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— Spring Data JPA๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ ๋ณด๋‹ค JdbcTemplate์„ ์‚ฌ์šฉํ•˜์˜€๋‹ค.

@Repository
public class NamedLockRepository {

    private final JdbcTemplate jdbcTemplate;

    public NamedLockRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void getLock(String key) {
        jdbcTemplate.execute("SELECT GET_LOCK('" + key + "', 10)");
    }

    public void releaseLock(String key) {
        jdbcTemplate.execute("SELECT RELEASE_LOCK('" + key + "')");
    }

}
@Component
public class NamedLockStockFacade {

    private final LockRepository lockRepository;
    private final StockService stockService;

    public NamedLockStockFacade(LockRepository lockRepository, StockService stockService) {
        this.lockRepository = lockRepository;
        this.stockService = stockService;
    }

    @Transactional
    public void decrease(Long id, Long quantity) {
        String lockKey = id.toString();
        try{
            lockRepository.getLock(lockKey);
            // critical section
            stockService.decrease(id, quantity);
        } finally {
            lockRepository.releaseLock(lockKey);
        }
    }
    
}

 

(4) ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๋ฝ (Metadata Lock)

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ์ฒด์˜ ์ด๋ฆ„์ด๋‚˜ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒฝ์šฐ์— ํš๋“ํ•˜๋Š” lock์ด๋‹ค.

RENAME TABLE table_a TO table_b ๊ฐ™์ด ํ…Œ์ด๋ธ” ์ด๋ฆ„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒฝ์šฐ ์ž๋™์œผ๋กœ ํš๋“๋œ๋‹ค. (๋‘ ํ…Œ์ด๋ธ” ๋ชจ๋‘ lock ์„ค์ •)

 

2. InnoDB ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„์—์„œ์˜ Lock์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž!

InnoDB ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„์€ ๋ ˆ์ฝ”๋“œ ๊ธฐ๋ฐ˜์˜ ์ž ๊ธˆ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š”๋ฐ, ์ž ๊ธˆ์ •๋ณด๊ฐ€ ์ƒ๋‹นํžˆ ์ž‘์€ ๊ณต๊ฐ„์œผ๋กœ ๊ด€๋ฆฌ๋œ๋‹ค.

๋”ฐ๋ผ์„œ MyISAM ๋ณด๋‹ค ํ›จ์”ฌ ๋›ฐ์–ด๋‚œ ๋™์‹œ์„ฑ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ InnoDB์˜ ์ž ๊ธˆ์€ ๋ ˆ์ฝ”๋“œ๋ฅผ ์ž ๊ทธ๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์ธ๋ฑ์Šค๋ฅผ ์ž ๊ทธ๋Š” ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ณ€๊ฒฝํ•ด์•ผํ•˜๋Š” ๋ ˆ์ฝ”๋“œ๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด ๊ฒ€์ƒ‰ํ•œ ์ธ๋ฑ์Šค์˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ๋ฝ์„ ๊ฑธ์–ด์•ผ ํ•œ๋‹ค๋Š” ํŠน์ง•์ด ์žˆ๋‹ค

์Šคํ† ๋ฆฌ์ง€ ์—”์ง„ ์ž ๊ธˆ์€ ๋ ˆ์ฝ”๋“œ ๋ฝ๊ณผ ๋ ˆ์ฝ”๋“œ์™€ ๋ ˆ์ฝ”๋“œ ์‚ฌ์ด๋ฅผ ์ž ๊ทธ๋Š” ๊ฐญ(Gap)๋ฝ ๋“ฑ์ด ์กด์žฌํ•˜๋Š”๋ฐ ํ•˜๋‚˜์”ฉ ์•Œ์•„๊ฐ€๋ณด์ž!

 

(1) ๋ ˆ์ฝ”๋“œ ๋ฝ

๋ ˆ์ฝ”๋“œ ์ž์ฒด๋งŒ์„ ์ž ๊ทธ๋Š” ๊ฒƒ์„ ๋ ˆ์ฝ”๋“œ ๋ฝ (Record Lock)์ด๋ผ๊ณ  ํ•˜๋ฉฐ, ๋‹ค๋ฅธ ์ƒ์šฉ DBMS์˜ ๋ ˆ์ฝ”๋“œ ๋ฝ๊ณผ ๋™์ผํ•œ ์—ญํ• ์„ ํ•œ๋‹ค.

ํ•˜์ง€๋งŒ ์•ž์„œ ์–ธ๊ธ‰ํ–ˆ์ง€๋งŒ, InnoDB ์Šคํ† ๋ฆฌ์ง€ ์—”์ง„์€ ๋ ˆ์ฝ”๋“œ ์ž์ฒด๊ฐ€ ์•„๋‹Œ ์ธ๋ฑ์Šค์˜ ๋ ˆ์ฝ”๋“œ๋ฅผ ์ž ๊ทผ๋‹ค๋Š” ๊ฒƒ์ด ์ฐจ์ด์ ์ด ์žˆ๋‹ค. 

์ธ๋ฑ์Šค๊ฐ€ ์—†์œผ๋ฉด ๊ทธ๋Ÿผ ์–ด์ฉŒ๋Š”๊ฐ€? -> ์ธ๋ฑ์Šค๊ฐ€ ์—†๋”๋ผ๋„ ๋‚ด๋ถ€์ ์œผ๋กœ ์ƒ์„ฑ๋œ ํด๋Ÿฌ์Šคํ„ฐ ์ธ๋ฑ์Šค๋ฅผ ์ด์šฉํ•ด ์ž ๊ธˆ์„ ์„ค์ •ํ•œ๋‹ค.

 

๋ ˆ์ฝ”๋“œ ๋ฝ์€ ์–ด๋–ป๊ฒŒ ๊ทธ๋Ÿผ ์„ค์ •ํ•˜๋Š” ๊ฑธ๊นŒ?

UPDATE, DELETE ์ฟผ๋ฆฌ๋ฅผ ํ†ตํ•ด ๋ ˆ์ฝ”๋“œ๋ฅผ ์—…๋ฐ์ดํŠธ or ์‚ญ์ œ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋ฉด UPDATE, DELETEํ•˜๋Š” ๋™์•ˆ ๋ ˆ์ฝ”๋“œ ๋ฝ์ด ์„ค์ •๋œ๋‹ค!

๊ทธ๋Ÿผ SELECT ํ• ๋•Œ lock ์„ค์ •์€ ์–ด๋–ป๊ฒŒ ํ• ๊นŒ?

SELECT name FROM user WHERE id = 1 FOR SHARE; // ๊ณต์œ  ๋ฝ (์ฝ๊ธฐ ์ž ๊ธˆ)
SELECT name FROM user WHERE id = 1 FOR UPDATE; // ๋ฐฐํƒ€ ๋ฝ (์“ฐ๊ธฐ ์ž ๊ธˆ)

์œ„์™€ ๊ฐ™์ด shared lock (๊ณต์œ  ๋ฝ)๊ณผ exclusive lock (๋ฐฐํƒ€ ๋ฝ)์ด ์กด์žฌํ•œ๋‹ค.

์ด๋Ÿฐ shared lock๊ณผ exclusive lock์€ ๋ฉ€ํ‹ฐ ํŠธ๋žœ์žญ์…˜ ํ™˜๊ฒฝ์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ผ๊ด€์„ฑ๊ณผ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด์„œ ํŠธ๋žœ์žญ์…˜์˜ ์ˆœ์ฐจ์  ์ง„ํ–‰์„ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์น˜์™€ ๊ด€๋ จ๋˜์–ด ์žˆ๋‹ค (ํŠธ๋žœ์žญ์…˜์˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€์— ๋”ฐ๋ฅธ ๋ฐ์ดํ„ฐ ์ •ํ•ฉ์„ฑ ๋ณด์žฅ์— ์‚ฌ์šฉ)

 

๋”ฐ๋ผ์„œ ํŠธ๋žœ์žญ์…˜์ด ์‹œ์ž‘๋˜์–ด shared lock๊ณผ exclusive lock์„ ์ทจ๋“ํ•˜๊ณ , ํŠธ๋žœ์žญ์…˜์ด ์ปค๋ฐ‹๋˜๊ฑฐ๋‚˜ ๋กค๋ฐฑ๋˜์–ด ์™„๋ฃŒ๊ฐ€ ๋˜๋ฉด lock์ด ๋ฐ˜ํ™˜๋œ๋‹ค.

-- ํŠธ๋žœ์žญ์…˜ ์‹œ์ž‘
BEGIN;

-- Shared Lock ์„ค์ •
SELECT * FROM ํ…Œ์ด๋ธ” WHERE id = 1 FOR SHARE;

-- Exclusive Lock ์„ค์ •
SELECT * FROM ํ…Œ์ด๋ธ” WHERE id = 2 FOR UPDATE;

-- ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ
UPDATE ํ…Œ์ด๋ธ” SET ์ด๋ฆ„ = '์ƒˆ๋กœ์šด ์ด๋ฆ„' WHERE id = 1;

-- ํŠธ๋žœ์žญ์…˜ ๋กค๋ฐฑ or ์ปค๋ฐ‹
ROLLBACK or COMMIT;
-- Shared Lock๊ณผ Exclusive Lock ๋ชจ๋‘ ํ•ด์ œ (์ถœ์ฒ˜ : google gemini)

ํŠธ๋žœ์žญ์…˜์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, UNLOCK TABLES; ์„ ํ†ตํ•ด ๋ช…์‹œ์ ์œผ๋กœ lock์„ ํ•ด์ œํ•  ์ˆ˜ ์žˆ๋‹ค.

์ •๋ฆฌ :

- shared lock๊ณผ exclusive lock์€ ํŠธ๋žœ์žญ์…˜์ด ์ข…๋ฃŒ์‹œ์ ์— ํ•ด์ œ๋˜์ง€๋งŒ, UNLOCK TABLE ํ‚ค์›Œ๋“œ๋ฅผ ํ†ตํ•ด ๋ช…์‹œ์ ์œผ๋กœ ํ•ด์ œํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

- ๋ ˆ์ฝ”๋“œ ๋ฝ ์ทจ๋“ ๋ฐฉ๋ฒ•์€ 1๏ธโƒฃ UPDATE ์ฟผ๋ฆฌ, 2๏ธโƒฃ DELETE ์ฟผ๋ฆฌ, 3๏ธโƒฃ Shared Lock / Exclusive Lock ํš๋“ ์‹œ์— ๋ฐœ์ƒํ•œ๋‹ค.

 

 

(2) Gap Lock (๊ฐญ ๋ฝ)

๊ฐญ ๋ฝ์€ ๋ ˆ์ฝ”๋“œ ์ž์ฒด๊ฐ€ ์•„๋‹Œ ๋ ˆ์ฝ”๋“œ์™€ ๋ฐ”๋กœ ์ธ์ ‘ํ•œ ๋ ˆ์ฝ”๋“œ ์‚ฌ์ด์˜ ๊ฐ„๊ฒฉ๋งŒ์„ ์ž ๊ทธ๋Š” ๊ฒƒ์œผ๋กœ, ๋ ˆ์ฝ”๋“œ์™€ ๋ ˆ์ฝ”๋“œ ์‚ฌ์ด์˜ ๊ฐ„๊ฒฉ์— ์ƒˆ๋กœ์šด ๋ ˆ์ฝ”๋“œ๊ฐ€ ์ƒ์„ฑ (insert) ๋˜๋Š” ๊ฒƒ์„ ์ œ์–ดํ•œ๋‹ค.

 

๊ฐญ๋ฝ์— ๋Œ€ํ•ด์„œ๋Š” ์•„๋ž˜ ๋‹น๊ทผ๋งˆ์ผ“ ๋ธ”๋กœ๊ทธ์—์„œ ์ž์„ธํžˆ ๋‹ค๋ฃจ๊ณ  ์žˆ๋‹ค.

 

MySQL Gap Lock ๋‹ค์‹œ๋ณด๊ธฐ

์šฐ๋ฆฌ๊ฐ€ ์ผ๋ฐ˜์ ์œผ๋กœ ์•Œ๊ณ  ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„œ๋ฒ„์˜ ์ž ๊ธˆ(Lock)์€ ๋ ˆ์ฝ”๋“œ ์ž์ฒด์— ๋Œ€ํ•œ ์ž ๊ธˆ(Record Lock)์ด์—์š”. ์–ด๋–ค ํŠธ๋žœ์žญ์…˜์—์„œ ๋ ˆ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ทธ ๋ ˆ์ฝ”๋“œ๋ฅผ ์ž ๊ทธ๊ณ , ๊ทธ ๋™์•ˆ์€ ๋‹ค๋ฅธ

medium.com

 

 

(3) Next Key Lock (๋„ฅ์ŠคํŠธ ํ‚ค ๋ฝ)

๋ ˆ์ฝ”๋“œ ๋ฝ๊ณผ ๊ฐญ ๋ฝ์„ ํ•ฉ์ณ ๋†“์€ ํ˜•ํƒœ์˜ ์ž ๊ธˆ์„ ๋งํ•œ๋‹ค. 

๊ฐญ ๋ฝ, ๋„ฅ์ŠคํŠธ ํ‚ค ๋ฝ์˜ ๋ชฉ์ ์€ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋กœ๊ทธ์— ๊ธฐ๋ก๋˜๋Š” ์ฟผ๋ฆฌ๊ฐ€ replica server์—์„œ ์‹คํ–‰๋  ๋•Œ source server์—์„œ ๋งŒ๋“ค์–ด ๋‚ธ ๊ฒฐ๊ณผ์™€ ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋งŒ๋“ค์–ด ๋‚ด๋„๋ก ๋ณด์žฅํ•ด ์ฃผ๋Š” ๊ฒƒ์ด ์ฃผ ๋ชฉ์ ์ด๋‹ค. 

์˜ˆ๋ฅผ๋“ค์–ด, UPDATE tb_gaplock SET .. WHERE id BETWEEN 1 AND 3 ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด, 1,3๋ฒˆ row + 1,3 row ์‚ฌ์ด๋„ ์ž ๊ทผ๋‹ค. 

๋”ฐ๋ผ์„œ ์‹ค์ œ ์—…๋ฌด ํ”„๋กœ๊ทธ๋žจ์—์„œ๋Š” ์ด๋Ÿฐ Next Key Lock์ด ๋” ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ ๋œ๋‹ค๊ณ  ํ•œ๋‹ค.

 

 

(4) Auto Increment Lock (์ž๋™ ์ฆ๊ฐ€ ๋ฝ)

AUTO_INCREMENT ์นผ๋Ÿผ์ด ์ ์šฉ๋œ ํ…Œ์ด๋ธ”์— ๋™์‹œ์— ์—ฌ๋Ÿฌ ๋ ˆ์ฝ”๋“œ๊ฐ€ insert ๋˜๋Š” ๊ฒฝ์šฐ, ์ €์žฅ๋˜๋Š” ๊ฐ ๋ ˆ์ฝ”๋“œ๋Š” ์ค‘๋ณต๋˜์ž ์•Š๊ณ  ์ˆœ์„œ๋Œ€๋กœ ์ฆ๊ฐ€ํ•˜๋Š” ์ผ๋ จ๋ฒˆํ˜ธ๋ฅผ ๊ฐ€์ ธ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‚ด๋ถ€์ ์œผ๋กœ auto_increment lock์ด๋ผ๋Š” ํ…Œ์ด๋ธ” ์ˆ˜์ค€์˜ lock์„ ์‚ฌ์šฉํ•œ๋‹ค.

๋”ฐ๋ผ์„œ INSERT, REPLACE ๋ฌธ์—์„œ๋งŒ ํ•„์š”ํ•˜๋ฉฐ, UPDATE, DELETE ์ฟผ๋ฆฌ์—์„œ๋Š” ๊ฑธ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.

ํŠธ๋žœ์žญ์…˜๊ณผ ๊ด€๊ณ„ ์—†์ด auto_increment ๊ฐ’ ๊ฐ€์ ธ์˜ค๋Š” ์ˆœ๊ฐ„๋งŒ ๋ฝ ๊ฑธ๋ ธ๋‹ค๊ฐ€ ์ฆ‰์‹œ ํ•ด์ œ๋˜๋ฉฐ, ํ…Œ์ด๋ธ”์—์„œ lock์€ ๋‹จ ํ•˜๋‚˜๋งŒ ์กด์žฌํ•œ๋‹ค.

 

Reference

Real MySQL 8.0 Chapter 5

์ฐธ๊ณ  ์ž๋ฃŒ๋Š” ๋…ธ์…˜์— ๋” ์ถ”๊ฐ€์ ์œผ๋กœ ๊ธฐ๋กํ•ด ๋†“์•˜๋‹ค.

 

REAL MySQL - 05. ํŠธ๋žœ์žญ์…˜๊ณผ ์ž ๊ธˆ | Notion

MySQL์˜ ๋™์‹œ์„ฑ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š” Lock, ํŠธ๋žœ์žญ์…˜, ํŠธ๋žœ์žญ์…˜ ๊ฒฉ๋ฆฌ ์ˆ˜์ค€(Isolation level)์— ๋Œ€ํ•ด ๋‹ค๋ฃจ๋Š” ์žฅ์ด๋‹ค.

kaput-trombone-343.notion.site

https://techblog.woowahan.com/2631/

 

MySQL์„ ์ด์šฉํ•œ ๋ถ„์‚ฐ๋ฝ์œผ๋กœ ์—ฌ๋Ÿฌ ์„œ๋ฒ„์— ๊ฑธ์นœ ๋™์‹œ์„ฑ ๊ด€๋ฆฌ | ์šฐ์•„ํ•œํ˜•์ œ๋“ค ๊ธฐ์ˆ ๋ธ”๋กœ๊ทธ

{{item.name}} ์•ˆ๋…•ํ•˜์„ธ์š”. ๋น„์ฆˆ์ธํ”„๋ผ๊ฐœ๋ฐœํŒ€ ๊ถŒ์ˆœ๊ทœ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ๊ด‘๊ณ ์‹œ์Šคํ…œ์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” MySQL์„ ์ด์šฉํ•œ ๋ถ„์‚ฐ๋ฝ์— ๋Œ€ํ•ด ์„ค๋ช…๋“œ๋ฆฌ๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. ๋ถ„์‚ฐ๋ฝ์„ ์ ์šฉํ•˜๊ฒŒ๋œ ์›์ธ ํ˜„์žฌ ํ…Œ์ด๋ธ”์€ ์•„๋ž˜

techblog.woowahan.com

https://dev.mysql.com/doc/refman/8.0/en/locking-functions.html

 

MySQL :: MySQL 8.0 Reference Manual :: 14.14 Locking Functions

This section describes functions used to manipulate user-level locks. Table 14.19 Locking Functions GET_LOCK(str,timeout) Tries to obtain a lock with a name given by the string str, using a timeout of timeout seconds. A negative timeout value means infin

dev.mysql.com

 
 
 
์ตœ๊ทผ์— ์˜ฌ๋ผ์˜จ ๊ธ€
์ตœ๊ทผ์— ๋‹ฌ๋ฆฐ ๋Œ“๊ธ€
Total
Today
Yesterday