비관적 락 -> 낙관적 락으로 변경

Cori1304·2025년 12월 1일

스타벅스 클론 

목록 보기
3/3

배경

부트캠프에서 진행한 스타벅스 클론에서 동시성 이슈를 비관적 락으로 해결하였다. 그런데 낙관적 락으로 해결을 왜 안 했냐는 질문을 받았고 주문에 대한 내용은 중요한 정보이기에 비관적 락으로 해결했다라는 나의 답은 근거가 없는 거라고 답변을 들었기에 2개를 비교하는 실험을 진행하려고 한다.

이전 프로젝트 내용 (비 관적락 사용)

테스트 환경

낙 관적락 1차 테스트

1분 동안 100 RPS를 발생 시켰다. 당연히 실패율 0%에 비관적락 이상에 결과를 보여 줄 주 알았는데 아래 표와 같이 이상한 결과를 얻었다.

성능 비교표

락 방식부하 (req/s)총 요청수실패율평균 응답시간최대 응답시간결과
비관적 락10060000%~15ms~50ms성공
낙관적 락3018000.8-2.4%~15-18ms~487ms⚠️ 불안정
낙관적 락10012199.2%39,623ms60,003ms실패

1차 테스트 원인 분석

원인: 부하 테스트시 1개에 매장에만 요청을 보내는 것이 문제였다.

상세 설명

기존 테스트는 모든 주문이 1개에 매장으로 향한다. 이 경우 동시성 이슈가 있던 주문번호 생성 로직은 100% 충돌이 발생한다. 매장은 주문번호 생성을 위해서 주문이 들어오면 counter +1을 한 값을 주문번호로 만든다. 이때 동시에 요청이 들어오면 충돌이 발생했고 비관적 락은 충돌을 일어나는 것을 전제로 만든 로직이었고 낙관적 락은 그렇지 않았던게 문제 였다.
(낙관적 락은 충돌이 일어나지 않고 서로 다른 레코드에 적용해야 하는데 테스트 환경이 그렇지 못한 것이었다.)

시퀀스 다이어그램

k6 코드 변경

// 문제 코드
function makeOrderPayload(email, idx) {
  return JSON.stringify({
    storeId: 1, -> 원인 
    pickupType: "STORE_PICKUP",
    requestMemo: `주문자: ${email} - 얼음 많이, 샷 연하게 부탁드려요!`,
    cardNumber: `1234-5678-9${String(idx).padStart(3, "0")}-0000`,
    orderItems: [
      {
     //... 


// 수정 코드, storeId를 고정이 아닌 1~10인 랜덤 값으로 설정 (DB에 매장은 10개만 존재) 
function makeOrderPayload(email, idx) {
  const randomStoreId = Math.floor(Math.random() * 10) + 1; // 1~10 사이의 랜덤 storeId
  return JSON.stringify({
    storeId: randomStoreId,
    pickupType: "STORE_PICKUP",
    requestMemo: `주문자: ${email} - 얼음 많이, 샷 연하게 부탁드려요!`,
    cardNumber: `1234-5678-9${String(idx).padStart(3, "0")}-0000`,
    orderItems: [
      {

원인 요약

  • spring boot에 문제 X, k6 부하 테스트 시나리오 문제
  • 기존은 1개 매장에 1분 동안 100RPS, 현재는 10개에 매장에 1분 동안 100RPS
  • 동일안 레코드에서 충돌 발생, 낙관적 락으로 해결할 수 없었기에 결과가 이상한게 아닌 테스트 시나리오가 문제

낙 관적락 2차 테스트

k6 코드 변경 후 드디어 원하던 결과를 얻었다.

성능 비교표

락 방식부하 (req/s)총 요청수실패율평균 응답시간최대 응답시간결과
비관적 락10060000%~15ms~50ms성공
낙관적 락10060000%~11ms107.63ms성공

결론

처음 시도에서는 많이 당황했다. java 코드 문제라고 생각해서 AI에게 계속 물어도 해결지 않았던 문제였지만 낙관적 락을 다시 공부하면서 문제를 해결하였다. 이래서 CS 지식 즉 이론이 중요하다고 생각하게 되었다.

참고 자료

profile
개발 공부 기록

0개의 댓글