์ฐ์ ๊ธฐ๋ก ๐ช
์ฟผ๋ฆฌ ๋์์ฑ ๊ฒ์ฆ (Mysql) ๋ณธ๋ฌธ
์๋ ์ฟผ๋ฆฌ๊ฐ ์์์ ์ผ๋ก ๋์ํ๋์ง ๊ฒ์ฆํด๋ณด์. InnoDB ์์ง์ ์ฌ์ฉํ๋ MySQL์ด๋ผ๊ณ ๊ฐ์ ํ๋ค.
UPDATE tmp SET cnt = cnt+1 WHERE id = 1;
์ฟผ๋ฆฌ๊ฐ ์์์ ์ผ๋ก ๋์ํ๋ค๋ ๊ฒ์ ์ฟผ๋ฆฌ๊ฐ ์ชผ๊ฐ์ง์ง ์๋์ง๋ฅผ ๊ฒ์ฆํ๋ ๊ฒ์ด๋ค. ์ฆ ์์ ์ฟผ๋ฆฌ์์๋ cnt๊ฐ 1 ์ฆ๊ฐํ๊ฑฐ๋ ์ ํ ์ฆ๊ฐํ์ง ์๋์ง๋ฅผ ๊ฒ์ฆํ๋ค.
1) ํ ์คํธ ์ค๋น
์๋ ์ฟผ๋ฆฌ๋ฅผ ์คํํด ๊ฐ๋จํ ํ ์ด๋ธ๊ณผ ๋ฐ์ดํฐ๋ฅผ ์ค๋นํ๋ค.
CREATE TABLE tmp (
id INT PRIMARY KEY,
cnt INT NOT NULL DEFAULT 0
) ENGINE=InnoDB;
INSERT INTO tmp VALUES (1, 0), (2, 0);
2) ์ด๋ก ์ ๊ฒ์ฆ
๋จผ์ InnoDB์์๋ ์ ๋ฐ์ดํธ ๋๋ ํ์ exclusive lock(๋ฐฐํ์ ์ ๊ธ)์ ๊ฑด๋ค. ์ด๋ฅผ ํตํด ๋์์ ํ์ ์ ๋ฐ์ดํธ ํ๋ ๊ฒ์ ๋ง๋๋ค. ์ด ๋ฝ๋๋ฌธ์ ๋ค๋ฅธ ํธ๋์ญ์ ์ด ๋๊ธฐํ๊ฑฐ๋ ๋ฐ๋๋ฝ์ด ๊ฑธ๋ฆด ์ ์๋ค. ์ด ๋ด์ฉ์ 3๋ฒ์์ ์ดํด๋ณด๋๋ก ํ์.
์ ๊ธ ์ข ๋ฅ
Exclusive Lock(X-Lock) : ๋๋ฅผ ์ ์ธํ๊ณ ์ฐ๊ธฐ/์ฝ๊ธฐ ๋๋ค ๋ถ๊ฐ
Shared Lock(S-Lock) : ๋๋ฅผ ์ ์ธํ๊ณ ์ฐ๊ธฐ ๋ถ๊ฐ(์ฝ๊ธฐ๋ ๊ฐ๋ฅ)
InnoDB๋ ๋ฒํผ ํ์ด๋ผ๋ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ด ์๋ค. ํด๋น ๊ณต๊ฐ์ ๊ฐ์ ์บ์ฑํด์์ ์ฌ์ฉํ๋ค. ๊ทธ๋ ๊ธฐ์ ์ ๋ฐ์ดํธ๊ฐ ๋ฐ์ํ๋ฉด ํด๋น row์ ๋ฝ์ ๊ฑธ๊ณ (๋ฝ ๋ฏธํ๋์ ๋๊ธฐ), ๋ฒํผ ํ์์ ๊ฐ์ ๋ณํ์ํจ๋ค. ๋ํ Crash๋ฅผ ๋๋นํด redo log์ ๊ธฐ๋กํ๋ค.
-- redo log ์์
Row id=1: cnt 0 -> 1
๊ทธ๋ฆฌ๊ณ ์ปค๋ฐ ์์ ์ ๋์คํฌ์ ๋ณ๊ฒฝ๋ด์ฉ์ ๋ฐ์ํ๋ค.
๊ทธ๋ ๊ธฐ์ ์ด๋ก ์ ์ผ๋ก ์ ์ฟผ๋ฆฌ๋ ์์์ ์ด๋ค. InnoDB๋ ํ ๋จ์๋ก ๋ฝ์ ๊ฑธ๊ณ ์ ๋ฐ์ดํธ๋ฅผ ํ๊ธฐ ๋๋ฌธ์ ๋์์ ํ์ ์์ ํ๋ ๊ฒ์ ๋ชปํ๊ฒ ๋ง๊ธฐ ๋๋ฌธ์ด๋ค.
3) DB ์์ง ๋ ๋ฒจ ํ ์คํธ
์ด์ ์๋ 3๊ฐ์ง ์ํฉ์ ๋ํด ํด๋น ์ฟผ๋ฆฌ๊ฐ ๊ธฐ๋ํ๋๋ก ๋์ํ๋์ง ํ์ธํด๋ณด๋๋ก ํ์.
- ๋จ์ผ ์ฟผ๋ฆฌ ์คํ → Update๋ฌธ ๋ฐ์
- ๋ ์ธ์ ์์ ๊ฐ์ ํ ์ ๋ฐ์ดํธ ์๋ → row-level lock์ด ๊ฑธ๋ ค ๋ฆ๊ฒ ์ํํ ์ฟผ๋ฆฌ ๋๊ธฐ
- ๋ฐ๋๋ฝ ์ํฉ → ๋ฐ๋๋ฝ ๊ฐ์ง ๋ฐ ์ธ์ ํฌ&๋กค๋ฐฑ ํ ์ฟผ๋ฆฌ ์ํ
๋จผ์ 1๋ฒ ์ํฉ์ ๋ํด ์๋ ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ค.
UPDATE tmp SET cnt = cnt + 2 WHERE id = 1;
cnt๊ฐ 2 ์ฆ๊ฐํ๋ค๋ฉด ๊ธฐ๋ํ๋๋ก ๋์ํ ๊ฒ์ด๋ค.
์ด์ 2๋ฒ ์ํฉ์ ๋ํด์๋ ์๋์ ๊ฐ์ด ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ค. ์ธ์ 1์ด ์ปค๋ฐ๋ ๋ ๊น์ง ์ธ์ 2๊ฐ ๋๊ธฐํ๋ฉด ๋๋ค.
-- ์ธ์
1์์ ์ํ
START TRANSACTION;
UPDATE tmp SET cnt = cnt + 2 WHERE id = 1;
-- ์ธ์
2์์ ์ํ
START TRANSACTION;
UPDATE tmp SET cnt = cnt + 1 WHERE id = 1; -- ๋๊ธฐ
์ด๋ show processlist; ๋ฅผ ํตํด ์๋์ ๊ฐ์ด ์ ๋ฐ์ดํธ๊ฐ ์คํ๋์ง ๋ชปํ๊ณ updating์ํ์ ๋จธ๋ฌผ๋ฌ ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค.

3๋ฒ ์ํฉ์ ๋ํด์๋ ์๋์ ๊ฐ์ด ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ค. InnoDB๋ ๋ฐ๋๋ฝ์ ๊ฐ์งํ๋ฉด ๋ด๋ถ ์๊ณ ๋ฆฌ์ฆ์ ๋ฐ๋ผ Killํ ์ธ์ ์ ์ ํํ๊ณ (๋กค๋ฐฑ ๋น์ฉ, ํธ๋์ญ์ ID ๋ฑ ๊ณ ๋ ค) ํด๋น ์ธ์ ์ ํธ๋์ญ์ ์ ๋กค๋ฐฑ ๋ฐ ๋ฝ์ ํด์ ํ๋ค. ์ฆ ๋ฐ๋๋ฝ์ด ๋ฐ์ํ์ ๋ ์ด๋ฅผ ๊ฐ์งํ๊ณ ์ธ์ ์ Kill ๋ฐ ๋กค๋ฐฑํ์ฌ ๋ฐ๋๋ฝ์ ํด์ํ๊ณ , ์ฟผ๋ฆฌ๋ฅผ ์ํํ๋ฉด ์ฑ๊ณต์ด๋ค.
-- ์ธ์
1
START TRANSACTION;
UPDATE tmp SET cnt = cnt + 2 WHERE id = 1;
-- ์ธ์
2
START TRANSACTION;
UPDATE tmp SET cnt = cnt + 1 WHERE id = 2;
UPDATE tmp SET cnt = cnt + 1 WHERE id = 1;
--์ธ์
1
-- ERROR 1213 (40001): Deadlock found when trying to get lock;
-- try restarting transaction
UPDATE tmp SET cnt = cnt + 2 WHERE id = 2; -- ๋ฐ๋๋ฝ ๋ฐ์
-- ์ธ์
1
COMMIT;
-- ์ธ์
2
COMMIT;
์ด ์ํฉ์ ๊ทธ๋ฆผ๊ณผ ํํํ๋ฉด ์๋์ ๊ฐ๋ค. ์ด๋ค ์ธ์ ์ Killํ ์ง๋ ๋ด๋ถ ์๊ณ ๋ฆฌ์ฆ์ ์ํด ๊ฒฐ์ ๋๋ค.

4) ๋์์ฑ ํ ์คํธ
์ด์ DB ๋ ๋ฒจ์์ ๋์์ฑ ํ ์คํธ๋ฅผ ์งํํ๋๋ก ํ์.
๋จผ์ ๊ฐ๋จํ๊ฒ mysqlslap์ ์ด์ฉํด ์ฟผ๋ฆฌ๋ฅผ ๋์์ ์คํํด๋ณผ ์ ์๋ค.
mysqlslap --host=127.0.0.1 --port=3306 --user=root --password=<์ค์ ๋น๋ฐ๋ฒํธ> \\
--concurrency=100 --iterations=10 \\
--query="UPDATE tmp SET cnt = cnt + 1 WHERE id =1;" \\
--create-schema=backend_basic
1000์ด ์ค์ ๋ก ์ฆ๊ฐํ๋ค๋ฉด ์ฑ๊ณต์ด๋ค.
๋ํ SHOW ENGINE INNODB STATUS\G ๋ช ๋ น์ด๋ฅผ ํตํด ๋ฐ๋๋ฝ ์ฌ๋ถ๋ Lock ๋๊ธฐ ์ฌ๋ถ ๋ฑ๋ ๋ถ์ํ ์ ์๋ค. ์๋๋ INNODB ๋ด๋ถ ์ํ ๋ก๊ทธ๋ฅผ ๋ฐ์ทํ ๊ฒ์ด๋ค.
=====================================
2025-10-28 14:27:30 281472164945664 INNODB MONITOR OUTPUT
=====================================
. . . ์ค๋ต . . .
------------
TRANSACTIONS
------------
Trx id counter 11539
Purge done for trx's n:o < 11539 undo n:o < 0 state: running but idle
History list length 1
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 562947761801648, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 562947761800840, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 11536, ACTIVE 28 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 1
MySQL thread id 10, OS thread handle 281472163827456, query id 115 192.168.65.1 root updating
/* ApplicationName=DataGrip 2022.3.3 */ UPDATE tmp SET cnt = cnt + 1 WHERE id = 1
------- TRX HAS BEEN WAITING 5 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 57 page no 4 n bits 72 index PRIMARY of table `backend_basic`.`tmp` trx id 11536 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000001; asc ;;
1: len 6; hex 000000002d0f; asc - ;;
2: len 7; hex 02000001522946; asc R)F;;
3: len 4; hex 800003ea; asc ;;
------------------
---TRANSACTION 11535, ACTIVE 32 sec
2 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 1
MySQL thread id 9, OS thread handle 281472164945664, query id 124 192.168.65.1 root starting
/* ApplicationName=DataGrip 2022.3.3 */ SHOW ENGINE INNODB STATUS
--------
FILE I/O
--------
. . . ์ดํ ์๋ต
์ฌ๊ธฐ์ ํธ๋์ญ์ ๋ถ๋ถ์ ์ข ๋ ๋ณด๋๋ก ํ์.
๋จผ์ ํธ๋์ญ์ 11536์ 28์ด์งธ ์คํ์ค์ด๋ค. ๋ํ ------- TRX HAS BEEN WAITING 5 SEC FOR THIS LOCK TO BE GRANTED: ๋ถ๋ถ์ ๋ณด๋ฉด ํด๋น ํธ๋์ญ์ ์ด ๋ฝ์ ์ป๊ธฐ ์ํด 5์ด์งธ ๋๊ธฐ์ค์ด๋ผ๋ ๋ป์ด๋ค.
---TRANSACTION 11536, ACTIVE 28 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s), undo log entries 1
MySQL thread id 10, OS thread handle 281472163827456, query id 115 192.168.65.1 root updating
/* ApplicationName=DataGrip 2022.3.3 */ UPDATE tmp SET cnt = cnt + 1 WHERE id = 1
------- TRX HAS BEEN WAITING 5 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 57 page no 4 n bits 72 index PRIMARY of table `backend_basic`.`tmp` trx id 11536 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 4; hex 80000001; asc ;;
1: len 6; hex 000000002d0f; asc - ;;
2: len 7; hex 02000001522946; asc R)F;;
3: len 4; hex 800003ea; asc ;;'Computer Science > ๋ฐ์ดํฐ๋ฒ ์ด์ค' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| DB์ ๋๋ ๋ฐ์ดํฐ ๋ฃ๊ธฐ (Mysql, Faker) (0) | 2025.10.12 |
|---|---|
| ์ปค๋ฅ์ ํ(Spring Boot, HikariCP) (0) | 2025.09.30 |
| [๋ฐ์ดํฐ๋ฒ ์ด์ค] B- Tree์ ์ธ๋ฑ์ค์ ์ดํด (3) | 2024.09.04 |