안녕하세요. 전에 올리브영 결제 이야기 Part-1 이후, 다시 인사드리게 된 우뱅입니다.
결제 방식의 변화와 모니터링에 대해 1, 2편에서 말씀을 드렸고, 이번 3편에서는 주문결제의 서비스 성능을 향상하기 위해 개선 한 이야기를 하고자 합니다.
'올리브영 선물 픽업 서비스'를 개발한 Aaron님과 함께 올리브영 주문 시에 어떤 식으로 변경하여 서비스 성능을 향상했는지 이야기해보겠습니다.
Introdution
주문을 최종 성립할 때는 많은 프로세스가 존재합니다.
구매 상품의 재고 정보 확인, 결제 금액 확인, 결제, 구매 상품 재고차감 처리, 배송 정보 처리, 주문 완료 정보 생성 등등등..의
많은 프로세스 및 데이터를 정확하게 처리해야만 고객님께서 주문하신 상품을 고객님의 품에 안겨드릴 수가 있습니다.
그래서 위와 같이 많은 로직을 처리할 때, 각각의 프로세스별로 트랜잭션을 이용하여 정확하게 처리하도록 구성되어 있습니다.
트랜잭션으로 처리했다면서??? 그럼 무엇이 문제였는지 알아보겠습니다!
Cause
문제는 동시에 많은 사용자가 물건을 구매할 때 느려지는 현상이었습니다.
왜 동시에 많은 사용자가 구매할 때 느려지게 되었는지를 낱낱이 파헤쳐 보겠습니다.
동시에 많은 사용자가 하나의 데이터를 무작위로 수정하게 된다면 아래 그림처럼 정확하게 어떤 데이터가 저장될지 알 수가 없습니다.
위에서 Olivia, Emma, John은 각각의 트랜잭션으로, Item은 실제 데이터를 저장하는 DB라고 표현을 할 수 있는데 동시성 문제를 해결하기 위해 트랜잭션을 직렬로 순차처리하고, 이렇게 직렬로 처리되는 동안 Item, 즉 DB는 Lock 상태가 되어 앞에 처리 요청한 데이터가 완료되기까지 대기를 하게 됩니다.
여기에서 문제가 되는 부분이 2가지가 있었습니다.
1 ) 줄을 세우니 앞에서 Olivia가 처리가 완료될 때까지 Emma는 기다려야 합니다. 2 ) 기다리는 동안 다른 트랜잭션 Emma는 또 다른 Item을 먼저 처리하고 기다릴 수 있습니다.
- 그런데 만약? Emma가 처리하고 Olivia가 선점한 Item에 접근하게 된다면 어떻게 될까요?
: 이 부분이 저희가 마주한 문제였고, 아래 추가로 이야기해보겠습니다.
DB Lock, Dead Lock!!
DB Lock 이란? 방금 위에서 설명한 대로 데이터 처리의 순차성을 보장하기 위해 해당 처리가 완료될 때까지 데이터 변경을 일시적으로 중지하는 것을 말하고, 순차적 처리를 위해 필요합니다. 그렇다면, 'Dead Lock'이란 무엇일까요? 간단하게 이야기하면 데이터 처리해야 하는데 데이터를 처리할 수 없는 교착상태가 되는 것을 의미합니다.
즉, 위에 문제 2번에 해당합니다.! 백문이 불여일견! 아래 그림으로 설명을 해보겠습니다.
굉장히 높은 트래픽으로 계속해서 교착상태가 발생할 수 있는 구조라면, Dead Lock이 발생하게 되고 이러한 Dead Lock이 중첩으로 계속해서 풀리지 않고 쌓인다면 시스템 리소스를 많이 사용하게 됩니다.
계속해서 점점 더 Dead Lock이 증가한다면... 장비의 리소스는 계속 높아질 거고.. 결국에는.. 시스템 shut down 현상까지도 될 수 있습니다.
개선 방향 및 진행
위처럼 올리브영 주문 완료 처리 시에 트랜잭션 단위가 통으로 묶여서 데이터의 Dead Lock이 발생하는 케이스가 있었습니다. 그래서 Aaron님과 발생하는 부분을 분석하고 각각 개별의 트랜잭션으로 나누기 시작했습니다.
위의 그림은 이해가 쉽게 설명하기 위해 작성된 그림으로 '이렇게 해야 하는 게 당연한 거 아냐?'라고 생각하실 수 있습니다.
맞습니다. 당연히 위처럼 처리해야 합니다.
하지만 실제로 구성된 시스템을 안쪽까지 샅샅이 보면, 단순히 상품 A, B가 아닌 1+1상품, 증정품, 예약상품, 프로모션 상품 등 다양한 속성의 상품들이 섞여 있고 각각 처리해야 부분이 다르고 거기에 롤백까지 고려해야 했기에 위의 그림처럼 구현하는데 많은 고민과 삽질이 있었습니다.ㅠㅠ
다시 본론으로 돌아와서, 위의 (해결 방안) 그림과 같이 Emma와 Olivia가 상품을 동시 주문을 하게 되었을 때,
과거는 Transaction을 Item A, B 모두 처리해야만 Commit이 되는 구조에서 (Dead Lock이 되는 경우가 발생하기 때문에)
Emma와 Olivia의 Item A, B 어느 것을 먼저 선점하든지 각각 바로 Commit이 되도록 Transaction을 나누었고,
결과는!! 기존 발생하던 Dead Lock은 전혀 발생하지 않았습니다.
개선 확인 (Jmeter)
그렇다면, 정말 개선이 되었는지를 확인해 봐야겠죠. 저희는 순간 트래픽을 쉽게 줄 수 있는 Jmeter를 이용하여 진행하였고, 다양한 케이스로 Dead Lock을 재연해 보았습니다.
우선 기존 프로세스에서 어떻게 되는지를 확인해봐야, 우리가 고친 프로세스에서 정상 동작하는지 알 수 있기에... 테스트를 진행했고, 결과는 예상한 바와 같이 처참했습니다...
무수한 오류 카운트와(녹색으로 표기 된 그래프가 오류 건입니다.).. 올라가지 않는 속도.. 그리고 수없이 쌓이는 DB Lock 카운트... 실제로 Dead Lock으로 풀리지 않기도 했습니다.. DBA분께 SOS 요청해서 Dead Lock을 kill 하기까지... ㅠ.ㅠ그렇다면!! 수정한 프로세스에서의 결과는?!!
위의 그래프와 같이 한결같이 유지되는 속도와 발생하지 않는 에러카운트, 무엇보다! Dead Lock은 전혀 발생하지 않았습니다.부하 발생기를 여러 대로 늘려가면서 테스트했고, 많은 트래픽에도 문제가 되는 Dead Lock 부분이 해소됨을 확인하였습니다.
현재 상황
위와 같이 개선 및 검증한 프로세스를 실제 운영에 적용하였습니다.
적용 후 매번 트래픽이 높은 이벤트마다 모니터링을 진행했고, 기존보다 확실히 성능이 향상되었음을 실제 운영 트래픽 기준으로도 확인할 수 있었습니다.
아래는 이벤트 때 주문 처리에 대한 과거와 현재 비교의 그래프입니다.
개선 전에는 트래픽이 높아지면 처참하게도.. 주문 처리를 제대로 못 했으나, 현재는 높은 트래픽이 발생해도 안정적으로 주문을 처리하게 되었습니다.개선하기 위해 고생하신 Aaron님께 감사를 표하며,
앞으로도 계속해서 더 안정적이고, 빠르고, 정확하고 편리한 올리브영 주문/결제 시스템을 만들기 위해 변화 되는 내용은 저희 올리브영 테크 블로그를 통해 찾아 뵙겠습니다!