πŸ‘¨‍πŸ‘©‍πŸ‘§‍πŸ‘¦ Project/πŸ“Ί KIOSEK

Named Lock - 뢄산락을 μ΄μš©ν•œ 쀑볡 μ˜ˆμ•½ λ™μ‹œμ„± 문제 ν•΄κ²°

DevPoong 2023. 3. 12. 01:44

1. 뢄산락을 μ μš©ν•˜κ²Œ 된 상황

λ§Œμ•½ μ—¬λŸ¬ μ‚¬μš©μžκ°€ λ™μ‹œμ— 같은 μ‹œκ°„λŒ€μ΄κ±°λ‚˜ κ²ΉμΉ˜λŠ” μ‹œκ°„λŒ€μ— μ˜ˆμ•½ 진행 μ‹œ μ–΄λ–»κ²Œ 쀑볡 μ˜ˆμ•½μ„ 막을 수 μžˆμ„μ§€ λ¬Έμ œμ˜€λ‹€.

ν•΄λ‹Ή μ˜ˆμ•½ ν…Œμ΄λΈ”μ€ μ•„λž˜μ™€ 같이 생성 λ˜μ–΄μžˆλ‹€. ν•˜λ‚˜μ˜ 회의 ν…Œμ΄λΈ”μ€ 이미 μ˜ˆμ•½λœ 데이터와 μ‹œκ°„μ΄ κ²ΉμΉ˜μ§€ μ•Šμ•„μ•Όλ§Œ μ˜ˆμ•½μ΄ κ°€λŠ₯ν•˜λ‹€.

λ™μ‹œμ— 100λͺ…μ˜ μ‚¬μš©μžκ°€ λ˜‘κ°™μ€ (μ‹œκ°„ + μž₯μ†Œ)λ₯Ό μ˜ˆμ•½ν–ˆμ„ λ•Œ 8개의 쀑볡 데이터가 λ°œμƒν•˜μ˜€λ‹€.

쀑볡 데이터가 8개 발

 

2. MySQL을 μ΄μš©ν•œ 뢄산락을 κ΅¬ν˜„ν•œ 이유

뢄산락을 κ΅¬ν˜„ν•˜λŠ” 방법에 λŒ€ν•΄ 검색해보면 Redisλ₯Ό μ΄μš©ν•œ 방법이 많이 보인닀.
ν•˜μ§€λ§Œ Redisλ₯Ό μ΄μš©ν•˜κ²Œ 되면 좔가적인 인프라 κ΅¬μΆ•λΉ„μš©μ΄λ‚˜ μœ μ§€λ³΄μˆ˜ λΉ„μš©μ΄ λ°œμƒν•œλ‹€.

λ”°λΌμ„œ, λ‚˜λŠ” 기쑴에 μ‚¬μš©ν•˜κ³  있던 MySQLμ—μ„œ μ œκ³΅ν•˜λŠ” NAMED LOCK을 μ΄μš©ν•˜μ—¬ Lock에 이름을 지정할 수 있기 λ•Œλ¬Έμ— ν•΄λ‹Ή Lock의 이름을 μ΄μš©ν•˜μ—¬ μ• ν”Œλ¦¬μΌ€μ΄μ…˜λ‹¨μ—μ„œ μ œμ–΄κ°€ κ°€λŠ₯ν•˜λ‹€λŠ” μž₯점을 μ΄μš©ν•˜κΈ°λ‘œ ν–ˆλ‹€.

 

3. Named Lock에 λŒ€ν•œ 간단 μ„€λͺ…

Named Lock은 이름을 가진 metadata lock이라고 ν•  수 μžˆλ‹€. 이름을 가진 lock을 νšλ“ν•œ ν›„ ν•΄μ œν•  λ•ŒκΉŒμ§€ λ‹€λ₯Έ μ„Έμ…˜μ€ 이 Lock을 νšλ“ν•  수 없도둝 ν•œλ‹€. 데이터 μ‚½μž…μ‹œμ— 정합성을 λ§žμΆ°μ•Ό ν•˜λŠ” 경우 μ‚¬μš© κ°€λŠ₯ν•˜λ‹€.

βœ… μ£Όμ˜ν•  μ μœΌλ‘œλŠ” transaction이 μ’…λ£Œλ  λ•Œ lock이 μžλ™μœΌλ‘œ ν•΄μ œλ˜μ§€ μ•ŠλŠ”λ‹€λŠ” 것이닀. λ³„λ„μ˜ λͺ…λ Ήμ–΄λ‘œ ν•΄μ œλ₯Ό μˆ˜ν–‰ν•΄ μ£Όκ±°λ‚˜ μ„ μ μ‹œκ°„μ΄ λλ‚˜μ•Ό ν•΄μ œλœλ‹€.

  • 이름과 ν•¨κ»˜ lock을 νšλ“ν•œλ‹€. ν•΄λ‹Ή lock 은 λ‹€λ₯Έ μ„Έμ…˜μ—μ„œ νšλ“ 및 ν•΄μ œκ°€ λΆˆκ°€λŠ₯ν•˜λ‹€.
  • mysqlμ—μ„œλŠ” get_lock()을 톡해 lock을 μ–»κ³ , release_lock()μ΄λΌλŠ” λͺ…λ Ήμ–΄λ₯Ό 톡해 named lock을 ν•΄μ œν•  수 μžˆλ‹€.
  • Pessimistic, Optimistic lock은 item에 λŒ€ν•΄μ„œ lock을 κ±Έμ—ˆλ‹€λ©΄ named Lock은 별도 MySQL μ„œλ²„ λ©”λͺ¨λ¦¬ 곡간에 lock을 건닀.

GET_LOCK(name, timeout)

  • μž…λ ₯받은 μ΄λ¦„μœΌλ‘œ timeout초 λ™μ•ˆ 잠금 νšλ“μ„ μ‹œλ„ν•œλ‹€.
  • λ§Œμ•½μ—λΌλ„ μž κΈˆμ„ νšλ“ν•  λ•ŒκΉŒμ§€ λ¬΄ν•œλŒ€λ‘œ λŒ€κΈ°ν•˜λ„λ‘ ν•˜λ €λ©΄ timeout을 음수둜 μ„€μ •ν•˜λ©΄ λœλ‹€.
  • ν•œ sessionμ—μ„œ μž κΈˆμ„ μœ μ§€ν•˜κ³  μžˆλŠ” λ™μ•ˆμ—λŠ” λ‹€λ₯Έ sessionμ—μ„œ λ™μΌν•œ μ΄λ¦„μ˜ μž κΈˆμ„ νšλ“ν•  수 μ—†λ‹€.
  • GET_LOCK()을 μ΄μš©ν•˜μ—¬ νšλ“ν•œ μž κΈˆμ€ Transaction이 commit λ˜λŠ” rollback λ˜λ”λΌλ„ 슀슀둜 ν•΄μ œλ˜μ§€ μ•ŠλŠ”λ‹€.
  • GET_LOCK의 결과값은 1, 0, null을 λ°˜ν™˜ν•œλ‹€.
    • 1 : μž κΈˆμ„ νšλ“ν•˜λŠ”λ° 성곡
    • 0 : timeout 초 λ™μ•ˆ 잠금 νšλ“ μ‹€νŒ¨
    • null : 잠금 νšλ“ 쀑 μ—λŸ¬κ°€ λ°œμƒ (ex : Out of Memory, ν˜„μž¬ μŠ€λ ˆλ“œκ°€ κ°•μ œ μ’…λ£Œλ¨)
  • MySQL 5.7 버전 μ΄ν›„λΆ€ν„°λŠ” λ™μ‹œμ— μ—¬λŸ¬κ°œμ˜ μž κΈˆμ„ νšλ“ κ°€λŠ₯ν•˜κ³ , 잠금 이름 κΈ€μžμˆ˜κ°€ 60자둜 μ œν•œλ˜μ—ˆμŒμ„ μœ μ˜ν•˜μž.

RELEASE_LOCK(name)

  • μž…λ ₯받은 μ΄λ¦„μ˜ μž κΈˆμ„ ν•΄μ œν•œλ‹€.
  • RELEASE_LOCK의 결과값은 1, 0, null을 λ°˜ν™˜ν•œλ‹€.
    • 1 : μž κΈˆμ„ μ„±κ³΅μ μœΌλ‘œ ν•΄μ œ
    • 0 : 잠금이 ν•΄μ œλ˜μ§€λŠ” μ•Šμ•˜μ§€λ§Œ, ν˜„μž¬ μ“°λ ˆλ“œμ—μ„œ νšλ“ν•œ 잠금이 μ•„λ‹Œκ²½μš°
    • null : 잠금이 μ‘΄μž¬ν•˜μ§€ μ•ŠμŒ
  • RELEASE_ALL_LOCKS() λͺ…령을 톡해 ν˜„μž¬ μ„Έμ…˜μ—μ„œ μœ μ§€λ˜κ³  μžˆλŠ” λͺ¨λ“  μž κΈˆμ„ ν•΄μ œν•˜κ³  ν•΄μ œν•œ 잠금 개수λ₯Ό λ°˜ν™˜λ°›μ„ 수 μžˆλ‹€.

ETC

  • IS_FREE_LOCK(name)
    • μž…λ ₯ν•œ 이름에 ν•΄λ‹Ήν•˜λŠ” 잠금이 νšλ“ κ°€λŠ₯ν•œμ§€ ν™•μΈν•œλ‹€.
      • 1 : μž…λ ₯ν•œ μ΄λ¦„μ˜ 잠금이 μ—†μŒ
      • 0 : μž…λ ₯ν•œ μ΄λ¦„μ˜ 잠금이 있음
      • null: μ—λŸ¬ λ°œμƒ (ex: 잘λͺ»λœ 인자)
  • IS_USED_LOCK(name)
    • μž…λ ₯ν•œ μ΄λ¦„μ˜ 잠금이 μ‚¬μš©μ€‘μΈμ§€ ν™•μΈν•œλ‹€.
    • μž…λ ₯받은 μ΄λ¦„μ˜ 잠금이 μ‘΄μž¬ν•˜λ©΄ connection idλ₯Ό λ°˜ν™˜ν•˜κ³ , μ—†μœΌλ©΄ null을 λ°˜ν™˜ν•œλ‹€.

 

4. 뢄산락 κ΅¬ν˜„

1. DataSource 뢄리

Lock을 μ–»κΈ° μœ„ν•œ DataSourceλ₯Ό λΆ„λ¦¬ν•¨μœΌλ‘œμ¨, Lock이 ν•„μš”ν•˜μ§€ μ•Šμ€ μš”μ²­λ“€μ— λŒ€ν•΄μ„œ 컀λ„₯μ…˜ 풀을 보μž₯해쀄 수 μžˆλ‹€.
μ™œλƒν•˜λ©΄, 컀λ„₯μ…˜ ν’€λ§Œ 더 늘렀봀자 Lock이 ν•„μš”ν•œ μš”μ²­λ“€μ—κ²Œ λͺ¨λ‘ μ μœ λ‹Ήν•΄λ²„λ¦°λ‹€λ©΄ Lock이 ν•„μš”ν•˜μ§€ μ•Šμ€ μš”μ²­λ“€μ€ 컀λ„₯μ…˜ 풀에 λ°˜ν™˜λ˜κΈ° κΉŒμ§€ λŒ€κΈ°ν•΄μ•Ό ν•˜λŠ” λ¬Έμ œκ°€ μƒκΈ°λ―€λ‘œ ConnectionPool을 λΆ„λ¦¬ν•˜λŠ” 것이닀.

 

2. λΆ„λ¦¬λœ Named Lock용 DataSourceλ₯Ό μ΄μš©ν•œ JDBC κ΅¬ν˜„

 

βœ… JdbcTemplate을 μ΄μš©ν•˜μ§€ μ•Šμ€ μ΄μœ λŠ”?
JdbcTemplate의 νŠΉμ„±μƒ GET_LOCK을 μˆ˜ν–‰ν•˜λŠ” 쿼리가 μ‹€ν–‰λ˜κ³  νŠΈλžœμž­μ…˜μ΄ μ’…λ£Œλ˜κ²Œ 되면 pollμ—μ„œ μ–»μ–΄μ˜¨ Connection을 λ°˜ν™˜ν•˜κ²Œ λœλ‹€.
1. κ·Έλž˜μ„œ RELEASE_LOCK을 μ‹€ν–‰ν•  λ•Œ GET_LOCK을 μ‹€ν–‰ν• λ•Œμ™€ λ‹€λ₯Έ Connection을 μ–»μ–΄μ˜€λŠ” λ¬Έμ œκ°€ μžˆκΈ°μ— LOCK을 μ œλŒ€λ‘œ λ°˜ν™˜ λͺ»ν•  μˆ˜λ„ μžˆλ‹€.
2. λ˜ν•œ λ™μ‹œμ— λ™μΌν•œ Lock μ΄λ¦„μœΌλ‘œ μš”μ²­ν•œ λ‹€λ₯Έ μŠ€λ ˆλ“œμ—μ„œ GET_LOCKμ—μ„œ λ°˜ν™˜ν–ˆλ˜ Connection을 νšλ“ν•˜μ—¬ μž κΈˆμ„ 풀어버릴 μœ„ν—˜λ„ μ‘΄μž¬ν•œλ‹€.

 

λ”°λΌμ„œ λ‚˜λŠ” λΆ„λ¦¬λœ DataSourceλ₯Ό μ£Όμž…λ°›μ•„ JDBC둜 직접 κ΅¬ν˜„ν•˜μ˜€λ‹€. JDBCλ₯Ό μ΄μš©ν•΄ 직접 κ΅¬ν˜„ν•˜μ˜€κΈ°μ— Connection을 직접 관리할 수 μžˆμ–΄μ„œ GET_LOCKκ³Ό RELEASE_LOCK이 λͺ¨λ‘ λ™μΌν•œ Connection을 μ‚¬μš©ν•  수 μžˆμ—ˆκ³  νšλ“ν•œ LOCK을 μ •μƒμ μœΌλ‘œ λ°˜ν™˜ν•  수 있게 게 λ˜μ—ˆλ‹€.

executeWithLock() λ©”μ„œλ“œμ—μ„œλŠ” @Transactional을 뢙이지 μ•Šμ•˜λ‹€. μ™œλƒν•˜λ©΄ Lock을 μ–»λŠ” λΆ€λΆ„κ³Ό μ˜ˆμ•½ λ‘œμ§μ„ μˆ˜ν–‰ν•˜λŠ” λΆ€λΆ„μ—μ„œ μ‚¬μš©ν•˜λŠ” ConnectionPool을 각기 λ‹€λ₯΄κ²Œ ν•˜κΈ° μœ„ν•΄ @Transactional을 뢙이지 μ•Šμ•˜λ‹€.
ν•œλ²ˆμ˜ 호좜둜 Lock을 μ–»κ³ , Lock을 ν•΄μ œν•˜λŠ” μž‘μ—…μ„ ν•˜κΈ° μœ„ν•΄ μ˜ˆμ•½ λΉ„μ§€λ‹ˆμŠ€ λ‘œμ§μ€ Supplier μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ΄μš©ν•œ 콜백으둜 μˆ˜ν–‰λ˜λ„λ‘ κ΅¬ν˜„ν–ˆλ‹€. 

GET_LOCKκ³Ό RELEASE_LOCK은 1, 0, null의 값을 λ°˜ν™˜ν•œλ‹€.
λ”°λΌμ„œ getLockκ³Ό releaseLock λ©”μ„œλ“œμ—μ„œ λ°˜ν™˜κ°’μ΄ 0, null인 경우λ₯Ό μ²΄ν¬ν•˜μ—¬ 잠금이 ν•΄μ œλλŠ”μ§€κΉŒμ§€ ν™•μΈν•΄μ•Όν•˜κ³ , 적절히 βœ… μ˜ˆμ™Έμ²˜λ¦¬λ₯Ό ν•΄μ£Όμ–΄μ•Ό ν•œλ‹€.

timeout μ‹œκ°„μ„ 짧게 μ£Όμ—ˆμ„ κ²½μš°λŠ” 락이 ν’€λ €λ²„λ¦¬λŠ” κ²½μš°κ°€ λ°œμƒν•  수 있고 μ‹œκ°„μ„ λ„ˆλ¬΄ 길게 μ£Όλ©΄ releaseλ₯Ό ν•˜μ§€ λͺ»ν–ˆμ„ 경우 μ„œλΉ„μŠ€μ†λ„μ €ν•˜λ‘œ μ΄μ–΄μ§ˆ 수 μžˆλ‹€. μΌλ°˜μ μœΌλ‘œ 2~5초 사이면 μ λ‹Ήν•˜λ‹€.

 

5. λ™μ‹œμ„± ν…ŒμŠ€νŠΈ + TPS μΈ‘μ •

1.Jmeterλ₯Ό μ΄μš©ν•œ ν…ŒμŠ€νŠΈ

μš΄μ˜μ„œλ²„μ— λŒ€ν•΄μ„œ 100λͺ…이 λ™μ‹œμ— 쀑볡 μ˜ˆμ•½ν•˜λŠ” 상황을 ν…ŒμŠ€νŠΈν–ˆλ‹€.
ν…ŒμŠ€νŠΈ ν•œ κ²°κ³Ό ν•˜λ‚˜μ˜ μš”μ²­λ§Œ μ„±κ³΅ν•˜κ³  λ‹€λ₯Έ μš”μ²­λ“€μ€ μ‹€νŒ¨ν•¨μœΌλ‘œμ¨ λ™μ‹œμ„± μ²˜λ¦¬κ°€ μž˜λ˜μ—ˆμŒμ„ λ³Ό 수 μžˆμ—ˆλ‹€.

2.  Junit을 μ΄μš©ν•œ ν…ŒμŠ€νŠΈ

μœ„μ™€ 같이 100λͺ…이 λ™μ‹œμ— μ€‘λ³΅λ˜λŠ” μ˜ˆμ•½μ„ μš”μ²­ν•˜λŠ” ν…ŒμŠ€νŠΈμ—μ„œ 1개의 μ˜ˆμ•½λ§Œ DB에 μ‚½μž…λ˜μ—ˆμŒμ„ λ³Ό 수 μžˆμ—ˆλ‹€.