๐ ๊ฐ์
์ ๊ธ(Lock)์ ๋์์ฑ ์ ์ด๋ฅผ ์ํ ์ค์ํ ๊ธฐ๋ฅ์ด๊ณ ํธ๋์ญ์ (Transaction)์ ์์ ์ ์์์ฑ์ ๋ณด์ฅํด์ฃผ๋ ๊ฒ์ด ์ฃผ์ ์์ฑ์ ๋๋ค. ์ฌ๊ธฐ์ ๊ฒฉ๋ฆฌ ์์ค(Isolation Level)์ ํ๋์ ํธ๋์ญ์ ๋ด๋ถ ๋๋ ์ฌ๋ฌ ํธ๋์ญ์ ๊ฐ์ ์์ ๋ด์ฉ์ ์ด๋ป๊ฒ ๊ณต์ ํ๊ณ ์ฐจ๋จํ ๊ฒ์ธ์ง๋ฅผ ๊ฒฐ์ ํ๋ ๋ ๋ฒจ์ ์๋ฏธํฉ๋๋ค.
MySQL์์ ์ฌ์ฉํ๋ ์คํ ๋ฆฌ์ง ์์ง, InnoDB์์ ๊ฒฉ๋ฆฌ ์์ค์ ๋ฐ๋ผ ํธ๋์ญ์ ์ด ์ด๋ป๊ฒ ๋์ํ๋์ง ์ดํด๋ณด๋ ค๊ณ ํฉ๋๋ค.
๋์ ๋ฐฉ์์ ์ดํด๋ณด๊ธฐ์ ์ ๊ฒฉ๋ฆฌ ์์ค์ ๋ํ ์ดํด๊ฐ ๋ถ์กฑํ๋ค๋ฉด ์๋ ๊ธ์ ์ฐธ์กฐํ์๋ฉด ๋์์ด ๋ ์ ์์ต๋๋ค.
[Database] RDBMS์ ๊ฒฉ๋ฆฌ ์์ค (Isolation Level)
๐ ๊ฐ์์ค๋์ ์ฌ๋ฌ RDBMS์ ์ ์ฉ๋๊ณ ์๋ ๊ฒฉ๋ฆฌ ์์ค์ ๋ํด์ ์์๋ณด๋ ค๊ณ ํฉ๋๋ค. SQL ํ์ค ๊ฒฉ๋ฆฌ ์์ค์ ์ต๊ทผ ํ์ค๊น์ง ๊ธฐ๋ณธ ์์น์ด ๋ณํ์ง ์์ ๊ฒ์ผ๋ก ํ๋จ๋์ด, SQL-99 ํ์ค ๋น๊ณต์ ๋ฌธ์๋ฅผ ๊ธฐ
phellinus-linteus.tistory.com
InnoDB๋ ๊ธฐ๋ณธ์ ์ผ๋ก REPEATABLE READ๊ฐ ์ ์ฉ๋์ด ์์ต๋๋ค. SQL ํ์ค์์๋ REPEATABLE READ์์ ํฌํ ๋ฆฌ๋๊ฐ ๋ฐ์ํ ์ ์๋ค๊ณ ์ค๋ช ํ์ง๋ง, InnoDB๋ ๋ฅ์คํธ ํค ๋ฝ(Next-Key Lock) ๋ฉ์ปค๋์ฆ์ ํตํด ์ด๋ฅผ ๋ฐฉ์งํ๊ณ ์์ต๋๋ค. ์ด ๊ธ์์๋ ๋ฅ์คํธ ํค ๋ฝ์ ๊ฐ๋ ๊ณผ ํจ๊ป, ๊ฐ ๊ฒฉ๋ฆฌ ์์ค๋ณ ๋์ ๋ฐฉ์์ ๋ํ ์ดํด๋ฅผ ๋๊ธฐ ์ํด ๊ฐ๋จํ ์ค์ต ์์ ๋ฅผ ๋ค๋ฃฐ ์์ ์ ๋๋ค. ์ด๋ฅผ ํตํด InnoDB์ ๊ฒฉ๋ฆฌ ์์ค์ ๋์ฑ ๋ช ํํ๊ฒ ์ดํดํ๋ ๊ฒ์ ๋ชฉํ๋ก ํฉ๋๋ค.
๋ชฉ์ฐจ
- READ UNCOMMITTED
- READ COMMITTED
- REPEATABLE READ
- SERIALIZABLE
- ๋ง์น๋ฉฐ
์ค์ต ํ๊ฒฝ
- MySQL 8.0.32
- ์๋ก ๋ค๋ฅธ ํธ๋์ญ์ ์ ๋์์ ํ์ธํ๊ธฐ ์ํด mysql ํด๋ผ์ด์ธํธ ์ธ์ 3๊ฐ ์์ฑ
- ์ค์ต์ฉ ๋ฐ์ดํฐ๋ฒ ์ด์ค & ํ ์ด๋ธ ์์ฑ
-- ํ
์คํธ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ฑ
CREATE DATABASE test;
-- ํ
์คํธ ํ
์ด๋ธ ์์ฑ
CREATE TABLE users (
id BIGINT PRIMARY KEY,
name VARCHAR(10) NOT NULL
);
๐ข READ UNCOMMITTED
๊ฐ ํธ๋์ญ์ ์์์ ๋ณ๊ฒฝ ๋ด์ฉ์ COMMIT ์ด๋ ROLLBACK ์ฌ๋ถ์ ์๊ด์์ด ๋ค๋ฅธ ํธ๋์ญ์ ์์ ์กฐํํ ์ ์๋ ๊ฒฉ๋ฆฌ ์์ค์ ๋๋ค.
- ํธ๋์ญ์ A๋ฅผ ์์ํฉ๋๋ค.
- ํธ๋์ญ์ A์์ users ํ ์ด๋ธ์ id=1, name=’ian’ ๋ ์ฝ๋๋ฅผ ์ฝ์ ํฉ๋๋ค.
- ํธ๋์ญ์ B๋ฅผ ์์ํฉ๋๋ค.
- ํธ๋์ญ์
B์์ users ํ
์ด๋ธ์์ id=1์ธ ๋ ์ฝ๋๋ฅผ ์กฐํํฉ๋๋ค.
- ์์ง ์ปค๋ฐ๋์ง ์์ ๋ฐ์ดํฐ๋ผ๋ ๊ฒฉ๋ฆฌ ์์ค์ด READ UNCOMMITTED ์ด๊ธฐ ๋๋ฌธ์ id=1์ธ ์ ์ ๋ ์ฝ๋๊ฐ ๋ฐํ๋ฉ๋๋ค.
- ํธ๋์ญ์
A์์ ์์ธ๊ฐ ๋ฐ์ํด, ๋กค๋ฐฑ์ ์ํํฉ๋๋ค.
- ์ฝ์ ํ๋ id=1์ธ ์ ์ ๋ ์ฝ๋๊ฐ ์ญ์ ๋ฉ๋๋ค.
์ ์์์ฒ๋ผ, ์ด๋ค ํธ๋์ญ์ ์์ ์ปค๋ฐ๋์ง ์์ ๋ณ๊ฒฝ์ฌํญ์ ๋ค๋ฅธ ํธ๋์ญ์ ์์ ์กฐํํ ์ ์๋ ๊ฒฉ๋ฆฌ ์์ค์ ๋๋ค. ๋กค๋ฐฑ ๋ฐ์ ํ id=1์ธ ์ ์ ๋ฐ์ดํฐ๋ ์์ด์ผ๋ ๋ฐ์ดํฐ์ด์ง๋ง, ํธ๋์ญ์ B ์ ์ฅ์์ ์กด์ฌํ๋ ์ ์ ๋ก ์ธ์งํ๊ฒ ๋ฉ๋๋ค. ์ด๋ฌํ ๋ฌธ์ ๋ฅผ Dirty Read ๋ผ๊ณ ํฉ๋๋ค.
์์์์๋ ํธ๋์ญ์ A๊ฐ ํ๋์ ์ฟผ๋ฆฌ๋ง ์คํํ์ง๋ง, ํ ํธ๋์ญ์ ๋ด์์ ๋ ๋ง์ ์ฟผ๋ฆฌ๊ฐ ์ํ๋ ์ ์๊ณ ํด๋น ๋ณ๊ฒฝ ์ฌํญ์ ๋ค๋ฅธ ํธ๋์ญ์ ์์ ์ปค๋ฐ ์ ์ ์ฝ๋๋ค๋ฉด ์น๋ช ์ ์ธ ์ ํฉ์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
๐งช ์ค์ต
START TRANSACTION;
INSERT INTO users (id, name) VALUES (1, 'ian');
- ํธ๋์ญ์ A(์ผ์ชฝ)๋ฅผ ์์ํ๊ณ {id=1, name='ian'} ์ธ ๋ ์ฝ๋๋ฅผ ์ฝ์ ํฉ๋๋ค.
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT * FROM users WHERE id = 1;
- ํธ๋์ญ์
B(์ค๋ฅธ์ชฝ)์ ์์ํ๊ธฐ ์ ์ ๊ฒฉ๋ฆฌ ์์ค์ READ UNCOMMITTED๋ก ์ค์ ํฉ๋๋ค.
- InnoDB์ ๊ธฐ๋ณธ ๊ฒฉ๋ฆฌ ์์ค์ REPEATABLE READ์ด๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ์ค์ ์ด ํ์ํฉ๋๋ค.
- ํธ๋์ญ์
B(์ค๋ฅธ์ชฝ)๋ฅผ ์์ํ๊ณ users ํ
์ด๋ธ์์ id=1 ์ธ ๋ ์ฝ๋๋ฅผ ์กฐํํฉ๋๋ค.
- ์์ง ์ปค๋ฐ๋์ง ์์์ง๋ง ์กฐํ๊ฐ ๋๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
ROLLBACK;
SELECT * FROM users WHERE id = 1;
- ํธ๋์ญ์
A(์ผ์ชฝ)์์ ๋ช
์์ ์ผ๋ก ๋กค๋ฐฑ์ ์ํํ๊ณ users ํ
์ด๋ธ์์ id=1์ธ ๋ ์ฝ๋๋ฅผ ์กฐํํฉ๋๋ค.
- ๋กค๋ฐฑ์ผ๋ก ์ธํด ํธ๋์ญ์ A๊ฐ ์ฝ์ ํ๋ ์ ์ ๋ ์ฝ๋๊ฐ ์์ด์ก์์ ํ์ธํ ์ ์์ต๋๋ค.
- ๊ฒฐ๊ณผ์ ์ผ๋ก ํธ๋์ญ์ B์์ Dirty Read ๊ฐ ๋ฐ์ํ์ต๋๋ค.
๐ต READ COMMITTED
์ด๋ค ํธ๋์ญ์ ์์ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋๋ผ๋ ์ปค๋ฐ์ด ์๋ฃ๋ ๋ฐ์ดํฐ๋ง ๋ค๋ฅธ ํธ๋์ญ์ ์์ ์กฐํํ ์ ์๋ ๊ฒฉ๋ฆฌ ์์ค์ ๋๋ค.
- users ํ ์ด๋ธ์ id=1, name=’ian’ ๋ ์ฝ๋๊ฐ ์กด์ฌํฉ๋๋ค.
- ํธ๋์ญ์ A๋ฅผ ์์ํฉ๋๋ค.
- ํธ๋์ญ์
A์์ users ํ
์ด๋ธ์ ์๋ id=1์ธ ๋ ์ฝ๋๋ฅผ name=’patrick’์ผ๋ก ์
๋ฐ์ดํธ ํฉ๋๋ค.
- ํ ์ด๋ธ์ ๋ณ๊ฒฝ์ฌํญ์ด ๋ฐ์๋๊ณ , ์ธ๋ ๋ก๊ทธ์ ์ ๋ฐ์ดํฐ ์ด์ ๋ฐ์ดํฐ๊ฐ ๊ธฐ๋ก๋ฉ๋๋ค.
- ํธ๋์ญ์ B๋ฅผ ์์ํฉ๋๋ค.
- ํธ๋์ญ์
B์์ users ํ
์ด๋ธ์ ์๋ id=1์ธ ๋ ์ฝ๋๋ฅผ ์กฐํํฉ๋๋ค.
- ์ ์ฉ๋ ๊ฒฉ๋ฆฌ ์์ค์ด READ COMMITTED์ด๊ณ , ํธ๋์ญ์ A๊ฐ ์ปค๋ฐ๋์ง ์์๊ธฐ ๋๋ฌธ์ ์ธ๋ ๋ก๊ทธ๋ฅผ ์ฐ์ ์ ์ผ๋ก ๋ฐ๋ผ๋ณด๊ณ ๋ฐ์ดํฐ๊ฐ ๋ฐํ๋ฉ๋๋ค.
- ํธ๋์ญ์ A๋ฅผ ์ปค๋ฐํฉ๋๋ค.
์ ์์์ฒ๋ผ, ์ด๋ค ํธ๋์ญ์ ์์ ์ปค๋ฐ๋ ๋ณ๊ฒฝ์ฌํญ๋ง ๋ค๋ฅธ ํธ๋์ญ์ ์์ ์กฐํํ ์ ์๋ ๊ฒฉ๋ฆฌ ์์ค์ ๋๋ค. ํธ๋์ญ์ A๊ฐ id=1์ธ ๋ ์ฝ๋๋ฅผ ์ ๋ฐ์ดํธํ ํ ํ ์ด๋ธ์๋ ์ต์ ๋ณ๊ฒฝ ์ฌํญ์ด, ์ธ๋ ๋ก๊ทธ์๋ ์ด์ ๋ฒ์ ์ด ๊ธฐ๋ก๋ฉ๋๋ค.
ํธ๋์ญ์ A๊ฐ ์ปค๋ฐ๋์ง ์์ ์ํ์์ ํธ๋์ญ์ B๊ฐ ํด๋น ๋ ์ฝ๋๋ฅผ ์ฝ์ผ๋ ค๊ณ ํ ๋ ์ธ๋ ๋ก๊ทธ๋ฅผ ๋ฐ๋ผ๋ณด๊ฒ ๋จ์ผ๋ก์จ ์ด์ ๋ฒ์ ์ ์ฝ๊ฒ ๋๊ณ Dirty Read ๋ฌธ์ ๊ฐ ๋ฐฉ์ง๋๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค. ์์๋ ๋ ์ฝ๋์ UPDATE ์ํฉ์ด์ง๋ง, INSERT, DELETE๋ ๋ง์ฐฌ๊ฐ์ง๋ก ์ธ๋ ๋ก๊ทธ์ ์ด์ ๋ฒ์ ์ด ๊ธฐ๋ก๋ ์ ์๊ณ Dirty Read ๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
๊ฒฉ๋ฆฌ ์์ค READ COMMITTED์์ Dirty Read ๋ฅผ ๋ฐฉ์งํ๋ ๊ฒ์ ํ์ธํ๋๋ฐ, ์ด๋ฒ์ Non-Repeatable Read ๋ฌธ์ ๋ฐ์ ์์๋ฅผ ๋ณด๊ฒ ์ต๋๋ค.
- users ํ ์ด๋ธ์ id=1, name=’ian’ ๋ ์ฝ๋๊ฐ ์กด์ฌํฉ๋๋ค.
- ํธ๋์ญ์ A๋ฅผ ์์ํฉ๋๋ค.
- ํธ๋์ญ์
A์์ users ํ
์ด๋ธ์ ์๋ name=’ian’์ธ ๋ ์ฝ๋๋ฅผ ์กฐํํฉ๋๋ค.
- name=’ian’์ธ ๋ ์ฝ๋ ํ ๊ฑด์ด ๋ฐํ๋ฉ๋๋ค.
- ํธ๋์ญ์ B๋ฅผ ์์ํฉ๋๋ค.
- ํธ๋์ญ์ B์์ users ํ ์ด๋ธ์ ์๋ name=’ian'์ธ ๋ ์ฝ๋๋ฅผ name=’patrick’์ผ๋ก ์ ๋ฐ์ดํธ ํฉ๋๋ค.
- ํธ๋์ญ์ B๋ฅผ ์ปค๋ฐํฉ๋๋ค.
- ํธ๋์ญ์
A์์ ์๊น ์กฐํํ๋ users ํ
์ด๋ธ์ ์๋ name=’ian’์ธ ๋ ์ฝ๋๋ฅผ ๋ค์ ์กฐํํฉ๋๋ค.
- ๊ฒฉ๋ฆฌ ์์ค์ด READ COMMITTED ์ด๊ธฐ ๋๋ฌธ์ ๋ ์ฝ๋์ ์ปค๋ฐ๋ ๋ฒ์ ๋ง ์ฝ๊ฒ๋๊ณ name=’ian’ ๋งค์นญ๋๋ ๋ ์ฝ๋๊ฐ ์์ผ๋ฏ๋ก ๋ฐํ ๊ฒฐ๊ณผ๊ฐ ์์ต๋๋ค.
READ COMMITTED์์๋ Dirty Read๋ฅผ ๋ฐฉ์งํ ์ ์์ผ๋, ํ ํธ๋์ญ์ ๋ด์์ ๊ฐ์ ์ฟผ๋ฆฌ๊ฐ ์ผ๊ด๋ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฅํด์ค ์๋ ์์ต๋๋ค. ํด๋น ๋ฌธ์ ๋ฅผ Non-Repeatable Read๋ผ๊ณ ํ๋ฉฐ, ์ด ๋ํ ์ ํฉ์ฑ ๋ฌธ์ ๋ฅผ ์ผ์ผํฌ ์ ์์ต๋๋ค.
๐งช ์ค์ต
users ํ ์ด๋ธ์ id=1, name=’ian’ ๋ ์ฝ๋ ํ๋๋ฅผ INSERTํ๊ณ ๊ฒฉ๋ฆฌ ์์ค READ COMMITTED์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ์ ๋ํด์๋ง ์ค์ต์ ์งํํด๋ณด๊ฒ ์ต๋๋ค.
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
SELECT * FROM users WHERE name = 'ian';
- ํธ๋์ญ์
A(์ผ์ชฝ)์ ์์ํ๊ธฐ ์ ์ ๊ฒฉ๋ฆฌ ์์ค์ READ COMMITTED๋ก ์ค์ ํฉ๋๋ค.
- InnoDB์ ๊ธฐ๋ณธ ๊ฒฉ๋ฆฌ ์์ค์ REPEATABLE READ์ด๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ์ค์ ์ด ํ์ํฉ๋๋ค.
- ํธ๋์ญ์
A(์ผ์ชฝ)๋ฅผ ์์ํ๊ณ users ํ
์ด๋ธ์์ name=’ian’ ์ธ ๋ ์ฝ๋๋ฅผ ์กฐํํฉ๋๋ค.
- ์กด์ฌํ๋ ๋ ์ฝ๋ 1๊ฑด์ด ์กฐํ๋๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
START TRANSACTION;
UPDATE users SET name = 'patrick' WHERE name = 'ian';
COMMIT;
- ํธ๋์ญ์ B(์ค๋ฅธ์ชฝ)๋ฅผ ์์ํ๊ณ users ํ ์ด๋ธ์์ name=’ian’ ์ธ ๋ ์ฝ๋๋ฅผ name=’patrick’์ผ๋ก ์ ๋ฐ์ดํธ ํฉ๋๋ค.
- ํธ๋์ญ์ B(์ค๋ฅธ์ชฝ)๋ฅผ ์ปค๋ฐํฉ๋๋ค.
SELECT * FROM users WHERE name = 'ian';
- ํธ๋์ญ์
A(์ผ์ชฝ)์์ ์ฒ์ ์กฐํํ๋ users ํ
์ด๋ธ์ name=’ian’์ธ ๋ ์ฝ๋๋ฅผ ์กฐํํฉ๋๋ค.
- ์์ง ํธ๋์ญ์ ์ ๋๋ด์ง ์์ ์ฑ๋ก ๊ฐ์ ์กฐํ ์ฟผ๋ฆฌ๋ฅผ ์ํํ์ผ๋ ํธ๋์ญ์ B(์ค๋ฅธ์ชฝ)๊ฐ ์คํ์ํจ ์ปค๋ฐ ๋ด์ญ์ผ๋ก ์ธํด์ ์กฐํ๋๋ ๊ฒฐ๊ณผ๊ฐ ์๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
- ๊ฒฐ๊ณผ์ ์ผ๋ก ํธ๋์ญ์ A์์ Non-Repeatable Read ๊ฐ ๋ฐ์ํ์ต๋๋ค.
๐ฃ REPEATABLE READ
๋์ผํ ํธ๋์ญ์ ๋ด์์ ๋์ผํ SELECT ์ฟผ๋ฆฌ๋ฅผ ๋ฐ๋ณตํด์ ์ํํ์ ๋ ํญ์ ๊ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฅํ๋ ๊ฒฉ๋ฆฌ ์์ค์ ๋๋ค.
- users ํ ์ด๋ธ์ id=1, name=’ian’ ๋ ์ฝ๋๊ฐ ์กด์ฌํฉ๋๋ค.
- ํธ๋์ญ์
A๋ฅผ ์์ํฉ๋๋ค.
- TRX ID๋ 15 ์ ๋๋ค.
- ํธ๋์ญ์
A์์ users ํ
์ด๋ธ์ ์๋ name=’ian’์ธ ๋ ์ฝ๋๋ฅผ ์กฐํํฉ๋๋ค.
- name=’ian’์ธ ๋ ์ฝ๋ ํ ๊ฑด์ด ๋ฐํ๋ฉ๋๋ค.
- ํธ๋์ญ์
B๋ฅผ ์์ํฉ๋๋ค.
- TRX ID๋ 17 ์ ๋๋ค.
- ํธ๋์ญ์ B์์ users ํ ์ด๋ธ์ ์๋ name=’ian'์ธ ๋ ์ฝ๋๋ฅผ name=’patrick’์ผ๋ก ์ ๋ฐ์ดํธ ํฉ๋๋ค.
- ํธ๋์ญ์ B๋ฅผ ์ปค๋ฐํฉ๋๋ค.
- ํธ๋์ญ์
A์์ ์๊น ์กฐํํ๋ users ํ
์ด๋ธ์ ์๋ name=’ian’์ธ ๋ ์ฝ๋๋ฅผ ๋ค์ ์กฐํํฉ๋๋ค.
- ๊ฒฉ๋ฆฌ ์์ค์ด REPEATABLE READ์ด๊ธฐ ๋๋ฌธ์ ํธ๋์ญ์ A์ TRX ID๋ 15์ด๊ณ , ์ดํ ํธ๋์ญ์ (TRX ID 16 ์ด์)๋ค์ ์ ๋ฐ์ดํธ ์ฌํญ๋ค์ ์ปค๋ฐ๋๋ค๊ณ ํด๋ ์ธ๋ ๋ก๊ทธ๋ฅผ ์ฐ์ ์ผ๋ก ๋ฐ๋ผ๋ณด๊ฒ ๋ฉ๋๋ค.
- ๊ฒฐ๊ณผ์ ์ผ๋ก TRX ID 17 ์ด ๋ณ๊ฒฝํ๊ณ ์ปค๋ฐํ ์ฌํญ์ TRX ID 15์ธ ํธ๋์ญ์
A๋ ์์ง ๋ชปํ๊ณ ์ธ๋ ๋ก๊ทธ๋ฅผ ํตํด ์์ ์์ ๊ณผ ๊ฐ์ ์ํ์ ๋ ์ฝ๋๋ฅผ ์ ์์ ์ผ๋ก 1๊ฑด ์กฐํํฉ๋๋ค.
- ๋ง์น ํธ๋์ญ์ ์์ ์์ ์ ์ค๋ ์ท์ ์ฐ์ด๋๊ณ ์ฌ์ฉํ๋ ๋ฏํ ๋์์ ํฉ๋๋ค.
๊ฒฉ๋ฆฌ ์์ค REPEATABLE READ์์๋ ํธ๋์ญ์ ์์ ์์ ์ ๋ฐ๋ผ์ MVCC(Multi-Version Concurrency Control) ๋ฉ์ปค๋์ฆ์ผ๋ก ์ธ๋ ๋ก๊ทธ๋ฅผ ํ์ฉํ ์ ์์ต๋๋ค. ๋๋ถ์ Non-Repeatable Read ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ ์ ์๊ฒ ๋ฉ๋๋ค.
SQL ํ์ค์ ๊ธฐ์ ๋ ๊ฒฉ๋ฆฌ ์์ค์ ๋ณด๋ฉด, REPEATABLE READ์์ Phantom Read๊ฐ ๋ฐ์ํ ์ ์๋ค๊ณ ํ์ง๋ง, ์ ํํ ์ด๋ป๊ฒ ๋์ํ๊ธฐ ๋๋ฌธ์ Phantom Read๊ฐ ๋ฐ์ํ๋์ง๋ ๋ช ์๋์ด ์์ง ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ InnoDB์์๋ ๋ฅ์คํธ ํค ๋ฝ ์ด๋ผ๋ ๋ฉ์ปค๋์ฆ์ผ๋ก REPEATABLE READ์์๋ Phantom Read๋ฅผ ๋ฐฉ์งํ๊ณ ์์ต๋๋ค. ๋๋ถ์ ๊ธฐ๋ณธ ์ค์ ๋ ๊ฒฉ๋ฆฌ ์์ค๋ง์ผ๋ก Dirty Read, Non-Repeatable Read, Phantom Read๋ฅผ ๋ฐฉ์งํ ์ ์๋ ๋ฅ๋ ฅ์ ๊ฐ์ถ ์ ์ด๊ฒ ๋ค์.
์ด๋ฒ์ Phantom Read๋ฅผ ์ฐจ๋จํ๋ ์๋๋ฆฌ์ค์ ๋ํด์ ๊ฐ๋จํ ๋ณด๊ฒ ์ต๋๋ค.
- users ํ ์ด๋ธ์ {id=1, name=’ian’}, {id=2, name=’bob’} ๋ ์ฝ๋๊ฐ ์กด์ฌํฉ๋๋ค.
- ํธ๋์ญ์
A๋ฅผ ์์ํฉ๋๋ค.
- TRX ID๋ 15 ์ ๋๋ค.
- ํธ๋์ญ์
A์์ users ํ
์ด๋ธ์ ์๋ 1 ์ด์์ id๋ฅผ ๊ฐ์ง๋ ๋ ์ฝ๋๋ฅผ ์กฐํํฉ๋๋ค.
- FOR UPDATE ํค์๋๋ฅผ ํตํด ๋น๊ด์ ๋ฝ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ์ธ๋ ๋ก๊ทธ๋ฅผ ๋ฐ๋ผ๋ณผ ์ ์์ต๋๋ค.
- ๋ ์ฝ๋ ๋ ๊ฑด์ด ๋ฐํ๋ฉ๋๋ค.
- ํธ๋์ญ์
B๋ฅผ ์์ํฉ๋๋ค.
- TRX ID๋ 17 ์ ๋๋ค.
- ํธ๋์ญ์
B์์ users ํ
์ด๋ธ์ id=3, name=’json’์ธ ๋ ์ฝ๋๋ฅผ ์ฝ์
ํฉ๋๋ค.
- ํ ์ด๋ธ์ ์ ๋ ์ฝ๋๊ฐ ์ถ๊ฐ๋๊ณ ๋กค๋ฐฑ & MVCC ๋์์ ์ํด ์ธ๋ ๋ก๊ทธ์ ๊ธฐ๋ก๋ฉ๋๋ค.
- ํธ๋์ญ์ B๋ฅผ ์ปค๋ฐํฉ๋๋ค.
- ํธ๋์ญ์
A์์ users ํ
์ด๋ธ์ ์๋ 1 ์ด์์ id๋ฅผ ๊ฐ์ง๋ ๋ ์ฝ๋๋ฅผ ๋ค์ ์กฐํํฉ๋๋ค.
- FOR UPDATE ํค์๋๋ฅผ ํตํด ๋น๊ด์ ๋ฝ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ์ธ๋ ๋ก๊ทธ๋ฅผ ๋ฐ๋ผ๋ณผ ์ ์๊ธฐ ๋๋ฌธ์ ํธ๋์ญ์ B์ ์ํด ์ฝ์ ๋๊ณ ์ปค๋ฐ๋ id=3์ธ ๋ ์ฝ๋๊น์ง ํฌํจํ์ฌ ์ด 3๊ฑด์ ๋ ์ฝ๋๊ฐ ๋ฐํ๋ฉ๋๋ค.
์ธ๋ ๋ก๊ทธ๋ฅผ ํ์ธํ๋ฉด ํธ๋์ญ์ A์ ์์ ์์ ๊ธฐ์ค์ผ๋ก id=3์ธ ๋ ์ฝ๋๋ ์ดํ์ ์ฝ์ ๋๊ณ ์ปค๋ฐ๋ ๋ฐ์ดํฐ๋๊น ํํฐ๋งํด์ ์ ์กฐํํ ์ ์์ ๊ฒ ๊ฐ์๋ฐ, ์ด์งธ์ ์กฐํ๊ฐ ๋์ด์ Phantom Read๋ฅผ ๋ฐ์์ํฌ๊น์?
FOR UPDATE ๋ฅผ ์ฌ์ฉํ๋ฉด SELECT ์์๋ ๋ฐ๋ก ๋ฝ์ ํ๋ํฉ๋๋ค. ์์์๋ ๊ฐ๋จํ ์๋๋ฆฌ์ค๋ฅผ ๊ตฌ์ฑํ์ง๋ง, FOR UPDATE๋ฅผ ์ฌ์ฉํ๋ค๋ ๊ฒ์ ์ค์ ๋ก ๋ ์ฝ๋ UPDATE๋ฅผ ์ํด์ SELECT๋ฅผ ํ ๊ฒ์ด๊ณ UPDATE๋ฅผ ์ํด ์ฝ๋ ๋ฐ์ดํฐ๊ฐ ์ด์ ๋ฒ์ ์ผ๋ก ์กฐํ๋๋ค๋ฉด ์๊ธฐ์น ๋ชปํ ๊ฐ์ผ๋ก ์ ๋ฐ์ดํธ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก, ๋ฝ๊ณผ ๊ด๋ จ๋ ๋ ์ฝ๋์ ํํด์๋ ์ธ๋ ๋ก๊ทธ๋ฅผ ๋ฐ๋ผ๋ณผ ์ ์๊ฒ ๋๊ณ ์์ ๊ฐ์ด Phantom Read๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.
์ด๋ฒ์ ์ค์ InnoDB์์ ๋ฅ์คํธ ํค ๋ฝ์ด ์ ์ฉ๋์ด ์๋ค๊ณ ํ์ ๋ ๊ฐ์ ์๋๋ฆฌ์ค๋ฅผ ๋ณด๊ฒ ์ต๋๋ค.
- users ํ ์ด๋ธ์ {id=1, name=’ian’}, {id=2, name=’bob’} ๋ ์ฝ๋๊ฐ ์กด์ฌํฉ๋๋ค.
- ํธ๋์ญ์
A๋ฅผ ์์ํฉ๋๋ค.
- TRX ID๋ 15 ์ ๋๋ค.
- ํธ๋์ญ์
A์์ users ํ
์ด๋ธ์ ์๋ 1 ์ด์์ id๋ฅผ ๊ฐ์ง๋ ๋ ์ฝ๋๋ฅผ ์กฐํํฉ๋๋ค.
- FOR UPDATE ํค์๋๋ฅผ ํตํด ๋น๊ด์ ๋ฝ์ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ์ธ๋ ๋ก๊ทธ๋ฅผ ๋ฐ๋ผ๋ณผ ์ ์๊ณ ๊ด๋ จ ๋ ์ฝ๋์ ๋ฅ์คํธ ํค ๋ฝ(๋ ์ฝ๋ ๋ฝ + ๊ฐญ ๋ฝ)์ด ์ ์ฉ๋ฉ๋๋ค.
- ๋ ์ฝ๋ ๋ ๊ฑด์ด ๋ฐํ๋ฉ๋๋ค.
- ํธ๋์ญ์
B๋ฅผ ์์ํฉ๋๋ค.
- TRX ID๋ 17 ์ ๋๋ค.
- ํธ๋์ญ์
B์์ users ํ
์ด๋ธ์ id=3, name=’json’์ธ ๋ ์ฝ๋๋ฅผ ์ฝ์
ํฉ๋๋ค.
- ํด๋น ํ ์ด๋ธ์ id=3์ ๊ฐญ ๋ฝ์ด ํด์ ๋์ด์ผ ์ฝ์ ๊ฐ๋ฅํ๋ฏ๋ก ํธ๋์ญ์ B๋ ๋๊ธฐ์ํ๊ฐ ๋ฉ๋๋ค.
- ํธ๋์ญ์
A์์ users ํ
์ด๋ธ์ ์๋ 1 ์ด์์ id๋ฅผ ๊ฐ์ง๋ ๋ ์ฝ๋๋ฅผ ๋ค์ ์กฐํํฉ๋๋ค.
- ์ฒ์ ๊ฒฐ๊ณผ๊ฐ ๋ง์ฐฌ๊ฐ์ง๋ก, 2๊ฑด์ ๋ ์ฝ๋๊ฐ ๋ฐํ๋ฉ๋๋ค.
์ ์๋๋ฆฌ์ค๊ฐ ์ค์ MySQL์์ ์ฌ์ฉํ๋ ์คํ ๋ฆฌ์ง ์์ง์ธ InnoDB์ ๋์์ ๋๋ค. ๊ฒฉ๋ฆฌ ์์ค์ ๊ธฐ๋ณธ๊ฐ์ด REPEATABLE READ์ด๊ณ ๋ฅ์คํธ ํค ๋ฝ์ผ๋ก Phantom Read๋ฅผ ์์ ํ ์ฐจ๋จํฉ๋๋ค.
๋ฅ์คํธ ํค ๋ฝ์ ๋ ์ฝ๋ ๋ฝ๊ณผ ๊ฐญ ๋ฝ์ ํฉ์ณ๋์ ํํ๋ฅผ ์๋ฏธํฉ๋๋ค. ์์์์ ๋์จ ๋ ์ฝ๋ ๋ ๊ฐ์ ๋ ์ฝ๋ ๋ฝ์ ๊ฑธ๊ณ ์ดํ ๋ค์ด์ฌ ๊ฐ๋ค์ ๋ํ ๊ฐญ ๋ฝ์ ๊ฑธ์ด์ ํฌํ ์ด ์๊ธฐ๋ ํ์์ ์์ ํ ์ฐจ๋จํ ๊ฒ ์ ๋๋ค. ์ด์ฒ๋ผ ๊ธฐ๋ณธ ๊ฒฉ๋ฆฌ ์์ค๋ง์ผ๋ก Dirty Read, Non-Repeatable Read, Phantom Read๋ฅผ ๋ฐฉ์งํด์ฃผ๋ ์์ ํ ๋์ด๋ผ๊ณ ๋ณผ ์ ์์ต๋๋ค.
๐งช ์ค์ต
users ํ ์ด๋ธ์ {id=1, name=’ian’}, {id=2, name=’bob’} ๋ ์ฝ๋ ๋ ๊ฐ๋ฅผ INSERT ํ๊ณ ๊ธฐ๋ณธ ๊ฒฉ๋ฆฌ ์์ค์ธ REPEATABLE READ์์ ๋ฅ์คํธ ๋ฝ ๋ฉ์ปค๋์ฆ์ด ์ด๋ป๊ฒ ๋์ํ๋์ง, ๋ฝ ํ๋์ ์ด๋ป๊ฒ ํ๋์ง ์ค์ต์ ์งํํด๋ณด๊ฒ ์ต๋๋ค.
START TRANSACTION;
SELECT * FROM users WHERE id >= 1 FOR UPDATE;
- ํธ๋์ญ์
A(์ผ์ชฝ ์๋จ)์ ์์ํ๊ณ users ํ
์ด๋ธ์์ 1์ด์์ id๋ฅผ ๊ฐ์ง ๋ ์ฝ๋๋ฅผ ๋ชจ๋ ์กฐํํฉ๋๋ค.
- FOR UPDATE๋ฅผ ํตํด ๋น๊ด์ ๋ฝ์ ๋ช ์ํฉ๋๋ค.
SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE, LOCK_DATA, LOCK_STATUS, ENGINE_TRANSACTION_ID
FROM performance_schema.data_locks;
- ํธ๋์ญ์
A๊ฐ ๋น๊ด์ ๋ฝ์ ํ๋ํ ์ดํ ๋ฝ ํํฉ์ ์กฐํํฉ๋๋ค.
- users ํ ์ด๋ธ์ ์ธํ ์ ๋ฐฐํ ๋ฝ๊ณผ ์กด์ฌํ๋ ๋ ์ฝ๋ 2๊ฐ์ ๋ํ ๋ ์ฝ๋ ๋ฝ ๊ทธ๋ฆฌ๊ณ LOCK_DATA ์นผ๋ผ์ supremum pseudo-record ๋ผ๋ ํ์๋ก ๊ฐญ ๋ฝ์ ์ก๊ณ ์์์ ํ์ธํ ์ ์์ต๋๋ค.
START TRANSACTION;
INSERT INTO users (id, name) VALUES (3, 'json');
- ํธ๋์ญ์
B(์ค๋ฅธ์ชฝ ์๋จ)์ ์์ํ๊ณ users ํ
์ด๋ธ์ id=3, name=’json’์ธ ๋ ์ฝ๋๋ฅผ ์ฝ์
ํฉ๋๋ค.
- ํธ๋์ญ์ A๊ฐ ํ๋ํ ๊ฐญ ๋ฝ์ผ๋ก์ธํด ํธ๋์ญ์ B์ INSERT๊ฐ ๋๊ธฐ ์ํ๊ฐ ๋๊ณ , ์ค์ ๋ ํ์์์ ๊ฐ์ด ์ง๋์ ํธ๋์ญ์ ์ฌ์คํํ ๊ฒ์ ์๊ตฌํฉ๋๋ค.
๐ด SERIALIZABLE
๋ชจ๋ ํธ๋์ญ์ ์ด ๋ง์น ์์ฐจ์ ์ผ๋ก ์คํ๋๋ ๊ฒ ๊ฐ์ ๋์์ ๋ณด์ฅํ๋ ๊ฒฉ๋ฆฌ ์์ค์ ๋๋ค.
- users ํ ์ด๋ธ์ {id=1, name=’ian’}, {id=2, name=’bob’} ๋ ์ฝ๋๊ฐ ์กด์ฌํฉ๋๋ค.
- ํธ๋์ญ์
A๋ฅผ ์์ํฉ๋๋ค.
- TRX ID๋ 15 ์ ๋๋ค.
- ํธ๋์ญ์
A์์ users ํ
์ด๋ธ์ ์๋ 1 ์ด์์ id๋ฅผ ๊ฐ์ง๋ ๋ ์ฝ๋๋ฅผ ์กฐํํฉ๋๋ค.
- ๊ฒฉ๋ฆฌ ์์ค์ด SERIALIZABLE์์๋ SELECT ์ฒ๋ฆฌ ๊ด๋ จ ๋ ์ฝ๋(๋ฅ์คํธ ํค ๋ฝ ํฌํจ)์ ๊ณต์ ๋ฝ์ด ์ ์ฉ๋ฉ๋๋ค.
- ๋ ์ฝ๋ ๋ ๊ฑด์ด ๋ฐํ๋ฉ๋๋ค.
- ํธ๋์ญ์
B๋ฅผ ์์ํฉ๋๋ค.
- TRX ID๋ 17 ์ ๋๋ค.
- ํธ๋์ญ์
B์์ users ํ
์ด๋ธ์ id=3, name=’json’์ธ ๋ ์ฝ๋๋ฅผ ์ฝ์
ํฉ๋๋ค.
- ํด๋น ํ ์ด๋ธ์ id=3์ ๊ฐญ ๋ฝ์ด ํด์ ๋์ด์ผ ์ฝ์ ๊ฐ๋ฅํ๋ฏ๋ก ํธ๋์ญ์ B๋ ๋๊ธฐ์ํ๊ฐ ๋ฉ๋๋ค.
- ํธ๋์ญ์
A์์ users ํ
์ด๋ธ์ ์๋ 1 ์ด์์ id๋ฅผ ๊ฐ์ง๋ ๋ ์ฝ๋๋ฅผ ๋ค์ ์กฐํํฉ๋๋ค.
- ์ฒ์ ๊ฒฐ๊ณผ๊ฐ ๋ง์ฐฌ๊ฐ์ง๋ก, 2๊ฑด์ ๋ ์ฝ๋๊ฐ ๋ฐํ๋ฉ๋๋ค.
๐งช ์ค์ต
users ํ ์ด๋ธ์ {id=1, name=’ian’}, {id=2, name=’bob’} ๋ ์ฝ๋ ๋ ๊ฐ๋ฅผ INSERT ํ๊ณ SERIALIZABLE์์ ๋ฝ์ ์ด๋ป๊ฒ ํ๋ํ์ฌ ์์ฐจ์ ์ธ ์ฒ๋ฆฌ์ฒ๋ผ ๋ณด์ด๊ฒ ํ๋์ง ์ค์ตํด๋ณด๊ฒ ์ต๋๋ค.
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
START TRANSACTION;
SELECT * FROM users WHERE id >= 1;
- ํธ๋์ญ์
A(์ผ์ชฝ)์ ์์ํ๊ธฐ ์ ์ ๊ฒฉ๋ฆฌ ์์ค์ SERIALIZABLE๋ก ์ค์ ํฉ๋๋ค.
- InnoDB์ ๊ธฐ๋ณธ ๊ฒฉ๋ฆฌ ์์ค์ REPEATABLE READ์ด๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ์ค์ ์ด ํ์ํฉ๋๋ค.
- ํธ๋์ญ์
A(์ผ์ชฝ)๋ฅผ ์์ํ๊ณ users ํ
์ด๋ธ์์ 1์ด์์ id๋ฅผ ๊ฐ์ง ๋ ์ฝ๋๋ฅผ ๋ชจ๋ ์กฐํํฉ๋๋ค.
- ๊ฒฉ๋ฆฌ ์์ค์ SERIALIZABLE๋ก ์ค์ ํ๊ธฐ ๋๋ฌธ์ ๋ช ์์ ์ผ๋ก ๋ฝ ํ๋์ ํ์ง ์์๋ SELECT์์ ๊ณต์ ๋ฝ์ ํ๋ํฉ๋๋ค.
SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE, LOCK_DATA, LOCK_STATUS, ENGINE_TRANSACTION_ID
FROM performance_schema.data_locks;
- ํธ๋์ญ์
A๊ฐ SELECT ํ ์ดํ ๋ฝ ํํฉ์ ์กฐํํฉ๋๋ค.
- users ํ ์ด๋ธ์ ์ธํ ์ ๊ณต์ ๋ฝ๊ณผ ์กด์ฌํ๋ ๋ ์ฝ๋ 2๊ฐ์ ๋ํ ๋ ์ฝ๋ ๋ฝ ๊ทธ๋ฆฌ๊ณ LOCK_DATA ์นผ๋ผ์ supremum pseudo-record ๋ผ๋ ํ์๋ก ๊ฐญ ๋ฝ์ ์ก๊ณ ์์์ ํ์ธํ ์ ์์ต๋๋ค.
START TRANSACTION;
INSERT INTO users (id, name) VALUES (3, 'json');
- ํธ๋์ญ์
B(์ค๋ฅธ์ชฝ ์๋จ)์ ์์ํ๊ณ users ํ
์ด๋ธ์ id=3, name=’json’์ธ ๋ ์ฝ๋๋ฅผ ์ฝ์
ํฉ๋๋ค.
- ํธ๋์ญ์ A๊ฐ ํ๋ํ ๊ฐญ ๋ฝ์ผ๋ก์ธํด ํธ๋์ญ์ B์ INSERT๊ฐ ๋๊ธฐ ์ํ๊ฐ ๋๊ณ , ์ค์ ๋ ํ์์์ ๊ฐ์ด ์ง๋์ ํธ๋์ญ์ ์ฌ์คํํ ๊ฒ์ ์๊ตฌํฉ๋๋ค.
๐๐ป ๋ง์น๋ฉฐ
MySQL์์ ์ฌ์ฉํ๋ ์คํ ๋ฆฌ์ง ์์ง, InnoDB์ ๊ฒฉ๋ฆฌ ์์ค์ ๋ฐ๋ฅธ ๋์๋ค์ ์์ธํ ์ดํด๋ณด์์ต๋๋ค.
์ฒ์ ๊ฒฉ๋ฆฌ ์์ค์ ๊ฐ๋ ์ ์ ํ์ ๋, ํ์ค ๋ด์ฉ์ ๊ธฐ๋ฐ์ผ๋ก ๋์ ํด์ ์๊ฐํ๊ธฐ ๋๋ฌธ์ InnoDB์ ๋์ ์ดํด๋ฅผ ํ์ง ๋ชปํ์์ต๋๋ค. ํ์ค์ด๋ผ๋๊ฒ ๋ฐ๋์ ๋ฐ๋ฅผ ํ์ ์๋ ๊ฒ์ด๊ณ ์ธํฐํ์ด์ค ๊ฐ์ ์ญํ ์ด๊ธฐ ๋๋ฌธ์ ์ค์ RDBMS์ ๋์์ ํ์ค์ ๊ธฐ๋ฐ์ผ๋ก ํ๋, ๊ตฌํ์ฒด(์ค์ ์ ํ)์ ๋ฐ๋ผ ์กฐ๊ธ์ฉ ๋ค๋ฅผ ์ ์๊ณ ๋ ํ๋ฅญํ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ค๋ ๊ฒ์ ์๊ฒ ๋์์ต๋๋ค.
์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. ๊ถ๊ธํ ์ ์ ๋ํ ์ง๋ฌธ ํ์ํฉ๋๋ค!
๋ถ์กฑํ, ์๋ชป๋ ์ค๋ช ์ด ์๋ค๋ฉด ์๋ ค์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค ๐๐ป