πŸ’₯ 마주친 이슈

  • λͺ©ν‘œ: μ—¬λŸ¬ μ‚¬μš©μžκ°€ λ™μ‹œμ— 포인트λ₯Ό μ†‘κΈˆν•  λ•Œ 데이터 정합성을 μœ μ§€ν•˜λŠ” λ‘œμ§μ„ κ΅¬ν˜„ν•˜κ³ , λ°λ“œλ½ λ°œμƒ μ—¬λΆ€λ₯Ό 확인

πŸ§ͺ μˆ˜ν–‰ν•œ ν…ŒμŠ€νŠΈ

  • ν…ŒμŠ€νŠΈ μ‹œλ‚˜λ¦¬μ˜€
    • ν•˜λ‚˜μ˜ 솑신 κ³„μ’Œ(sender)μ—μ„œ μˆ˜μ‹  κ³„μ’Œ(receiver)둜 50 ν¬μΈνŠΈμ”© μ†‘κΈˆ
    • 총 10개의 μ“°λ ˆλ“œκ°€ λ™μ‹œμ— μ†‘κΈˆ μš”μ²­μ„ μ‹€ν–‰
    • 각 μ“°λ ˆλ“œλŠ” PointService.transferPoints() λ©”μ„œλ“œλ₯Ό 호좜
  • 핡심 ν…ŒμŠ€νŠΈ μ½”λ“œ

  • ν…ŒμŠ€νŠΈ 둜그
2025-04-05T16:38:42.166+09:00  WARN 15836 --- [point-service] [pool-2-thread-9] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1213, SQLState: 40001
2025-04-05T16:38:42.166+09:00  WARN 15836 --- [point-service] [pool-2-thread-5] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1213, SQLState: 40001
2025-04-05T16:38:42.166+09:00 ERROR 15836 --- [point-service] [pool-2-thread-9] o.h.engine.jdbc.spi.SqlExceptionHelper   : Deadlock found when trying to get lock; try restarting transaction
2025-04-05T16:38:42.166+09:00 ERROR 15836 --- [point-service] [pool-2-thread-5] o.h.engine.jdbc.spi.SqlExceptionHelper   : Deadlock found when trying to get lock; try restarting transaction
2025-04-05T16:38:42.166+09:00  WARN 15836 --- [point-service] [pool-2-thread-3] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1213, SQLState: 40001
2025-04-05T16:38:42.167+09:00  WARN 15836 --- [point-service] [pool-2-thread-7] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1213, SQLState: 40001
2025-04-05T16:38:42.166+09:00  WARN 15836 --- [point-service] [pool-2-thread-4] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1213, SQLState: 40001
2025-04-05T16:38:42.166+09:00  WARN 15836 --- [point-service] [pool-2-thread-6] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1213, SQLState: 40001
2025-04-05T16:38:42.167+09:00 ERROR 15836 --- [point-service] [pool-2-thread-3] o.h.engine.jdbc.spi.SqlExceptionHelper   : Deadlock found when trying to get lock; try restarting transaction
2025-04-05T16:38:42.166+09:00  WARN 15836 --- [point-service] [ool-2-thread-10] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1213, SQLState: 40001
2025-04-05T16:38:42.167+09:00 ERROR 15836 --- [point-service] [ool-2-thread-10] o.h.engine.jdbc.spi.SqlExceptionHelper   : Deadlock found when trying to get lock; try restarting transaction
2025-04-05T16:38:42.166+09:00  WARN 15836 --- [point-service] [pool-2-thread-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1213, SQLState: 40001
2025-04-05T16:38:42.167+09:00 ERROR 15836 --- [point-service] [pool-2-thread-2] o.h.engine.jdbc.spi.SqlExceptionHelper   : Deadlock found when trying to get lock; try restarting transaction
2025-04-05T16:38:42.166+09:00  WARN 15836 --- [point-service] [pool-2-thread-8] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1213, SQLState: 40001
2025-04-05T16:38:42.168+09:00 ERROR 15836 --- [point-service] [pool-2-thread-8] o.h.engine.jdbc.spi.SqlExceptionHelper   : Deadlock found when trying to get lock; try restarting transaction
2025-04-05T16:38:42.167+09:00 ERROR 15836 --- [point-service] [pool-2-thread-7] o.h.engine.jdbc.spi.SqlExceptionHelper   : Deadlock found when trying to get lock; try restarting transaction
2025-04-05T16:38:42.167+09:00 ERROR 15836 --- [point-service] [pool-2-thread-4] o.h.engine.jdbc.spi.SqlExceptionHelper   : Deadlock found when trying to get lock; try restarting transaction
2025-04-05T16:38:42.167+09:00 ERROR 15836 --- [point-service] [pool-2-thread-6] o.h.engine.jdbc.spi.SqlExceptionHelper   : Deadlock found when trying to get lock; try restarting transaction
μ—λŸ¬ λ°œμƒ: could not execute statement [Deadlock found when trying to get lock; try restarting transaction] [update point_accounts set total_points=?,user_id=? where account_id=?]; SQL [update point_accounts set total_points=?,user_id=? where account_id=?]
μ—λŸ¬ λ°œμƒ: could not execute statement [Deadlock found when trying to get lock; try restarting transaction] [update point_accounts set total_points=?,user_id=? where account_id=?]; SQL [update point_accounts set total_points=?,user_id=? where account_id=?]
μ—λŸ¬ λ°œμƒ: could not execute statement [Deadlock found when trying to get lock; try restarting transaction] [update point_accounts set total_points=?,user_id=? where account_id=?]; SQL [update point_accounts set total_points=?,user_id=? where account_id=?]
μ—λŸ¬ λ°œμƒ: could not execute statement [Deadlock found when trying to get lock; try restarting transaction] [update point_accounts set total_points=?,user_id=? where account_id=?]; SQL [update point_accounts set total_points=?,user_id=? where account_id=?]
μ—λŸ¬ λ°œμƒ: could not execute statement [Deadlock found when trying to get lock; try restarting transaction] [update point_accounts set total_points=?,user_id=? where account_id=?]; SQL [update point_accounts set total_points=?,user_id=? where account_id=?]
μ—λŸ¬ λ°œμƒ: could not execute statement [Deadlock found when trying to get lock; try restarting transaction] [update point_accounts set total_points=?,user_id=? where account_id=?]; SQL [update point_accounts set total_points=?,user_id=? where account_id=?]
μ—λŸ¬ λ°œμƒ: could not execute statement [Deadlock found when trying to get lock; try restarting transaction] [update point_accounts set total_points=?,user_id=? where account_id=?]; SQL [update point_accounts set total_points=?,user_id=? where account_id=?]
μ—λŸ¬ λ°œμƒ: could not execute statement [Deadlock found when trying to get lock; try restarting transaction] [update point_accounts set total_points=?,user_id=? where account_id=?]; SQL [update point_accounts set total_points=?,user_id=? where account_id=?]
μ—λŸ¬ λ°œμƒ: could not execute statement [Deadlock found when trying to get lock; try restarting transaction] [update point_accounts set total_points=?,user_id=? where account_id=?]; SQL [update point_accounts set total_points=?,user_id=? where account_id=?]
Sender μž”μ•‘: 950
Receiver μž”μ•‘: 50
Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2025-04-05T16:38:42.229+09:00  INFO 15836 --- [point-service] [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2025-04-05T16:38:42.232+09:00  INFO 15836 --- [point-service] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2025-04-05T16:38:42.306+09:00  INFO 15836 --- [point-service] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
> Task :test
BUILD SUCCESSFUL in 6s
4 actionable tasks: 1 executed, 3 up-to-date
μ˜€ν›„ 4:38:42: Execution finished ':test --tests "com.timebank.pointservice.service.PointServiceTest.λ™μ‹œμ„±_ν…ŒμŠ€νŠΈ_μ†‘κΈˆ_좩돌"'.

🚨 λ°œμƒν•œ 문제: λ°λ“œλ½(Deadlock)

  • μ—λŸ¬ 둜그 μš”μ•½:
  • pgsql
    λ³΅μ‚¬νŽΈμ§‘
    SQL Error: 1213, SQLState: 40001 Deadlock found when trying to get lock; try restarting transaction
  • 원인 뢄석:
    • μ—¬λŸ¬ νŠΈλžœμž­μ…˜μ΄ λ™μ‹œμ— λ™μΌν•œ 솑신 κ³„μ’Œ row에 λŒ€ν•΄ updateλ₯Ό μ‹œλ„ν•˜λ©° ꡐ착 μƒνƒœ λ°œμƒ
    • JPAκ°€ μžλ™μœΌλ‘œ μ²˜λ¦¬ν•˜λŠ” SQL update μΏΌλ¦¬μ—μ„œ 락 좩돌 λ°œμƒ

🧩 ν•΄κ²° μ ‘κ·Ό 방식

  • 문제 인식:
    • λ‹¨μˆœν•œ λ™μ‹œ 호좜이 μ•„λ‹ˆλΌ 동일 μžμ›μ— λŒ€ν•œ 경쟁 접근이 μ›μΈμž„μ„ νŒŒμ•…
  • λŒ€μ‘ 방법:

 

βœ… 1. 비관적 락 + νŠΈλžœμž­μ…˜ 처리 (μ„œλΉ„μŠ€ λ‚΄λΆ€)

πŸ’‘ μ–΄λ–€ 상황에 μœ λ¦¬ν•΄?

  • λ™μΌν•œ 데이터λ₯Ό λ™μ‹œμ— μˆ˜μ •ν•˜λ €λŠ” 경우 (ex. 포인트 차감, μž”κ³  κ°μ†Œ λ“±)
  • μ‹€μ‹œκ°„ 정합성이 μ•„μ£Ό μ€‘μš”ν•œ 경우

πŸ’ͺ μž₯점:

  • 좩돌 μ¦‰μ‹œ 차단 (락을 κ±Έμ–΄μ„œ λ‹€λ₯Έ νŠΈλžœμž­μ…˜ μ ‘κ·Ό λ§‰μŒ)
  • 데이터 μ •ν•©μ„± 맀우 λ†’μŒ

⚠️ 주의점:

  • λ™μ‹œμ— λ“€μ–΄μ˜€λŠ” νŠΈλž˜ν”½μ΄ 많으면 락 λŒ€κΈ° μ‹œκ°„μ΄ λŠ˜μ–΄λ‚˜ μ„±λŠ₯ μ €ν•˜ 우렀 → κ·Έλž˜μ„œ CQRS + λ©”μ‹œμ§€ 큐 μ‘°ν•©κ³Ό 같이 μ¨μ€˜μ•Ό 함.

βœ… 2. Redis λΆ„μ‚° 락 or λ©”μ‹œμ§€ 큐 (μ„œλΉ„μŠ€ κ°„ 처리)

πŸ’‘ μ–΄λ–€ 상황에 μœ λ¦¬ν•΄?

  • μ—¬λŸ¬ μ„œλΉ„μŠ€κ°€ λ™μ‹œμ— 동일 λ¦¬μ†ŒμŠ€λ₯Ό μ²˜λ¦¬ν•  수 μžˆμ–΄μ„œ μˆœμ„œ 보μž₯, 쀑볡 λ°©μ§€κ°€ ν•„μš”ν•  λ•Œ
  • MSA ν™˜κ²½μ—μ„œ 비동기 μ†‘μˆ˜μ‹ ν•  λ•Œ (예: Kafka 이벀트 μ†ŒλΉ„)

πŸ’ͺ μž₯점:

  • λ©”μ‹œμ§€ 큐(Kafka λ“±)λŠ” μˆœμ„œ 보μž₯, μž¬μ‹œλ„, μž₯μ•  λŒ€μ‘ κ°€λŠ₯
  • Redis λΆ„μ‚° 락은 κ°„λ‹¨ν•œ 순차 μ œμ–΄μš©μœΌλ‘œ λΉ λ₯΄κ³  유용

⚠️ 주의점:

  • Redis 락은 λ„€νŠΈμ›Œν¬ μž₯μ• /만료 λ“± μ—£μ§€ μΌ€μ΄μŠ€ 주의
  • Kafka λ“± λ©”μ‹œμ§€ νλŠ” μ μ ˆν•œ consumer 처리 속도와 μ˜€ν”„μ…‹ 관리 ν•„μš”

βœ… 3. μ‘°νšŒλŠ” CQRS둜 뢄리 (Command/Query 뢄리)

πŸ’‘ μ–΄λ–€ 상황에 μœ λ¦¬ν•΄?

  • νŠΈλž˜ν”½μ˜ λŒ€λΆ€λΆ„μ΄ 쑰회 쀑심일 λ•Œ
  • 정합성보닀 응닡 속도가 μ€‘μš”ν•œ ν™”λ©΄ (예: λ§ˆμ΄νŽ˜μ΄μ§€, λŒ€μ‹œλ³΄λ“œ λ“±)

πŸ’ͺ μž₯점:

  • 쑰회 쿼리λ₯Ό 별도 DB/Redis μΊμ‹œλ‘œ λΆ„λ¦¬ν•˜λ©΄ 메인 νŠΈλžœμž­μ…˜μ— λΆ€λ‹΄ X
  • 읽기 μ „μš© DBλ‚˜ Read Replication μ‚¬μš©ν•˜λ©΄ ν™•μž₯성도 πŸ‘

πŸ’‘ κ²°λ‘ : 이 κ΅¬μ‘°λŠ” λŒ€κ·œλͺ¨ μ„œλΉ„μŠ€μ—μ„œ μ‹€μ œλ‘œ 많이 μ“°μ΄λŠ” νŒ¨ν„΄

  • 카카였, λ°°λ―Ό, ν† μŠ€, 쿠팑 등도 이런 λ°©μ‹μœΌλ‘œ λ‚΄λΆ€ μ •ν•©μ„± + μ™ΈλΆ€ 이벀트 처리 + 쑰회 μ΅œμ ν™”λ₯Ό μ‘°ν•©ν•΄μ„œ μ‚¬μš©ν•΄.
  • μ§€κΈˆμ²˜λŸΌ MSA + Kafka + DB 기반 κ΅¬μ‘°μ—μ„œλŠ” 이 μ…‹μ˜ 쑰합이 ν˜„μ‹€μ μΈ 베슀트 ν”„λž™ν‹°μŠ€λΌκ³  보면 돼.

+ Recent posts