โ๏ธย ๊ฒฐ๋ก
๐ย ๋ฌธ์ ์ ์
์ฒ์์ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ค๊ณ ํ์์ ๋, ์ฒ์์ ๊ฐ์ ์ Isolation Level์ด Repeatable Read๋ก ํ๋ฉด ์ ๋๋ค๋ ์๋ชป๋ ๊ฐ๋ ์ ๊ฐ์ง๊ณ ์์์ด์ Read Committed๋ก ๋ด๋ ค์ผ ๋๋ ์ค ์๊ณ ์๋ฅผ ์ํด Read Committed๋ก ๋ด๋ฆฌ๋ ๊ฑด ๋ง์ด ์ ๋๋ ๊ฒ ๊ฐ์ ๋น๊ด์ ๋์์ฑ ์ ์ด๋ก ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ์ ํํ์๋๋ฐ ใ ,,
์๋ชป๋ ์ง์์ผ๋ก ์๋ชป๋ ํ๋จ์ ๋ด๋ฆฌ๊ณ ์์๋ค ใ ใ ใ
JPA๋ฅผ ์ฌ์ฉํ๋ค๋ฉด Version์ผ๋ก ๊น๋ํ๊ฒ ๊ตฌํ์ด ๊ฐ๋ฅํ๋๋ฐ,
๋๋ Spring + MyBatis์ฌ์ ์ฟผ๋ฆฌ๋ฌธ์์ CAS(compare-and-set) ๋ฐฉ์์ผ๋ก ๋๊ด์ ๋์์ฑ ์ ์ด ๋ฐฉ๋ฒ์ ๊ตฌํํ๋ ๊ฒ์ ์ฌ์ฉํ์๋ค.
์์์ ๋งํ ์๋ชป๋ ์ง์์ ๊ฐ์ง๊ณ ์ผ๋จ ๋๊ด์ ๋์์ฑ ์ ์ด๋ฅผ ์ ์ฉํด๋ณด์๋ค
- Isolation Level์ MySQL์ ๊ธฐ๋ณธ์ธ Repeatable Read โ Read Committed๋ก ๋ณ๊ฒฝ
@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED) public void order(Order order) throws Exception { // 0-1. ์ฌ๊ณ ์๋ ๊ฐ์ decreaseProdQty(order.getOrdDtlList());
- ์ฌ๊ณ ์๋ ๊ฐ์ ์ฟผ๋ฆฌ์ UPD_DTTM(๋ณ๊ฒฝ์ผ์)์ด ๋ง์ง๋ง์ผ๋ก ์ฝ์ ํ๋ก ๋ณ๊ฒฝ๋์ง ์์์ ๋๋ง ์์ ๋๋๋ก ์ฟผ๋ฆฌ ์์
<update id="decreaseProdQty" parameterType="Map"> UPDATE prod_opt SET INV_QTY = INV_QTY - #{qty} , UPD_DTTM = now() WHERE OPT_COMB_NO = #{optCombNo} AND UPD_DTTM = #{updDttm} </update>
โ ์ด๋ ๊ฒ ์์ ํ ํ ์ฌ๊ณ ๊ฐ 1๊ฐ ๋จ์ ์ํ์ 2๊ฐ์ ์ฐ๋ ๋๊ฐ ๋์์ ์ฃผ๋ฌธํ๋๋ก ํ๊ณ ๋๋ ค๋ณด์๋๋ฐ ๋ด๊ฐ ๊ธฐ๋ํ ๊ฒฐ๊ณผ๊ฐ ์๋ ์ฌ๊ณ ์๋์ด ์ฌ์ ์ด -1์ด ๋จ
โย ๋ถ์&์๋
์๋1) ์ ๋๋ ์ด์ ๋ฅผ ๋๋ฆ ๋ถ์ํด๋ณด์๋ค
์๋๋ ๋ด๊ฐ ์๊ฐํ ์์ ์ํฉ์ flow์๋ค..
๊ทธ๋ฌ๋๊น ๊ฒฐ๋ก ์ ํธ๋์ญ์
1์์ ์ฌ๊ณ ์๋์ ๋ณ๊ฒฝํด UPD_DTTM๋ ์ต์ ๊ฐ์ผ๋ก ๋ณ๊ฒฝ๋์์ผ๋
Isolation Level์ด Read Committed๊ณ ํธ๋์ญ์
์ด ๋๋์ง ์์ ์ปค๋ฐ์ด ๋์ง ์์๊ธฐ ๋๋ฌธ์ UPD_DTTM๋ ์ฌ์ ํ ํธ๋์ญ์
์์ ์ ์ฝ์ด์จ ๊ฐ์ด๋ค.
๋ฐ๋ผ์ UPD_DTTM ์ปฌ๋ผ์ ๋น๊ตํ๋ ๊ฒ์ฆ ๋ถ๋ถ์ด ํต๊ณผ๋์ด ์ฌ๊ณ ์๋์ด ๊ฐ์๋๋ค.
์๊ฐ ์์ | ํธ๋์ญ์ 1 ์ํ | ํธ๋์ญ์ 2 ์ํ | ์ค๋ช |
---|---|---|---|
1 | Read: ์ฌ๊ณ ์๋ & ๋ณ๊ฒฝ์ผ์ ์ฌ๊ณ ์๋: 1๊ฐ ๋ณ๊ฒฝ์ผ์: 02/05/2024 10:02:18.000 | ||
2 | ์ฌ๊ณ ์๋ ๊ฒ์ฆ: 1๊ฐ ์ด์์ด๊ธฐ ๋๋ฌธ์ ํต๊ณผ | Read: ์ฌ๊ณ ์๋ & ๋ณ๊ฒฝ์ผ์ ์ฌ๊ณ ์๋: 1๊ฐ ๋ณ๊ฒฝ์ผ์: 02/05/2024 10:02:18.000 | |
1 | decreaseProdQty๋ฅผ ์คํํด ์ฌ๊ณ ์๋์ ๊ฐ์์ํจ๋ค. ์ด๋ ํธ๋์ญ์ 1์ด ์์๋ ์ดํ๋ก ์ฌ๊ณ ์๋์ด ๋ณ๊ฒฝ๋์ง ์์๊ธฐ ๋๋ฌธ์ UPD_DTTM ์ปฌ๋ผ์ ๋น๊ตํ๋ ๊ฒ์ฆ ๋ถ๋ถ์ด ํต๊ณผ ๋์ด ์ฌ๊ณ ์๋์ด ๊ฐ์๋๋ค. | ์ฌ๊ณ ์๋ ๊ฒ์ฆ: 1๊ฐ ์ด์์ด๊ธฐ ๋๋ฌธ์ ํต๊ณผ | |
2 | ๊ทธ ๋ค๋ก ์ฃผ๋ฌธ ๋ก์ง ์์ฑ | decreaseProdQty๋ฅผ ์คํํด ์ฌ๊ณ ์๋์ ๊ฐ์์ํจ๋ค. ํธ๋์ญ์ 1์์ ์ฌ๊ณ ์๋์ ๋ณ๊ฒฝํด UPD_DTTM๋ ์ต์ ๊ฐ์ผ๋ก ๋ณ๊ฒฝ๋์์ผ๋ Isolation Level์ด Read Committed๊ณ ํธ๋์ญ์ ์ด ๋๋์ง ์์ ์ปค๋ฐ์ด ๋์ง ์์๊ธฐ ๋๋ฌธ์ UPD_DTTM๋ ์ฌ์ ํ ํธ๋์ญ์ ์์ ์ ์ฝ์ด์จ ๊ฐ์ด๋ค. ๋ฐ๋ผ์ UPD_DTTM ์ปฌ๋ผ์ ๋น๊ตํ๋ ๊ฒ์ฆ ๋ถ๋ถ์ด ํต๊ณผ๋์ด ์ฌ๊ณ ์๋์ด ๊ฐ์๋๋ค. | |
3 | ์ปค๋ฐ | ๊ทธ ๋ค๋ก ์ฃผ๋ฌธ ๋ก์ง ์์ฑ | |
4 | ๊ฒฐ๊ณผ๋ ์ฌ๊ณ ์๋ 0 | ์ปค๋ฐ | |
5 | ๊ฒฐ๊ณผ๋ ์ฌ๊ณ ์๋ -1 |
์๋2)
์์์ ์ ์ํ๋ ์ด์ํ ๊ฐ์ ์ผ๋ก ๋๊ด์ ๋์์ฑ ์ ์ด๋ฅผ ์ ์ฉํ๋ ๊ณผ์ ์์ ๊ต์ฅํ ์ฝ์ง์ ํ๊ณ ์์๋ค.
์ผ๋ฐ์ ์ผ๋ก ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ ๋ ๊ฐ์ง๋ค.
- ๋น๊ด์ ๋์์ฑ ์ ์ด(Pessimistic Concurrency Control)
- ๋๊ด์ ๋์์ฑ ์ ์ด(Optimistic Concurrency Control)
์ฌ๋ฌ ์ฑ ์์๋ ๋๊ด์ ๋์์ฑ ์ ์ด๋ฅผ ํ์์คํฌํ๋ฅผ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ผ๋ก ์๊ฐํ๊ณ ์์๋ค. ํ์ง๋ง ์ค์ ๋ก ์ ์ฉํด ๋ณด์๋๋ฐ ๋์์ฑ ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋์ง ์์๊ณ , ์ด์ ๋ฅผ ๋ถ์ํ๋ ค๋ค ์คํ๋ ค ๋ฌธ์ ํด๊ฒฐ๊ณผ๋ ์ ์ ๋ฉ์ด์ง๋ ์ด์ํ ๊ฐ์ ์ ํ๊ฒ ๋์๋ค.
๊ตฌ๊ธ๋ง์ ํตํด ๋ค์ํ ํด๊ฒฐ์ฑ ์ ์ฐพ์๋ดค์ง๋ง, ๋๋ถ๋ถ JPA์ @Version์ผ๋ก ํด๊ฒฐํ๊ณ ์์๊ณ MyBatis ์ฌ์ฉ ์ฌ๋ก๋ ์ฐพ๊ธฐ ์ด๋ ค์ ๋ค. ๊ทธ๋ฌ๋ค ์ฐพ๋ค์ฐพ๋ค ๋๋์ด ์ฐ์ฐํ ํ ๋ธ๋ก๊ทธ์์ JPA๊ฐ ์๋๋ผ ๋ด๊ฐ ์ฑ ์์ ๋ณธ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํด ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ๊ธ์ ๋ฐ๊ฒฌํ๊ฒ๋์๋ค..!


๋๋ ์ด๋ฏธ ์๋ชป๋ ๊ฐ์ ์ ๊น์ด ๋น ์ ธ ์์ด์ "์ ๋ฐฉ๋ฒ์ผ๋ก ํด๊ฒฐ๋ ๋ฆฌ๊ฐ ์๋ค"๊ณ ์๊ฐํ๋ฉฐ ๋ฐ์ ๋ฐ์ํ๋ฉฐ ์๋์ ์น๊ณ ์๋๋ฅผ ํด๋ณด์๋ค.
์ฌ๊ณ ์๋ ๊ฐ์์ฟผ๋ฆฌ์ INV_QTY >= #{qty}
์กฐ๊ฑด์ ์ถ๊ฐํด ์ค์ ์ฌ๊ณ ์๋์ด ์ถฉ๋ถํ ๋๋ง ์
๋ฐ์ดํธํ๋๋ก ํ๋ค. ๋ณ๊ฒฝ ํ ๊ฒฉ๋ฆฌ ์์ค์ ์๊ด์์ด Repeatable Read์ Read Committed ๋ชจ๋ ๋ฌธ์ ์์ด ์๋ํ๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค. ์ด ๊ฒฐ๊ณผ๋ ๋ด๊ฐ ์ธ์ด ๊ฐ์ ๊ณผ ์ ํ ๋ฌ๋๊ณ .. ์์์น ๋ชปํ ๊ฒฐ๊ณผ์๋ค. ๋ด ์์์ผ๋ก๋ -1๋ก ๋์์ฑ ๋ฌธ์ ๊ฐ ํด๊ฒฐ์ด ์ ๋์์ด์ผ ํ๋๋ฐ ์ค์ ๋ก๋ 0์ผ๋ก ๋์์ฑ ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋์๋ค..
๊ธฐ์กด
<update id="decreaseProdQty" parameterType="Map">
UPDATE prod_opt
SET INV_QTY = INV_QTY - #{qty}
, UPD_DTTM = now()
WHERE OPT_COMB_NO = #{optCombNo}
AND UPD_DTTM = #{updDttm}
</update>
๋ณ๊ฒฝ
<update id="decreaseProdQty" parameterType="Map">
UPDATE prod_opt
SET INV_QTY = INV_QTY - #{qty}
, UPD_DTTM = now()
WHERE OPT_COMB_NO = #{optCombNo}
AND INV_QTY >= #{qty}
</update>
๊ทธ ์ด์ ์ ๋ํด์๋ ์๋ ๊ธ ์ฐธ๊ณ .

3) ์๋ชป๋ ๊ฐ๋ ๋ฐ๋ก์ก์ ํ, ํ์์คํฌํ๋ก ์ ๋๋ ์ด์ ๋ถ์
์ ๊ธ์ ํตํด์๋ชป๋ ๊ฐ๋ ๋ฐ๋ก์ก๊ณ , ๊ทธ๋์ ์ผ์ชฝ์ด๋ ์ค๋ฅธ์ชฝ์ด๋ ๊ฐ์ ๋ฉ์ปค๋์ฆ์ธ๋ฐ.. ์ ๋๋ ์ด์ ๋ฅผ ๋ถ์ํด๋ณด์๋ค.
์ฒ์ ์์ ์ผ์๋ฅผ ์กฐํํด์จ ๊ฐ์ ์ฐ์ด๋ณด์๋๋ฐ,
thread2
Executing order # on thread: ์ฌ๊ณ ์๋ ๊ฐ์ ํ ์์ ์ผ์ ํ์ธ์ฉpool-1-thread-2prodQtyList2: [ProdOptDTO{optItemNm='null', optCombNo=1, optPrc=0, invQty=0, prodId=10001, shoesSize='null', regDttm=null, regrId=0, updDttm=Mon Feb 05 18:39:56 KST 2024, updrId=0, delDttm=null, delrId=0, delYn='null'}]
thread1
Executing order # on thread: ์ฌ๊ณ ์๋ ๊ฐ์ ํ ์์ ์ผ์ ํ์ธ์ฉpool-1-thread-1prodQtyList2: [ProdOptDTO{optItemNm='null', optCombNo=1, optPrc=0, invQty=-1, prodId=10001, shoesSize='null', regDttm=null, regrId=0, updDttm=Mon Feb 05 18:39:56 KST 2024, updrId=0, delDttm=null, delrId=0, delYn='null'}]
updDttm์ด ์๋ถโ์ดโ๊น์ง๋ง ๊ฐ์ ๊ฐ์ ธ์จ ๊ฑฐ ๋ณด๊ณ ํน์ ํธ๋์ญ์ ์ด ๋๋ฌด ๋น ๋ฅด๊ฒ ์งํ๋์ด์ ๊ทธ๋ฐ๊ฐ..? ๋ผ๋ ์๊ฐ์ด ๋ฒ๋ฉ ๋ค์๋ค.
๊ฒฐ๋ก ์ ์ด๊ฒ ๋ง์๋ค..!
๋ชจ๋ ๋ก์ง์ด ์ ๋ถ 56์ด ๋ด์ ์ํ๋์ด
ํธ๋์ญ์ 1์ด ๊ฐ์ ๋ณ๊ฒฝํ ํ now()๋ก updDttm์ ์์ ํ์ง๋ง ์ฒ์ ์กฐํํด์จ ๊ฐ๊ณผ ๊ฐ์ 18:39:56์ด ์ ์ฅ๋์ด์ ํธ๋์ญ์ 2๊ฐ ๋ณํ๋ฅผ ๊ฐ์งํ์ง ๋ชปํด ๋ง์ง๋ง ์ฝ์ ํ๋ก ๋ณ๊ฒฝ๋์ง ์์๋ค๊ณ ํ๋จํด ์ฌ๊ณ ์๋์ ๋ ์ฐจ๊ฐํด -1์ด ๋๊ฒ..
์ฌ๊ณ ๊ฐ 1๊ฐ ๋จ์ ์ํ์ 2๊ฐ์ ์ฐ๋ ๋๊ฐ ๋์์ ์ฃผ๋ฌธ
- ์ฒ์์ ์ฌ๊ณ ์๋์ ์กฐํํด์ฌ ๋ ๋ณ๊ฒฝ์ผ์๋ ์กฐํํด์จ๋ค.
- A ํธ๋์ญ์ ์ด ๋จผ์ ์๋ ๋ณ๊ฒฝ์ ํ๋ค.
- ๊ทธ๋ฆฌ๊ณ ๋ ๊ฑฐ์ ๋์์ B ํธ๋์ญ์ ์ด ์๋์ ๋ณ๊ฒฝํ๋ค.
- 2๋ฒ, 3๋ฒ ๋ณ๊ฒฝ ์์ฐจ ์ฐจ์ด๊ฐ ๊ฑฐ์ ์๋ค๋ณด๋๊น 2๊ฐ ๋ณ๊ฒฝ๋์ด์ ์ปค๋ฐ๋๊ธฐ ์ ์ 3๋ ๋ณ๊ฒฝ ์๋๋ฅผ ํจ
๐ ์์ธ
์์ ์ผ์ ์ปฌ๋ผ์ด ๋ฐ๋ฆฌ์ด(millisecond) ๋จ์๊น์ง ์ ์ฅํ์ง ์๊ณ , ์๋ถ์ด(second) ๋จ์๊น์ง๋ง ์ ์ฅํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ๋ ๋ฌธ์ ์๋ค. ์ด ๊ฒฝ์ฐ ๊ฐ์ ์ด ๋ด์ ์ฌ๋ฌ ํธ๋์ญ์ ์ด ๋์์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ฒ ๋๋ฉด ์์ ์ผ์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ณ๊ฒฝ์ ๊ฐ์งํ๋ ๊ฒฝ์ฐ ์ ํํ๊ฒ ๋์ํ์ง ์์ ์ ์๋ค. ํนํ ๊ณ ์์ผ๋ก ์ฒ๋ฆฌ๋๋ ํธ๋์ญ์ ํ๊ฒฝ์์๋ ๊ฐ์ ์ด ๋ด์ ์ฌ๋ฌ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋ ๊ฐ๋ฅ์ฑ์ด ๋๋ค.
โ ย ํด๊ฒฐ๋ฐฉ๋ฒ
- prod_opt ํ ์ด๋ธ์ timestamp ์ปฌ๋ผ์ ํ์ ์ timestamp(3)์ผ๋ก ๋ณ๊ฒฝ
- UPD_DTTM ๊ฐ์ ๋ฃ์ด์ค ๋ now()๊ฐ ์๋ CURRENT_TIMESTAMP(3)๋ก ๋ฃ์ด์ฃผ๋๋ก ์ฟผ๋ฆฌ ๋ณ๊ฒฝ
<select id="selectProductQty" parameterType="int" resultType="ProdOptDTO">
SELECT PO.PROD_ID, PO.OPT_COMB_NO, PO.INV_QTY, PO.UPD_DTTM
FROM PROD_OPT PO
WHERE OPT_COMB_NO IN
<foreach collection="array" item="optCombNoArr" open="(" close=")" separator=",">
#{optCombNoArr}
</foreach>
</select>
<update id="decreaseProdQty" parameterType="Map">
UPDATE prod_opt
SET INV_QTY = INV_QTY - #{qty}
, UPD_DTTM = CURRENT_TIMESTAMP(3)
WHERE OPT_COMB_NO = #{optCombNo}
AND UPD_DTTM = #{updDttm}
</update>
Uploaded by N2T