[ํธ๋ฌ๋ธ์ํ ] ์ฟ ํฐ ๋ฐ๊ธํ๋ค ํฐ์ง Deadlock, ๊ทธ๋ฆฌ๊ณ Redisson์ผ๋ก ํด๊ฒฐํ ์ด์ผ๊ธฐ
๐ฃ ๋์์ฑ ํ ์คํธ ์ค Deadlock ๋ฐ์! ๊ทธ๋ฆฌ๊ณ Redisson ๋ฝ๊น์ง ๋์ ํ ํธ๋ฌ๋ธ์ํ
์ ์ฐฉ์ ํ ์ธ ์ฟ ํฐ ๋ฐ๊ธ ๊ธฐ๋ฅ์ ๋ง๋ค๋ฉด์ ๊ฒช์๋ ํธ๋ฌ๋ธ์ํ
๊ณผ์ ์ ์ ๋ฆฌํด๋ณด๋ ค๊ณ ํ๋ค!
Deadlock์ด ์ค์ ๋ก ๋ฐ์ํ๋ ๋ถํ ํ
์คํธ ๊ฒฐ๊ณผ์, ๋ฝ ์ ๋ต์ ๊ณ ๋ฏผํ๊ณ ์ต์ข
์ ์ผ๋ก Redisson ๋ฝ์ ๋์
ํ๊ธฐ๊น์ง์ ์ค์ ๊ฒฝํ์ ๊ธฐ๋ฐ์ผ๋ก ํ์ด๋ธ ํธ๋ฌ๋ธ์ํ
๊ธฐ๋ก์ด๋ค!
๐ซ ๋ฌธ์ ์ํฉ: Deadlock ๋ฐ์ ์คํ
๊ธฐ๋ณธ์ ์ธ ์ฟ ํฐ ๋ฐ๊ธ API๋ง ์์ฑํ ์ํ์์, ๋ค์ ์ฌ์ฉ์๊ฐ ๋์์ ๋ฐ๊ธ์ ์์ฒญํ๋ฉด ์ด๋ค ์ผ์ด ์ผ์ด๋ ๊น ๊ถ๊ธํ๋ค.
๊ทธ๋์ JMeter๋ฅผ ์ด์ฉํด ๊ฐ๋จํ ๋ถํ ํ
์คํธ๋ฅผ ์งํํด ๋ดค๋ค.
ํ ์คํธ ์กฐ๊ฑด
ํญ๋ชฉ | ๊ฐ |
ํ ์คํธ ๋๊ตฌ | Apache JMeter |
์ค๋ ๋ ์ | 100๋ช |
๋ฃจํ ํ์ | 1ํ (1๋ช ๋น 1ํ ๋ฐ๊ธ ์๋) |
์ฟ ํฐ ์๋ | 10๊ฐ |
๋์ API | /api/coupons/{couponId}/issue |
๋ฝ ์ ์ฉ ์ฌ๋ถ | ์์ (๊ธฐ๋ณธ ๋ฐ๊ธ ๋ก์ง) |
โก ํ ์คํธ ์คํ ๊ฒฐ๊ณผ
ํ ์คํธ๋ฅผ ๋๋ฆฌ์๋ง์ ๋ค์๊ณผ ๊ฐ์ ์๋ฌ๊ฐ ์์์ก๋ค..
Deadlock found when trying to get lock; try restarting transaction
์์ธ ์๋ฌ ๋ก๊ทธ๋ฅผ ์ดํด๋ดค๋๋..
com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException:
Deadlock found when trying to get lock; try restarting transaction
SQL [update coupons set ... where id=?]
coupon.issueCount๋ฅผ ์ฆ๊ฐ์ํค๋ ๊ณผ์ ์์ Deadlock์ด ๋ฐ์ํ๊ณ , ์ผ๋ถ ํธ๋์ญ์ ์ด ๊ฐ์ ๋กค๋ฐฑ๋๊ณ ์์๋ค..
๐ Deadlock ์์ธ ๋ถ์
Deadlock์ด ๋ฐ์ํ ์์ธ์ ๋ค์๊ณผ ๊ฐ๋ค.
1. 100๋ช
์ ์ฌ์ฉ์๊ฐ ๋์์ ์ฟ ํฐ ๋ฐ๊ธ ์์ฒญ
2. DB๋ ๋ชจ๋ ๊ฐ์ ์ฟ ํฐ์ ์
๋ฐ์ดํธํ๋ ค๊ณ ์๋
3. row-level ๋ฝ์ ์ก์ผ๋ ค๋ค๊ฐ, ์๋ก ์์๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ํ ๋๊ธฐ๊ฐ ๋ฐ์
4. ๊ฒฐ๊ณผ์ ์ผ๋ก Deadlock ๊ฐ์ง → ์ผ๋ถ ํธ๋์ญ์
๋กค๋ฐฑ
๐ง ์ ์ด๊ฑธ ์ค์ํ๊ฒ ๋ด์ผ ํ์๊น?
๐ ๋จ์ํ coupon.issueCount++ ๊ฐ์ ๋ก์ง๋ ํธ๋ํฝ์ด ๋ชฐ๋ฆฌ๋ฉด ์ฌ๊ฐํ ๋ฌธ์ ๋ก ์ด์ด์ง ์ ์๋ค.
๐ race condition ๋ฟ ์๋๋ผ, Deadlock๊น์ง ๋ฐ์ํ ์ ์๋ค๋ ์ฌ์ค์ ์ง์ ๊ฒฝํํ๋ค.
๐ "์ ๋๊ฒ ์ง"๋ผ๋ ๋ง์ฐํ ๋ฏฟ์์ด ์๋๋ผ, ์ค์ ํ
์คํธ๋ก ํ์ธํด์ผ ํ๋ค๋ ๊ตํ์ ์ป์๋ค.
๐งฉ ๋ฌธ์ ํด๊ฒฐ์ ์ํ Lock ์ ๋ต ๊ณ ๋ฏผ
Deadlock์ ํด๊ฒฐํ๋ ค๋ฉด ๋์์ฑ ์ ์ด๊ฐ ํ์ํ๋ค.
๊ทธ๋์ ์ฌ๋ฌ Lock ์ ๋ต์ ์ ๋ฆฌํ๊ณ ๋น๊ตํด ๋ดค๋ค.
์ ๋ต | ์ค๋ช | ์ฅ์ | ๋จ์ |
๋น๊ด์ ๋ฝ (Pessimistic Lock) |
DB SELECT ... FOR UPDATE ์ฌ์ฉ | ๊ฐ๋ ฅํ ์ ํฉ์ฑ ๋ณด์ฅ ๋จ์ผ ์๋ฒ์์ ๋น ๋ฆ |
ํ์ฅ์ฑ ๋ถ์กฑ DB ๋ณ๋ชฉ ๊ฐ๋ฅ์ฑ |
๋๊ด์ ๋ฝ (Optimistic Lock) |
๋ฒ์ ํ๋ ๊ธฐ๋ฐ ์ถฉ๋ ๊ฐ์ง | ๋ฝ ์์ด ๋น ๋ฆ | ์ถฉ๋ ์ ์ฌ์๋ ํ์ ์ค์๊ฐ์ฑ ๋จ์ด์ง |
Redisson ๋ถ์ฐ ๋ฝ | Redis๋ฅผ ์ด์ฉํ ๊ธ๋ก๋ฒ ๋ฝ | ๋ถ์ฐ ํ๊ฒฝ ๋์ ์ค์๊ฐ ์ ์ด ๊ฐ๋ฅ |
๋คํธ์ํฌ ์ค๋ฒํค๋ ์ค์ ๋ณต์ก |
๋ฉ์์ง ํ ์ฒ๋ฆฌ | Kafka ๋ฑ์ผ๋ก ์ ์ฐฉ์ ํ ์ฒ๋ฆฌ | ๋๋ ์ฒ๋ฆฌ์ ๊ฐํจ ์ฅ์ ๋ณต์๋ ฅ ์ฐ์ |
์ค์๊ฐ์ฑ ๋ถ์กฑ ์ค๊ณ ๋ณต์ก |
๐งช ์ง์ ์ฑ๋ฅ ํ ์คํธ ํ์ธ
๋จ์ผ ์๋ฒ ํ๊ฒฝ์์ ๊ฐ๊ฐ์ ๋ฝ ์ ๋ต์ ์ ์ฉํ๊ณ JMeter ๋ถํ ํ ์คํธ๋ฅผ ๋ค์ ๋๋ ธ๋ค.
์ ๋ต | ํ๊ท ์๋ต ์๊ฐ | ์ค๋ณต ์ฌ๋ถ | ๋น๊ณ |
๋น๊ด์ ๋ฝ | 14ms | ์์ | DB ํธ๋์ญ์ ๊ธฐ๋ฐ์ผ๋ก ๊ฐ์ฅ ๋น ๋ฆ |
Redisson ๋ฝ | 413ms | ์์ | Redis ํต์ ์ค๋ฒํค๋ ๋ฐ์ |
โ ๋น๊ด์ ๋ฝ์ด ๋จ์ผ ์๋ฒ ํ๊ฒฝ์์ ์๋์ ์ผ๋ก ๋น ๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์๋ค.
๐ค๏ธ ์ต์ข ์ ํ: Redisson ๋ถ์ฐ ๋ฝ
๊ทธ๋ผ์๋ ์ต์ข ์ ์ผ๋ก Redisson ๋ถ์ฐ ๋ฝ์ ์ ํํ๋ค. ์ด์ ๋ ๋ช ํํ๋ค.
๊ณ ๋ ค์ฌํญ | ์ค๋ช |
์ํ ํ์ฅ์ฑ | ์๋ฒ๊ฐ ์ฌ๋ฌ ๋๊ฐ ๋์ด๋ ์ผ๊ด์ฑ ์ ์ง ๊ฐ๋ฅ |
์ค์๊ฐ์ฑ | ๋ฉ์์ง ํ๋ณด๋ค ๋น ๋ฅธ ์ฒ๋ฆฌ ๊ฐ๋ฅ |
์ ์ง๋ณด์์ฑ | Redis ์บ์ ๊ตฌ์กฐ์ ํตํฉ ์ด์ฉ ๊ฐ๋ฅ |
์ค๊ณ ์ ์ฐ์ฑ | DB ํธ๋์ญ์ ๊ณผ ๋ ๋ฆฝ๋ ๋ฝ ๊ด๋ฆฌ ๊ฐ๋ฅ |
๋น๊ด์ ๋ฝ์ DB ์์์๋ง ๋ฝ์ ๊ฑธ ์ ์๊ณ , ์ํ ํ์ฅ ์(์๋ฒ๊ฐ ์ฌ๋ฌ ๋๋ก ๋์ด๋๋ฉด) ๋ฝ ์ผ๊ด์ฑ ์ ์ง๊ฐ ์ด๋ ค์ ๋ค.
๊ทธ๋์ ๋ฏธ๋ ํ์ฅ์ ๊ณ ๋ คํ์ฌ, ์กฐ๊ธ ๋๋ฆฌ๋๋ผ๋ Redisson ๋ฝ์ผ๋ก ์ ํํ๋ ๊ฑธ ์ ํํ๋ค.
๋จ์ํ ๊ธฐ๋ฅ์ด๋ผ๊ณ ๊ฐ๋ณ๊ฒ ์๊ฐํ์ง๋ง, ์ค์ ํธ๋ํฝ ์ํฉ์์๋ race condition, Deadlock ๊ฐ์ ์ฌ๊ฐํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์์ ์ง์ ๊ฒฝํํ๋ค.
๋ฌธ์ ๋ฅผ ํ
์คํธ๋ก ์ฌํํ๊ณ , ๋ค์ํ ๋์์ ๋น๊ตํ๊ณ , ๋ฏธ๋ ํ์ฅ์ฑ๊น์ง ๊ณ ๋ คํ์ฌ ๊ธฐ์ ์ ๊ฒฐ์ ์ ๋ด๋ฆฌ๋ ๊ฒฝํ์ ๊ฐ๋ฐ์๋ก์ ์ ์ตํ ์๊ฐ์ด์๋ค.