๊ฐ์
๋์ฉ๋์๋น์ค๋ฅผ ํ๋ฉด ๋์์ฑ ์ฒ๋ฆฌ์ ๋ํ ๊ณ ๋ฏผ์ ๋ง์ด ํ๊ฒ ๋๋ค. ๋์์ ๋ค์ด์ค๋ ์์ฒญ์ ๋ํด์ ๋๋ฝ์์ด ์ ๋๋ก ๋ฐ์์ด ๋๋๊ฐ์ ๋ํ ๋ด์ฉ๋ค.
๋น์ทํ ๊ธ๋ค์ด ๋ง์์ ๋ ๋ํ ๊ทธ๋ฐ ๊ธ์ ์ธ ํ์๋ ์์ ๊ฒ ๊ฐ๊ณ , ๋จ์ง mysql ๊ณตํ์ ์๋ Locking Reads ๋ด์ฉ์ ์ฝ์ด๋ณด๊ณ , InnoDB engine ์์ ์ ๊ณตํ๋ ๋ฝ์ธ Shared Lock ๊ณผ Exclusive Lock ์ ์ดํดํ๋ ์๊ฐ์ ๊ฐ์ธ์ ์ผ๋ก ๊ฐ์ก๋ค. ๊ทธ๋ฆฌ๊ณ ์ ํ๋ฆฌ์ผ์ด์ ๋จ์์ ์ด๋ป๊ฒ ์ ์ฉํ ์ง์ ๋ํ ์ฝ๋๋ ์์ฑํ๋ค. (์ฌ์ค ์ด ๋ด์ฉ๋ ๋ธ๋ก๊ทธ์ ๋ฌด์ํ ๋ง์ง๋ง ๋ด ๊ฐ๋ ์ ๋ฆฌ์ฐจ ์์ฑํ๋ค.)
๊ณตํ์ ๋ด์ฉ์ ๊ตฌ๊ธ๋ฒ์ญ๊ธฐ์ ๋์์ ์ป์๋ค. (+ ์์ญ)
Locking Reads (์ฝ๊ธฐ์ ๊ธ)
์ ์ฅ์ด๋ ์์ ์ ๋ํ ํธ๋์ญ์ ์ํ ์, ์ผ๋ฐ์ ์ธ ์กฐํ ๊ตฌ๋ฌธ์ ๋ณดํธ๋ฐ์ ์ ์๋ค. ํ ํธ๋์ญ์ ์ด ๋ง์ฝ ํน์ ๋ก์ฐ์ ๋ํด์ ์์ /์ ์ฅ์ ์ํํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. InnoDB ๋ ํด๋น ์ฌํญ์ ๋ํ ๋ณดํธ์ฅ์น๋ฅผ ์ํด ๋ ๊ฐ์ง ํ์ ์ Locking Reads ๋ฅผ ์ ๊ณตํ๊ณ ์๋ค. ์ถ๊ฐ๋ก Locking Reads ๋ฅผ ์ด์ฉํ ๋๋ autocommit ์ด disabled ๋ก ์ฒ๋ฆฌ๋์ด์์ด์ผ ํ๋ค. (autocommit = 0)
SELECT ... FOR SHARE : Shared Lock (S-Lock : ๊ณต์ ์ ๊ธ)
์ฝ์๋งํ ํน์ ๋ก์ฐ์ ๋ํด์ shared mode lock ์ ์ธํ ํ ์ ์๋ค. ๋ค๋ฅธ ์ธ์ ๋ ํด๋น ๋ก์ฐ๋ฅผ ์ฝ์ ์ ์๋ค. ๊ทธ๋ฌ๋ ๋จผ์ ์ฝ์ transaction ์ด commit ํ๊ธฐ ์ด์ ์ ๋ค๋ฅธ ์ธ์ ์์ ํด๋น ๋ก์ฐ์ ๋ฐ์ดํฐ๋ฅผ update ํ ์ ์๋ค. ๋ค๋ฅธ ์ธ์ ์ ๋ณ๊ฒฝ์ฟผ๋ฆฌ๋ ๊ธฐ์กด์ transaction ์ด commit ์ ์ํํ๊ธฐ ์ด์ ๊น์ง ๊ณ์ ๊ธฐ๋ค๋ฆฌ๋ค๊ฐ commit ์ด ๋๋ฉด ๊ทธ์ ์์ผ update ์ฟผ๋ฆฌ๊ฐ ์ํ๋๋ค. SELECT ... FOR SHARE ๋ก ํน์ ๋ก์ฐ์ ๋ฝ์ด ๊ฑธ๋ ค์๋๋ผ๋ ํ ํธ๋์ญ์ ๋ ๊ทธ ๋ก์ฐ์ ๋ฝ์ ๊ฑธ ์ ์๋ค.
SELECT ... FOR UPDATE : Exclusive Lock (X-Lock : ๋ฐฐํ์ ๊ธ)
ํน์ ๋ก์ฐ์ update ๋ฌธ์ ์คํํ๊ฒ์ฒ๋ผ lock ์ด ์ธํ ๋๋ค. ๋ค๋ฅธ transaction ์ ํด๋น ๋ก์ฐ๋ฅผ ์ ๋ฐ์ดํธํ ์ ์์ด blocked ์ด ๋๋ค. ๋ค๋ฅธ transaction ์ด SELECT ... FOM SHARE ๋ฅผ ํ๋๋ผ๋ blocked ๋๋ค. X-Lock ์ด ๊ฑธ๋ฆฌ๋ฉด ํ ํธ๋์ญ์ ์ ๋ฝ์ ๊ฑธ ์ ์๋ค.
S-Lock & X-Lock ๋ค์ด์ด๊ทธ๋จ
์ค์ ํฐ๋ฏธ๋์ 2๊ฐ์ ๋ ๋์ด๋๊ณ , ์ฟผ๋ฆฌ๋ ๋ฆฌ๋ฉด์ ํ ์คํธํด๋ณธ ๊ฒฐ๊ณผ๋ฅผ ๋ค์ด์ด๊ทธ๋จ์ผ๋ก ์ฎ๊ฒจ๋ดค๋ค.
๊ทธ๋์..?
๋จ์ ๋์์ ์ฝ๋ ํ์๊ฐ ๋ง๋ค๋ฉด, SELECT ... FOR SHARE ๋ฅผ ํด๋ ์๊ด์๋ค.
ํ์ง๋ง ๋์์ฝ๊ธฐํ์ < ๋์์ฐ๊ธฐํ์์ธ ๊ฒฝ์ฐ์๋ SELECT ... FROM UPDATE ๋ก ๋ฐฐํ์ ๊ธ์ ๊ฑธ์ด๋ฌ์ผํ๋ค.
์ ํ๋ฆฌ์ผ์ด์ ๋ ๋ฒจ๋จ ์ฝ๋
@Transactional
fun addItemByInventoryIdWithPsRead(id: Long): Inventory {
// s-lock
// ๋์์ ์์ฒญ ์ค๊ฒ๋๋ค๋ฉด, ๋ฐ๋๋ฝ์ด ๋ฐ์ํ๋ค๋ฉด์ ์์ฒญ์ด ์คํจํ๋ค.
val inventory = entityManager.find(InventoryV3::class.java, id, LockModeType.PESSIMISTIC_READ)
?: throw RuntimeException("์ธ๋ฒคํ ๋ฆฌ ๋ฏธํ์ธ : $id")
inventory.addItemIfPossibleOrThrow()
return inventory
}
@Transactional
fun addItemByInventoryIdWithPsWrite(id: Long): Inventory {
// x-lock timeout ์ค์
// ๋์์ ์์ฒญ์ด ์ฌ ๋ ๋ฐ๋๋ฝ์ ๋ฐฉ์งํ๊ธฐ ์ํด ํ์์์์ ๊ฑธ์ด๋๋ค.
// ๋ง์ฝ ํ์์์์ ๋์ด๊ฐ๋ฉด 'LockTimeoutException' ์ด ๋ฐ์ํ๋ค.
val properties: Map<String, Any> = mutableMapOf<String, Long>().apply {
this["javax.persistence.lock.timeout"] = 1000L
}
val inventory = entityManager.find(Inventory::class.java, id, LockModeType.PESSIMISTIC_WRITE, properties)
?: throw RuntimeException("์ธ๋ฒคํ ๋ฆฌ ๋ฏธํ์ธ : $id")
inventory.addItemIfPossibleOrThrow()
return inventory
}
(์์ : 2022-11-12)
MySQL & JPA ๋ก๋ ๋ฐฐํ์ ๊ธ์์ javax.persisntence.lock.timeout ์ด ์ง์๋์ง ์๋๋ค. lock.timeout ๋์ ์ javax.persistence.query.timeout ์ ์ฐ๋ ๊ฑธ ๊ณ ๋ คํ๋ค. lock.timeout ์ ์ฐ๊ณ ์ ํ๋ค๋ฉด, ์ฝ๋๋ ๋ฒจ์์ ๋ณ๋ mysql ๋จ์ผ๋ก native query ๋ฅผ ๊ฐ๋ณ ํธ๋์ญ์ ๋ง๋ค ์ฟผ๋ฆฌ ํธ์ถ์ ์ ๋ ๋ ค์ฃผ์ด์ผ ํ๋ค.
์ฐธ๊ณ
https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html
https://dev.mysql.com/doc/refman/8.0/en/innodb-deadlock-example.html
'open source' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
2023-01-07 : [mockk] captured ์ฌ์ฉ (0) | 2023.01.07 |
---|---|
2022-08-20 [redis] : redis-cli ์ hex ๋ฅผ ์์ string ์ผ๋ก ๋ณด๊ณ ์ ํ ๋. (0) | 2022.08.20 |
2022-05-15 [mockk] wasNot called ์ฌ์ฉ (0) | 2022.05.15 |
2021-12-30 [jmeter] jmeter ์ฑ๋ฅ ํ ์คํธ ์ํ (2) (0) | 2021.12.30 |
2021-12-19 [jmeter] jmeter ์ค์น ๋ฐ ์คํ (1) (0) | 2021.12.19 |