-
매장 조회수 적용 api 성능 개선기(promise.all) 성능 4배 올리기Project/NYAM 2023. 3. 3. 01:48
사용 기술 : Nest.js - mongoDB
현제 서비스 중인 어플리케이션은 사용자의 행동들에 따른 log 데이터를 생성하여 특정 형식으로 mongodb 에 저장을 해둡니다.
가게 검색시 가게 조회수별 정렬을 위하여 다음과 같이 store_view 를 추가했습니다.
가게 데이터입니다.
추가로 store_total_view 도 추가를 같이 하였습니다.
서비스가 커지는 것을 예상을 하여 ,항상 가게들의 모든 조회수를 계산하여 데이터를 업데이트 하는 방식은 서버와 디비에 부담이 될것이므로, 스캐줄러를 이용하여 모든 조회수를 store_total_view 에 저장을 하고 , 특정 기간별 (예 최근 1주일 ) 조회는 store_view 에 넣는 방식으로 구성을 했습니다.
사용자의 모바일 기록 Log 데이터는 다음과 같습니다.
위의 두 데이터의 대한 기존의 코드입니다.
async store_set_view(query: StoreSetViewQuery) { const stores = await this.storeModel.find(query.query()); const logItems = await this.logsService.getActionLogListToSetView(); const storeCount = new Map<string, number>(); logItems.forEach((logItem) => { const storeId = logItem.table_id.toString(); if (!storeCount.has(storeId)) { storeCount.set(storeId, 1); } else { storeCount.set(storeId, storeCount.get(storeId) + 1); } }); for (let i = 0; i < stores.length; i++) { const storeId = stores[i]._id.toString(); const count = storeCount.get(storeId) ?? 0; stores[i].store_view = count; await stores[i].save(); } return true; }
async getActionLogListToSetView() { const actionLogsQuery = {}; actionLogsQuery['action'] = 'Read'; actionLogsQuery['table_name'] = 'store'; const logItems = await this.actionModel.find(actionLogsQuery); return logItems; }
코드 설명 :
로그데이터에서 action 이 Read 인것과 가게 테이블을 전부 불러와 map 에 갯수 와 같이 저장하고
store list 를 불러와 id 값을 비교하여 store_view 를 갱신해주는 코드입니다.
기존의 코드로 store-view 를 갱신해 봅니다.
처음 구동시 4초가 걸립니다.답답하죠 현제 가게 데이터는 약 360 개 로그 데이터는 약 2000 개 정도 입니다.
서비스가 커짐에 따라 사용자 로그 데이터가 급격한 속도로 늘어날것을 예상하여 리펙토링을 해줍니다.
promise.all 을 사용한 코드입니다.
async store_set_view(query: StoreSetViewQuery) { const stores = await this.storeModel.find(query.query()); const logItems = await this.logsService.getActionLogListToSetView(); const storeCount = new Map(); logItems.forEach(({ table_id }) => { storeCount.set(table_id.toString(), (storeCount.get(table_id) || 0) + 1); }); await Promise.all( stores.map(async (store) => { const storeId = store._id.toString(); const count = storeCount.get(storeId) || 0; store.store_view = count; await store.save(); }), ); return true; }
다음과 같이 코드를 변경하니, promise.all 은 모든 비동기 함수가 완료될때까지 기다린 다음 결과를 반환합니다.
각 요소들을 동시에 처리 하였고 for each 로 가독성을 높였습니다.
처음 구동시 1225ms 이며 이후에는 평균 400ms 가 나옵니다. promise.all 을 사용하여 모든 저장작업을 동시에 실행하게 하였고,
네트워크 지연 시간과 입/출력 작업에 대한 대기 시간을 최소화 하였습니다.
'Project > NYAM' 카테고리의 다른 글
거기는 업무 관리를 어떻게 하나요? (0) 2023.06.19 S3 버킷 정책과 api 를 이용한 이미지 다운로드 (1) 2023.03.03 매장 리뷰수 계산 api 성능 문제 (0) 2023.02.28 "아니 디도스 공격을 받았다고요 ???" (0) 2023.02.25 몽고디비 필드값 다중 검색 $or 사용 및 향상 (1) 2023.02.24