병렬적으로 공부하기로 다짐하면서 꾸준히 관심 분야 현직자 분들의 글을 읽고 정리해나갈 예정입니다. 추천시스템 오픈 카카오톡방에서 추천한 글들을 위주로 먼저 읽겠습니다. 🎈 인용구 박스에 들어있는 내용은 추가 아이디어나 제 개인적인 생각을 열어보기 위해 기록한 부분입니다.
해당 글은 RIDI의 유지훈님이 2018년 08월 31일에 발표한 추천 API 최적화 하기라는 발표자료를 읽고 정리한 내용임을 밝힙니다. 발표자료를 보고 개인적으로 이해한 내용을 위주로 정리했기 때문에 발표 내용이 아닙니다. 틀린 내용이 있으면 댓글로 알려주세요 :)
리디북스의 추천 API
추천 알고리즘
리디북스의 추천 API는 2가지의 추천 방식을 취합니다. Book-to-book, Book-to-user 방식의 추천 알고리즘을 가진 API를 통해 책을 추천합니다.
먼저, Book-to-book 방식은 구매하려고 보고 있는 책을 구매한 고객들이 어떤 책들을 구매했는지 보여줍니다. 현재 사이트를 보고 있는 구매 예정 고객과 구매하려는 책과 이전 구매 고객이 구매했던 책간에 유사도를 계산합니다. 예를 들어, 살펴보면 아래와 같은 구매내역을 가지고 있다고 가정합니다.
- 구매자 A - 책a
- 구매자 B - 책a, 책b
- 구매자 C - 책b
책a와 책b간의 유사도를 계산하는 방식은 책a와 책b를 구매한 전체 고객들 중에 특정 고객이 책a나 책b를 구매할 확률을 구하는 방식으로 보입니다. (제대로 이해한 것이 맞는지 확인이 필요합니다..!)
Book-to-User 방식은 책들 사이의 유사도와 User Profile을 사용합니다. User Profile 같은 경우 구매와 최근 조회한 책들로 생성합니다. Profile과 유사도가 높은 책들을 추천하는 방식이 Book-to-User입니다.
🎈 해당 글 작성연도는 2018년도 입니다. 2021년 현재의 RIDI에서는 어떤 요소들로 User Profile을 생성할까?개인적인 생각이지만 구매 이외에도장바구니에 담았던 책들에 대해서도 User Profile에 들어갈 수 있지 않을까 생각됩니다. 온라인 도서몰을 이용할 때 주로 굳이 안 읽을 책도 마음에 들면 장바구니에 넣는 습관을 가진 저 같은 분도.. 있을 수 있으니까요!
최근 조회한 책에는 장바구니의 책들이 포함되네요..!
🎈 User Profile을 어떻게 정의하냐에 따라 추천시스템에 반영되는 고객의 성향을 선택할 수 있다는 생각이 들었습니다. 어떤 User Profile이 User의 특성을 대표하는지와 적절하게 추천할 수 있도록 하는지도 조사해봐야겠습니다.
추천 API 구조
API 구조 부분은 CS 지식이 부족해서 관련 지식을 보완 후 추가하겠습니다.
- 모르는 내용 정리
- Client - Server
- Load Balancing
- HTTP Request
- MySQL
- Elasticache(Redis)
- Aurora
- HAProxy
추천 API 최적화하기
안정적인 서빙을 위한 저장소 찾기
안정적인 서빙을 위해서 클러스터를 도입하여 시간 효율성을 높였다고 합니다. 이전에는 C++, Single machine batch, MariaDB에 업로드하는 방식을 취했다면 이후에는 Scala, Python, Multi-machine batch, MongoDB에 업로드하여 약 10시간에서 약 2시간의 시간 효율성을 높였습니다.
여기서 어제 공부했던 개념을 다시 볼 수 있었습니다. (뿌듯..!) SQL에서 NoSQL인 MongoDB가 가지는 장점이 유연성, 속도, 그리고 수평확장으로 잦은 읽기처리에 강하다는 점들이 있습니다. 발표에서도 Bulk write와 real-time read를 언급하기 때문에 리디북스의 API 구조 상에서도 막대한 양의 데이터와 읽기 처리를 자주 하기 때문에 SQL에서 NoSQL로 바뀐 것으로 보입니다.
그러나, 리디북스에서는 MongoDB를 사용하면서 Bulk Load를 할 때 load average 경고가 자주 발생했습니다. 그래서 Bulk load를 할 때만 프로세스의 수를 줄여서 실행했습니다. 해당 이슈는 계속 발생해서 ElasticCache + Redis, DynamoDB 등 다른 DB를 활용했지만 비용 문제로 다시 MongoDB를 사용했습니다. 이후 Bulk Load를 할 때마다 새 instance로 교체하는 방식을 취했다고 합니다.
- SQL vs. NoSQL - https://siyoon210.tistory.com/130
추천 제외 데이터베이스 분리하기
리디북스에서는 추천 제외라는 버튼을 통해 고객으로부터 추천 제외 리스트를 받아올 수 있습니다. 추천 목록을 조회할 때에는 HBase Cluster로부터 추천 목록을 가져오고, 추천 제외 목록과 각종 통계를 Mater DB에서 가져와 추천 API에서 추천 제외 책들을 제거한 후에, 최종적인 추천 목록을 Web Server에 전달합니다.
기존 통계 테이블이 있는 Master DB에 추천 제외 테이블을 등록할 때 Single Point of Failure(SPoF) 문제가 발생했습니다. 데이터센터 전원 장애로 데이터팀 Master DB가 다운되어 서버 복구시까지 추천 제외 기능을 사용 못 하거나, DB 서버 메모리 증설로 서버 재부팅시 추천 제외 기능을 사용 못 하는 문제점이 발생했습니다.
추천제외 테이블(24시간 내내 INSERT, 대고객 서비스와 밀접)과 통계 테이블(새벽 배치 실행시에만 INSERT, 회사 내 서비스에만 관련)은 하나의 Master DB에 있지만 성격이 달랐기 때문에 두 개의 테이블을 분리해주는 작업이 필요했습니다. 추천 제외 테이블을 Aurora로 분리하여 확장성, 안정성, 편리함 등을 가져갈 수 있었습니다. Aurora로 분리 후 Master가 아닌 Slave에서 읽도록 변경하였고 덕분에 기존 DB 서버는 언제든 재부팅 가능하고 DB 운영의 안정성도 증가시킬 수 있었습니다.
- 모르는 내용 정리
- DB들 간의 Master - Slave 구조 어떤 것인지
- Master가 2개(Store, data team)끼리의 Replication과 Master와 Slave간의 Replication이 다른 것인지
- HBase Cluster가 무엇인지
- 왜 추천 목록은 HBase Cluster로 가져오고 추천제외 목록은 DB(Master - Slave)로부터 가져오는지
- Single Point of Failure가 어떤 것인지
- Aurora
API 응답시간 줄이기
응답시간이 3초가 초과되는 추천 API 요청이 발견되었고 약 4만건으로 전체 요청의 0.1359%를 차지했습니다. 특정 조회 쿼리에서 데이터가 많은 table을 조회하게 되면서 생긴 문제점이었습니다. 조회 쿼리가 있는 부분은 Batch에서 제외하여 조회 프로파일, 조회기반 추천을 따로 API측에서 진행하고 나머지를 Batch 상에서 미리 계산하도록 진행한 후 AI 추천이 이뤄지도록 변경했습니다. 그 결과 1/8 정도 응답시간을 줄일 수 있었다고 합니다.
출처 - https://ridicorp.com/story/optimizing-suggest-api/