ํธ๋์ญ์
ํธ๋์ญ์ ¶
1. ํธ๋์ญ์ ๊ฐ๋ ¶
ํธ๋์ญ์ ์ ํ๋์ ๋ ผ๋ฆฌ์ ์์ ๋จ์๋ฅผ ๊ตฌ์ฑํ๋ ์ฐ์ฐ๋ค์ ์งํฉ์ ๋๋ค.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๊ณ์ข ์ด์ฒด ํธ๋์ญ์
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 1. A ๊ณ์ข์์ 10๋ง์ ์ฐจ๊ฐ โ
โ 2. B ๊ณ์ข์ 10๋ง์ ์ถ๊ฐ โ
โ โ ๋ ๋ค ์ฑ๊ณตํ๊ฑฐ๋, ๋ ๋ค ์คํจํด์ผ ํจ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
2. ACID ์์ฑ¶
| ์์ฑ | ์๋ฌธ | ์ค๋ช |
|---|---|---|
| ์์์ฑ | Atomicity | ์ ๋ถ ์ฑ๊ณต ๋๋ ์ ๋ถ ์คํจ |
| ์ผ๊ด์ฑ | Consistency | ํธ๋์ญ์ ์ ํ๋ก ๋ฐ์ดํฐ ์ผ๊ด์ฑ ์ ์ง |
| ๊ฒฉ๋ฆฌ์ฑ | Isolation | ๋์ ์คํ ํธ๋์ญ์ ๊ฐ ๊ฐ์ญ ๋ฐฉ์ง |
| ์ง์์ฑ | Durability | ์๋ฃ๋ ํธ๋์ญ์ ์ ์๊ตฌ ์ ์ฅ |
3. ๊ธฐ๋ณธ ํธ๋์ญ์ ๋ช ๋ น¶
BEGIN / COMMIT / ROLLBACK¶
-- ํธ๋์ญ์
์์
BEGIN;
-- ๋๋
START TRANSACTION;
-- ์์
์ํ
UPDATE accounts SET balance = balance - 100000 WHERE id = 1;
UPDATE accounts SET balance = balance + 100000 WHERE id = 2;
-- ์ปค๋ฐ (๋ณ๊ฒฝ์ฌํญ ํ์ )
COMMIT;
-- ๋๋ ๋กค๋ฐฑ (๋ณ๊ฒฝ์ฌํญ ์ทจ์)
ROLLBACK;
์ค์ต ์์ ¶
-- ํ
์ด๋ธ ์์ฑ
CREATE TABLE accounts (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
balance NUMERIC(12, 2) DEFAULT 0
);
INSERT INTO accounts (name, balance) VALUES
('๊น์ฒ ์', 1000000),
('์ด์ํฌ', 500000);
-- ์ด์ฒด ํธ๋์ญ์
BEGIN;
UPDATE accounts SET balance = balance - 100000 WHERE name = '๊น์ฒ ์';
UPDATE accounts SET balance = balance + 100000 WHERE name = '์ด์ํฌ';
-- ํ์ธ
SELECT * FROM accounts;
-- ํ์ ๋๋ ์ทจ์
COMMIT; -- ๋๋ ROLLBACK;
4. ์๋ ์ปค๋ฐ (Autocommit)¶
psql์ ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ ์ปค๋ฐ ๋ชจ๋์ ๋๋ค.
-- ์๋ ์ปค๋ฐ ๋ชจ๋์์ ๊ฐ ๋ฌธ์ฅ์ ๊ฐ๋ณ ํธ๋์ญ์
INSERT INTO accounts (name, balance) VALUES ('๋ฐ๋ฏผ์', 300000);
-- ์ฆ์ ์ปค๋ฐ๋จ
-- ์๋ ์ปค๋ฐ ๋นํ์ฑํ
\set AUTOCOMMIT off
-- ์ด์ ๋ช
์์ COMMIT ํ์
INSERT INTO accounts (name, balance) VALUES ('์ต์ง์', 400000);
COMMIT;
-- ์๋ ์ปค๋ฐ ๋ค์ ํ์ฑํ
\set AUTOCOMMIT on
5. SAVEPOINT¶
ํธ๋์ญ์ ๋ด์์ ๋ถ๋ถ ๋กค๋ฐฑ ์ง์ ์ ๋ง๋ญ๋๋ค.
BEGIN;
INSERT INTO accounts (name, balance) VALUES ('์ ๊ท1', 100000);
SAVEPOINT sp1;
INSERT INTO accounts (name, balance) VALUES ('์ ๊ท2', 200000);
SAVEPOINT sp2;
INSERT INTO accounts (name, balance) VALUES ('์ ๊ท3', 300000);
-- sp2๋ก ๋กค๋ฐฑ (์ ๊ท3๋ง ์ทจ์)
ROLLBACK TO SAVEPOINT sp2;
-- sp1์ผ๋ก ๋กค๋ฐฑ (์ ๊ท2, ์ ๊ท3 ์ทจ์)
ROLLBACK TO SAVEPOINT sp1;
-- ์ ์ฒด ์ปค๋ฐ (์ ๊ท1๋ง ์ ์ฅ)
COMMIT;
SAVEPOINT ๊ด๋ฆฌ¶
-- SAVEPOINT ํด์
RELEASE SAVEPOINT sp1;
-- SAVEPOINT ๋ฎ์ด์ฐ๊ธฐ (๊ฐ์ ์ด๋ฆ์ผ๋ก ์ฌ์์ฑ)
SAVEPOINT mypoint;
-- ... ์์
...
SAVEPOINT mypoint; -- ์ ์ง์ ์ผ๋ก ๋์ฒด
6. ํธ๋์ญ์ ๊ฒฉ๋ฆฌ ์์ค¶
๋์์ ์คํ๋๋ ํธ๋์ญ์ ๊ฐ์ ๊ฒฉ๋ฆฌ ์ ๋๋ฅผ ๊ฒฐ์ ํฉ๋๋ค.
๊ฒฉ๋ฆฌ ์์ค ์ข ๋ฅ¶
| ์์ค | Dirty Read | Non-repeatable Read | Phantom Read |
|---|---|---|---|
| READ UNCOMMITTED | ๊ฐ๋ฅ | ๊ฐ๋ฅ | ๊ฐ๋ฅ |
| READ COMMITTED | ๋ฐฉ์ง | ๊ฐ๋ฅ | ๊ฐ๋ฅ |
| REPEATABLE READ | ๋ฐฉ์ง | ๋ฐฉ์ง | ๊ฐ๋ฅ* |
| SERIALIZABLE | ๋ฐฉ์ง | ๋ฐฉ์ง | ๋ฐฉ์ง |
*PostgreSQL์ REPEATABLE READ๋ Phantom Read๋ ๋ฐฉ์ง
PostgreSQL ๊ธฐ๋ณธ๊ฐ¶
PostgreSQL์ ๊ธฐ๋ณธ ๊ฒฉ๋ฆฌ ์์ค์ READ COMMITTED์ ๋๋ค.
๊ฒฉ๋ฆฌ ์์ค ์ค์ ¶
-- ํธ๋์ญ์
๋ณ ์ค์
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- ๋๋
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- ์ธ์
์ ์ฒด ์ค์
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- ํ์ฌ ๊ฒฉ๋ฆฌ ์์ค ํ์ธ
SHOW transaction_isolation;
7. ๋์์ฑ ๋ฌธ์ ¶
Dirty Read (๋ํฐ ๋ฆฌ๋)¶
์ปค๋ฐ๋์ง ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๋ ๋ฌธ์ โ PostgreSQL์์๋ ๋ฐ์ํ์ง ์์
Non-repeatable Read (๋น๋ฐ๋ณต ์ฝ๊ธฐ)¶
๊ฐ์ ํธ๋์ญ์ ๋ด์์ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ ๋ฒ ์ฝ์์ ๋ ๋ค๋ฅธ ๊ฐ์ด ๋์ค๋ ๋ฌธ์
-- ํธ๋์ญ์
A
BEGIN;
SELECT balance FROM accounts WHERE id = 1; -- 1000000
-- ํธ๋์ญ์
B๊ฐ ์
๋ฐ์ดํธํ๊ณ ์ปค๋ฐ
-- UPDATE accounts SET balance = 900000 WHERE id = 1; COMMIT;
SELECT balance FROM accounts WHERE id = 1; -- 900000 (๋ค๋ฅธ ๊ฐ!)
COMMIT;
Phantom Read (ํฌํ ๋ฆฌ๋)¶
๊ฐ์ ์กฐ๊ฑด์ผ๋ก ์กฐํํ์ ๋ ํ์ ๊ฐ์๊ฐ ๋ฌ๋ผ์ง๋ ๋ฌธ์
-- ํธ๋์ญ์
A
BEGIN;
SELECT COUNT(*) FROM accounts WHERE balance > 500000; -- 2๊ฐ
-- ํธ๋์ญ์
B๊ฐ ์ ํ ์ฝ์
ํ๊ณ ์ปค๋ฐ
-- INSERT INTO accounts VALUES (...); COMMIT;
SELECT COUNT(*) FROM accounts WHERE balance > 500000; -- 3๊ฐ (์ ๋ น ํ!)
COMMIT;
8. ๊ฒฉ๋ฆฌ ์์ค๋ณ ๋์¶
READ COMMITTED (๊ธฐ๋ณธ)¶
BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- ๋ค๋ฅธ ํธ๋์ญ์
์ด ์ปค๋ฐํ ๋ณ๊ฒฝ์ฌํญ์ ์ฆ์ ๋ณผ ์ ์์
SELECT * FROM accounts; -- ์ต์ ์ปค๋ฐ๋ ๋ฐ์ดํฐ
COMMIT;
REPEATABLE READ¶
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- ํธ๋์ญ์
์์ ์์ ์ ์ค๋
์ท์ ๋ด
SELECT * FROM accounts;
-- ๋ค๋ฅธ ํธ๋์ญ์
์ด ์ปค๋ฐํด๋ ๊ฐ์ ๊ฒฐ๊ณผ
SELECT * FROM accounts; -- ๋์ผ
COMMIT;
SERIALIZABLE¶
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- ๊ฐ์ฅ ์๊ฒฉํ ๊ฒฉ๋ฆฌ
-- ์ง๋ ฌํ ์ถฉ๋ ์ ์ค๋ฅ ๋ฐ์ ๊ฐ๋ฅ
SELECT * FROM accounts WHERE balance > 500000;
UPDATE accounts SET balance = balance + 10000 WHERE id = 1;
COMMIT;
-- ERROR: could not serialize access due to concurrent update
-- (๋ค๋ฅธ ํธ๋์ญ์
๊ณผ ์ถฉ๋ ์)
9. ์ ๊ธ (Locking)¶
ํ ์์ค ์ ๊ธ¶
-- SELECT FOR UPDATE: ์กฐํํ๋ฉด์ ์ ๊ธ
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- ๋ค๋ฅธ ํธ๋์ญ์
์ ์ด ํ์ ์์ /์ญ์ ๋ถ๊ฐ
UPDATE accounts SET balance = balance - 100000 WHERE id = 1;
COMMIT;
-- SELECT FOR SHARE: ๊ณต์ ์ ๊ธ (์ฝ๊ธฐ๋ ํ์ฉ, ์ฐ๊ธฐ ๋ถ๊ฐ)
SELECT * FROM accounts WHERE id = 1 FOR SHARE;
์ ๊ธ ์ต์ ¶
-- ๋๊ธฐํ์ง ์๊ณ ์คํจ
SELECT * FROM accounts WHERE id = 1 FOR UPDATE NOWAIT;
-- ์ง์ ๋ ์๊ฐ๋ง ๋๊ธฐ
SELECT * FROM accounts WHERE id = 1 FOR UPDATE SKIP LOCKED;
ํ ์ด๋ธ ์์ค ์ ๊ธ¶
-- ๋ช
์์ ํ
์ด๋ธ ์ ๊ธ (๋๋ฌผ๊ฒ ์ฌ์ฉ)
LOCK TABLE accounts IN EXCLUSIVE MODE;
10. ๊ต์ฐฉ์ํ (Deadlock)¶
๋ ํธ๋์ญ์ ์ด ์๋ก์ ์ ๊ธ์ ๊ธฐ๋ค๋ฆฌ๋ ์ํ
-- ํธ๋์ญ์
A
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
-- id=1 ์ ๊ธ
-- ํธ๋์ญ์
B
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 2;
-- id=2 ์ ๊ธ
-- ํธ๋์ญ์
A
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- id=2 ๋๊ธฐ...
-- ํธ๋์ญ์
B
UPDATE accounts SET balance = balance + 100 WHERE id = 1;
-- id=1 ๋๊ธฐ... โ ๊ต์ฐฉ์ํ!
-- PostgreSQL์ด ์๋์ผ๋ก ํ ํธ๋์ญ์
์ ์ค๋จ์ํด
-- ERROR: deadlock detected
๊ต์ฐฉ์ํ ๋ฐฉ์ง¶
-- ํญ์ ๊ฐ์ ์์๋ก ์ ๊ธ ํ๋
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- ํญ์ ์์ id ๋จผ์
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
11. ์ค์ต ์์ ¶
์ค์ต 1: ๊ธฐ๋ณธ ํธ๋์ญ์ ¶
-- ๊ณ์ข ์ด์ฒด
CREATE OR REPLACE PROCEDURE transfer(
from_id INTEGER,
to_id INTEGER,
amount NUMERIC
)
AS $$
BEGIN
-- ์ถ๊ธ
UPDATE accounts SET balance = balance - amount WHERE id = from_id;
-- ์์ก ํ์ธ
IF (SELECT balance FROM accounts WHERE id = from_id) < 0 THEN
RAISE EXCEPTION '์์ก ๋ถ์กฑ';
END IF;
-- ์
๊ธ
UPDATE accounts SET balance = balance + amount WHERE id = to_id;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
$$ LANGUAGE plpgsql;
-- ์ฌ์ฉ
CALL transfer(1, 2, 100000);
์ค์ต 2: SAVEPOINT ํ์ฉ¶
BEGIN;
-- ๊ธฐ๋ณธ ๋ฐ์ดํฐ ์ฝ์
INSERT INTO orders (user_id, amount) VALUES (1, 50000);
SAVEPOINT order_created;
-- ์ฌ๊ณ ์ฐจ๊ฐ ์๋
UPDATE products SET stock = stock - 1 WHERE id = 10;
-- ์ฌ๊ณ ํ์ธ
IF (SELECT stock FROM products WHERE id = 10) < 0 THEN
ROLLBACK TO SAVEPOINT order_created;
-- ์ฃผ๋ฌธ์ ์ ์งํ๋ ์ฌ๊ณ ์ฐจ๊ฐ๋ง ์ทจ์
END IF;
COMMIT;
์ค์ต 3: ๊ฒฉ๋ฆฌ ์์ค ํ ์คํธ¶
ํฐ๋ฏธ๋ 2๊ฐ๋ฅผ ์ด์ด ํ ์คํธํฉ๋๋ค.
-- ํฐ๋ฏธ๋ 1
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT * FROM accounts;
-- ํฐ๋ฏธ๋ 2
UPDATE accounts SET balance = balance + 50000 WHERE id = 1;
COMMIT;
-- ํฐ๋ฏธ๋ 1
SELECT * FROM accounts; -- ๋ณ๊ฒฝ ์ ๊ฐ (์ค๋
์ท)
COMMIT;
SELECT * FROM accounts; -- ์ด์ ๋ณ๊ฒฝ๋ ๊ฐ ๋ณด์
์ค์ต 4: FOR UPDATE ์ ๊ธ¶
-- ์ฌ๊ณ ํ์ธ ํ ์ฐจ๊ฐ (๋์์ฑ ์์ )
BEGIN;
-- ์ ๊ธ์ ๊ฑธ๋ฉฐ ์กฐํ
SELECT stock FROM products WHERE id = 1 FOR UPDATE;
-- ์ฌ๊ณ ํ์ธ ๋ฐ ์ฐจ๊ฐ
UPDATE products
SET stock = stock - 1
WHERE id = 1 AND stock > 0;
COMMIT;
12. ํธ๋์ญ์ ๋ชจ๋ํฐ๋ง¶
-- ํ์ฌ ์คํ ์ค์ธ ํธ๋์ญ์
ํ์ธ
SELECT
pid,
now() - xact_start AS duration,
query,
state
FROM pg_stat_activity
WHERE xact_start IS NOT NULL;
-- ์ ๊ธ ๋๊ธฐ ์ค์ธ ์ฟผ๋ฆฌ ํ์ธ
SELECT
blocked.pid AS blocked_pid,
blocking.pid AS blocking_pid,
blocked.query AS blocked_query
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking ON blocking.pid = ANY(pg_blocking_pids(blocked.pid));
๋ค์ ๋จ๊ณ¶
12_Triggers.md์์ ํธ๋ฆฌ๊ฑฐ๋ฅผ ๋ฐฐ์๋ด ์๋ค!