์ ๋ฆฌ์ค - 3
by JiwonDev- Transcation
๋ ผ๋ฆฌ์ ์ธ ์์ ๋จ์๋ก, ์ ๋ถ ์ฒ๋ฆฌ(commit)๋๊ฑฐ๋ ์ฒ๋ฆฌ๋์ง ์๋(rollback) ์์์ฑ์ ๋ณด์ฅํด์ผ ํ๋ค. - Lock
์๋ก ๋ค๋ฅธ ์์ ์์ ๊ฐ์ ์์์ด ๋์์ ํ์ํ๋ฉด ์ค๋ ๋๊ฐ์ ๊ฒฝํฉ(Race)๊ฐ ์ผ์ด๋๋ค.
์ด ๋ ๊ฐ์์ ์์ ์ ์ํฅ์ด ๋ผ์น์ง ์๋๋ก ๋์์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด Thread Lock์ ๊ฑธ์ด ์ค๋ ๋๋ฅผ ๋๊ธฐ์ํจ๋ค.
MySQL์์ ์ฌ์ฉํ๋ Lock์ 2๊ฐ์ง๊ฐ ์๋ค.
- MySQL Engine Level Lock
- Storage Engine Level Lock
๐ ์คํ ๋ฆฌ์ง ์์ง ๋ ๋ฒจ ๋ฝ
์คํ ๋ฆฌ์ง ์์ง ๋ ๋ฒจ (InnoDB)์์ ์ ๊ณตํ๋ Lock์ 2๊ฐ์ง ๋ฐฉ์์ด ์๋ค.
๋ณ๋ค๋ฅธ ์ค์ ์ ํ์ง ์๋๋ค๋ฉด ๋น๊ด์ Lock (Pessimistic locking)์ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํ๋ค.
- ๋น๊ด์ ์ธ(Pessimistic) lock
โก ์๋ฌดํผ ํธ๋์ญ์ ์ ์ถฉ๋์ด ๋ฐ์ํ๋ค๊ณ ๊ฐ์ ํ๊ณ , ์ผ๋จ ๋ฝ์ ๊ฑธ๊ณ ์ฟผ๋ฆฌ๋ฅผ ์ํํ๋ ๋ฐฉ๋ฒ - ๋๊ด์ ์ธ(Optimistic) lock
โก ์ผ๋จ ๋ฝ ์์ด ์ฟผ๋ฆฌ ์ํ์ ์งํํ๊ณ , ์๋ฃ๋์์ ๋ ๋ค๋ฅธ ํธ๋์ญ์ ์์ ์ถฉ๋์ด ์์๋์ง ํ์ธํ๊ณ , ๋ฌธ์ ๊ฐ ์๋ค๋ฉด ์ถฉ๋ ๋ ํธ๋์ญ์ ์ ๋กค๋ฐฑํ๋ ๋ฐฉ์ (๋ฒ์ ์ ๋ณด๋ฅผ ์ด์ฉํ์ฌ ์ ๋ฐ์ดํธ ํ๋ ๋ฐฉ๋ฒ)
๋๊ท๋ชจ ํธ๋ํฝ์ ์ฒ๋ฆฌํ๋ ์๋น์ค๋ ๋ฌด๊ฒฐ์ฑ์ ์ด์ง ํฌ๊ธฐํ๋๋ผ๋ lock์ ์ต์ํํ์ฌ ์ฑ๋ฅ์ ๋์ด๊ธฐ ์ํด ๋๊ด์ ์ธ Lock์ผ๋ก ๋ณ๊ฒฝํ์ฌ ์ฌ์ฉํ๊ธฐ๋ ํ๋ค.
@ Lock์ ๊ฑฐ๋ ๋ฒ์
๋ํ InnoDB๋ Lock์ ๊ฑฐ๋ ๋ฒ์๋ฅผ ์์ธํ๊ฒ ์ง์ ํ ์ ์๋ค. MyISBM๊ณผ ๋ค๋ฅด๊ฒ ๋ ์ฝ๋ ๋ฝ๋ ๊ฐ๋ฅ.
- Record Lock
SELECT col FROM table WHERE col=10 FOR UPDATE; ์ผ ๋ col=10 ์ธ ๋ ์ฝ๋์๋ง ๋ฝ์ ๊ฑด๋ค๊ณ ์ดํดํ๋ฉด ๋๋ค.
๋ค๋ง ์ค์ ํ ์ด๋ธ์ ๋ ์ฝ๋์ Lock์ ๊ฑฐ๋๊ฒ ์๋๋ผ, ์ธ๋ฑ์ค ๋ ์ฝ๋(=์ธ๋ฑ์ค)์ Lock์ ๊ฑด๋ค.
๋ง์ฝ ๋ฐ๋ก ์์ฑํ ์ธ๋ฑ์ค๊ฐ ์๋ค๋ฉด InnoDB๊ฐ ์์ฒด์ ์ผ๋ก ์์ฑํ ํด๋ฌ์คํฐ ์ธ๋ฑ์ค๋ฅผ ์ด์ฉํด Lock์ ๊ฑด๋ค. - Gap Lock (๋ฒ์, ์ค๊ฐ ๊ฐญ Lock)
์ธ๋ฑ์ค ๋ ์ฝ๋์ ์ธ์ ํ ์/๋ค ๊ณต๊ฐ์ Lock์ ๊ฑด๋ค. ๋จ๋ ์ผ๋ก ์ฌ์ฉ๋์ง๋ ์๊ณ ๋ค์ ์ค๋ช ํ Next Key Lock์ ์ฌ์ฉ.
Gap Lock์ ์ธ๋ํค๋ ์ค๋ณตํค๋ฅผ ๊ฒ์ฌํ๋ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด ๋ณดํต REPEATABLE_READ ์ด์์ ๊ฒฉ๋ฆฌ์์ค์์ ๋ฐ์ํ๋ค.
๋ค๋ง ๊ธฐ๋ณธ ํค๋ ์ ๋ํฌ ํค์ ์ํ ๋ณ๊ฒฝ ์์ ์ Gap Lock ์์ด Record Lock์ผ๋ก ์ธ๋ฑ์ค ๋ ์ฝ๋์๋ง ๋ฝ์ ๊ฑด๋ค. - Next Key Lock
Record lock + Gap lock ์ ํํ์ด๋ค. ์ธ๋ฑ์ค ๋ ์ฝ๋๋ ์ ๊ตฌ๊ณ , ์ ๋ค ๊ฐญ๋ ์ ๊ทผ๋ค.
SELECT c1 FROM t WHERE c1 BETWEEN 10 AND 20 FOR UPDATE; ์ผ ๋ c1=15 ์ ๊ฐ์ ๋์์ ํ๋ ํธ๋์ญ์ ์ด ์๋ค๋ฉด ๋ง๋๋ค. ๋ฐ๋์ ์ธ๋ฑ์ค ๋ฒ์์๋ง ๋ฝ์ ๊ฑฐ๋๊ฒ ์๋๋ผ, [์ธ๋ฑ์ค ๋ฒ์ ์ฌ์ด์ ์๋ ๊ฐญ]์ ๊ฐ์ด ๋ฝ์ ๊ฑด๋ค.
์๋ฅผ ๋ค์ด c1=10, c1=8 ์ด๋ ๊ฒ 2๊ฐ์ง ๋ ์ฝ๋์ Lock ์ด ๊ฑธ๋ ธ๋ค๋ฉด, ์ค๊ฐ์ ์๋ c1=9 ์์ ๋ Lock์ผ๋ก ๋งํ์๋ค.
๊ตณ์ด ๋ณํ๊ฐ์ Next key lock์ ์ฌ์ฉํ๋ ์ด์ ๋ REPEATABLE_READ์ ์ฌ์ฉ์ ๊ฐ์ ํ๊ธฐ ๋๋ฌธ์ด๊ณ , ์ด๋ฅผ ์ฌ์ฉํ๋ ์ด์ ๋ ๋ณต์ ๋ฅผ ์ํ ๋ฐ์ด๋๋ฆฌ ๋ก๊ทธ ๋๋ฌธ์ด๋ค.
MySQL5.1 ์ดํ์๋ ๋ฐ์ด๋๋ฆฌ ๋ก๊ทธ๊ฐ ํ์ฑํ๋๋ฉด REPEATABLE_READ ์ด์์ ๋ ๋ฒจ์ ๊ฐ์ ํ๋ค. ๊ทธ๋์ Next Key Lock์ผ๋ก
- Auto Increment lock
MySQL์์ ์๋ ์ฆ๊ฐํ๋ ์ซ์ ๊ฐ์ ์ป๊ธฐ์ํด Auto Increment๋ฅผ ์ ์ ํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค. ์ด ๋ ์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ Insert๋ฅผ ๋ชปํ๊ฒ ๊ฑธ์ด์ฃผ๋ Lock์ด๋ค.
@ InnoDB๋ ์ธ๋ฑ์ค ๋ ์ฝ๋ ๋ฝ์ ๋๋ค.
InnoDB์ ๋ ์ฝ๋๋ฝ์ '์ธ๋ฑ์ค ๋ ์ฝ๋' ๋ฝ์ด๋ค. ์ด๋ ๋งค์ฐ ํฐ ์ฐจ์ด์ธ๋ฐ, ์๋์ ์์ ๋ฅผ ํตํด ์์๋ณด์.
์ฐ๋ฆฌ์ ์ ์ ๊ฐ๋ฐ์ A๋ first_name ์นผ๋ผ์๋ง ์ธ๋ฑ์ค๋ฅผ ์ ์ฉํ์๊ณ , ์๋์ ๊ฐ์ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ์๋ค.
์ด ๋ [ first_name = 'KIM' ]์ธ ๋ ์ฝ๋๋ 300๊ฑด
[ first_name = 'KIM' , last_name = 'WON' ]์ธ ๋ ์ฝ๋๋ ๋จ 1๊ฑด์ด๋ผ๊ณ ๊ฐ์ ํด๋ณด์.
SELECT COUNT(*) FROM employees WHERE first_name='KIM'; -- 300๊ฑด ์กฐํ๋๋ค๊ณ ๊ฐ์
SELECT COUNT(*) FROM employees WHERE first_name='KIM' AND last_name='WON'; -- 1๊ฑด ์กฐํ๋๋ค๊ณ ๊ฐ์
UPDATE employees SET hire_date=NOW() WHERE first_name='KIM' AND last_name='WON'; -- ์ด๋์ ๋ฝ์ด ๊ฑธ๋ฆด๊น?
first_name ์นผ๋ผ์๋ง ์ธ๋ฑ์ค๊ฐ ์ ์ฉ๋์ด์๋ค๊ณ ๊ฐ์ ํด๋ณด์. 3๋ฒ์งธ Update ์ฟผ๋ฆฌ์์ Lock์ด ๊ฑธ๋ฆฌ๋ ๋ ์ฝ๋๋ ๋ช ๊ฐ์ผ๊น?
์ ๋ต์ 300๊ฐ์ ๋ ์ฝ๋์ด๋ค. ์ค์ ๋ ์ฝ๋๊ฐ ์๋๋ผ ์ธ๋ฑ์ค ๋ ์ฝ๋์ ๋ฝ์ด ๊ฑธ๋ฆฌ๊ธฐ ๋๋ฌธ.
[ first_name = 'KIM' , last_name = 'WON' ]์ธ ์ฌ๋์ ๋จ 1๊ฑด์ด์ง๋ง, ์ธ๋ฑ์ค์ Next key Lock์ ๊ฑฐ๋ ๊ธฐ๋ณธ ์ ์ฑ ์ first_name์ ๋ฝ์ด ๊ฑธ๋ฆฌ๋ฏ๋ก 300๊ฑด ์ ์ฒด์ Lock์ ๊ฑธ๊ฒ๋๊ณ , ์ค์ ์์ ์ ๋จ 1๊ฑด๋ง ์ด๋ฃจ์ด์ง๋ ๊ฒ์ด๋ค.
๋ง์ฝ ์ธ๋ฑ์ค๋ฅผ ์์ ๋ง๋ค์ง ์์๋ค๋ฉด ์ด๋จ๊น? ์ด ๊ฒฝ์ฐ InnoDB๋ ๋ ์ฝ๋ ๋จ์์ Next Key Lock์ ๊ฑฐ๋๊น ๊ด์ฐฎ๋ค๊ณ ์ฐฉ๊ฐํ๊ณ ์๋ค๋ฉด, ์ค์ ๋์์ ํ ์ด๋ธ ์ ์ฒด ๋ ์ฝ๋๋ฅผ ์ ๊ทธ๊ฒ ๋๋ค.
๋ ์ฝ๋ ๋จ์์ Lock์ ์ ๊ณตํ์ง ์์๋ค๋ฉด, ์ ๊ธ์ ๋์์ด ํ ์ด๋ธ์ด๋ผ์ ๋ฌธ์ ์ ์์ธ์ด ์ฝ๊ฒ ๋ฐ๊ฒฌ๋๊ณ , ํด๊ฒฐํ ์ ์๋ค.
ํ์ง๋ง ๋ ์ฝ๋ ์์ค ์ ๊ธ์ ํด๋น ์ฟผ๋ฆฌ, ํด๋น ๋ ์ฝ๋๊ฐ ๋ง์ด ์ฌ์ฉ๋์ง ์๋๋ค๋ฉด ์ ๋ฐ๊ฒฌ๋์ง ์๋๋ค.
๊ทธ๋์ MySQL5.1๋ถํฐ๋ information_schema์์ ์๋ INNODB_TRX์์ ๋ ์ฝ๋ ์ ๊ธ์ ์ ๊ธ ๋๊ธฐ์ ๋ํ ์กฐํ๊ฐ ๊ฐ๋ฅํ์๋ค. ์ฐธ๊ณ ๋ก MySQL8.0 ๋ถํฐ๋ ๊ธฐ์กด์ ์คํค๋ง ํ ์ด๋ธ์ ์ ๊ฑฐ(Deprecated)ํ๊ณ performance_scheman์ data_locks, data_lock_waits ๋ผ๋ ์๋ก์ด ํ ์ด๋ธ๋ก ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ค.
@ ์๋ธ์ฟผ๋ฆฌ SELECT LOCK, ๋ฐ๋๋ฝ
InnoDB ๋ฐ๋๋ฝ ๋ฐ์ ๊ฒฝ์ฐ
1. ๋จ์ ์กฐํ๋ ๋น์ฐํ ๋ฝ์ด ๊ฑธ๋ฆฌ์ง ์์ต๋๋ค. ๋ค๋ฅธ ํธ๋์ญ์ ์ด ๋ณ๊ฒฝ์ค์ด๋ผ๋ฉด ์ค์ ๊ฐ์ด ์๋ Undo ๋ ์ฝ๋๋ฅผ ์ฝ์ต๋๋ค.
SELECT ... FROM
2. ํ์ง๋ง ์๋์ ๊ฐ์ ๊ฒฝ์ฐ๋ ์ด๋จ๊น์? ์๋ธ์ฟผ๋ฆฌ์ Select๋ Shared Lock์ด ๊ฑธ๋ฆฝ๋๋ค.
์๋์ 3์ฟผ๋ฆฌ๊ฐ ์ด ์์๋๋ก ์ผ์ด๋๋ค๋ฉด ์์ํ ํด๊ฒฐ๋์ง ์๋ ๋ฐ๋๋ฝ ์ํ์ ๋น ์ง๊ฒ ๋ฉ๋๋ค.
# session 1 - ์ ์ ํ
์ด๋ธ๊ณผ accounts ํ
์ด๋ธ์์ ๊ณ์ ์ ์ญ์ ํ๋ ์ธ์
DELETE
FROM user_table
WHERE account_id = 99; # X-LOCK
# session 2 - VIP ๋ฅผ ์ฐพ์์ ์ต์
์ ์
๋ฐ์ดํธํ๋ ์ธ์
UPDATE user_table
SET vip_flag = 1
WHERE account_id in (SELECT account_id FROM accounts WHERE amount >= 100000000) # S-LOCK
# session 1 - ์ ์ ํ
์ด๋ธ ์ฟผ๋ฆฌ๋ ๋ ๋ ธ์ผ๋, ์ด์ accounts ํ
์ด๋ธ์ ์ ๊ทผํด๋ณผ๊น..
DELETE
FROM accounts
WHERE account_id = 99; # id=99 ์ธ ๋ ์ฝ๋๊ฐ ์๋ค๊ณ ๊ฐ์ .
ํด๋น ์ฟผ๋ฆฌ๋ ๋ฐ๋๋ฝ์ด ๊ฑธ๋ฆฝ๋๋ค. ์์ ๋์ํ์ง ์์ต๋๋ค.
session 1: account_id ๋ฅผ ์ธ๋ฑ์ค๋ก ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, ์ธ๋ฑ์ค ๋ ์ฝ๋ account_id=99 ์ Lock์ ๊ฒ๋๋ค.
session 2: accounts ํ ์ด๋ธ์ ์์ก์ด 1์ต์ ์ด์์ธ ์ ์ ๋ฅผ ์์ ํ๋ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฝ๋๋ค.
- ์๋ธ์ฟผ๋ฆฌ์์ accounts์ ๋ง์ ๋ ์ฝ๋์ ๊ฐญ์ Shared Lock์ ๊ฒ๋๋ค. ์๋ธ์ฟผ๋ฆฌ๋ Undo ๋ก๊ทธ๋ฅผ ์ฌ์ฉํ์ง ์์์.
- ์ฌ๊ธฐ์ session2๋ ์์์ session1์ account_id=99 ์ธ๋ฑ์ค ๋๋ฌธ์ user_table ๋ฝ์ด ๊ฑธ๋ ค์ ๋๊ธฐ ์ํ๊ฐ ๋ฉ๋๋ค.
session1 : ์ดํ account_id๊ฐ 99์ธ ๋ ์ฝ๋๋ฅผ accounts์์ ์ง์ฐ๋ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ ธ์ต๋๋ค. ํ์ง๋ง Session2๊ฐ ์๋ธ์ฟผ๋ฆฌ์ accounts Lock์ ๊ฑธ์ด๋ฒ๋ ค์ ๋๊ธฐํ๊ฒ ๋ฉ๋๋ค.
๐ MySQL ์์ง ๋ ๋ฒจ ๋ฝ
- ๊ธ๋ก๋ฒ ๋ฝ
๋ชจ๋ ํ ์ด๋ธ์ ๋ฝ์ ๊ฒ๋๋ค. DB ๋คํ๋ฅผ ๋ฐ๋ ๋ฑ์ ์ ๋ง ํน์ํ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด ์ฌ์ฉํ์ง ์์ต๋๋ค. - ํ
์ด๋ธ ๋ฝ
๊ฐ ํ ์ด๋ธ ๋จ์๋ก Lock์ ๊ฒ๋๋ค. InnoDB์์ ๋ ์ฝ๋ ๋ฝ์ด ์ ๊ณต๋๋ฏ๋ก DML์์๋ ๊ฑฐ์ ์ฌ์ฉ๋์ง ์๊ณ , DDL ์ฒ๋ผ ์คํค๋ง ์์ฒด๋ฅผ ๋ฐ๊ฟ ๋ ์ข ์ข ์ฌ์ฉํฉ๋๋ค. - Name ๋ฝ
ํ ์ด๋ธ ์ด๋ฆ, ๋ทฐ ์ด๋ฆ๋ฑ ์คํค๋ง ๊ฐ์ฒด์ ์ด๋ฆ์ ๋ณ๊ฒฝํ๋ ๊ฒฝ์ฐ ์๋์ผ๋ก ๋ฝ์ ๊ฒ๋๋ค. - ์ ์ ๋ฝ
์ฌ์ฉ์๊ฐ ์ง์ ํ ๋ฌธ์์ด์ ๋ฝ์ ๊ฒ๋๋ค. ์ฌ๋ฌ ์๋ฒ์์ ์ค์ ๊ฐ์ ๋๊ธฐํํ๋ ์ฉ๋๋ก ์ข ์ข ์ฌ์ฉํฉ๋๋ค.
'๐ฑBackend > DB(MySQL,PostgreSQL)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์ ๋ฆฌ์ค-5 (0) | 2021.10.27 |
---|---|
์ ๋ฆฌ์ค - 4 (0) | 2021.10.20 |
์ ๋ฆฌ์ค -2 (0) | 2021.10.20 |
์ ๋ฆฌ์ค #1 ์์ง ์ํคํ ์ฒ (0) | 2021.10.13 |
1. MySQL์ ๋ฌด์์ธ๊ฐ (0) | 2021.10.13 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev