λκ΄μ λμμ± μ μ΄λ₯Ό μ μ©νλ©΄μ λ§μ£ΌμΉ λ¬Έμ : νμμ€ν¬ν
βοΈΒ κ²°λ‘
πΒ λ¬Έμ μ μ
μ²μμ λμμ± λ¬Έμ λ₯Ό ν΄κ²°νλ €κ³ νμμ λ, μ²μμ κ°μ μ 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