Skip to content

백엔드 MySQL에서 MongoDB로 개선한 근거 및 Redis로 캐싱을 하지 않는 이유

fru1tworld edited this page Dec 3, 2024 · 5 revisions

개요

저희는 협업 프로그래밍을 위해 WebSocket 방식을 사용하였습니다.

이러한 WebSocket을 사용하기 위해 nest에서 지원하는 GateWay를 이용하여 구현하였습니다.

초기 로직은 다음과 같습니다.

V1

v1

SocketGateWay에서 Spcae와 Note에 따라서 분기가 나뉘고

Space Service와 Note Service는 각각 TypeORM을 통해 MySQL에 데이터를 읽고 씁니다.

그러나 Space와 Note같은 경우 따로 어떠한 관계를 맺고 있지 않고, 비정형화된 데이터라서 MySQL를 사용하는 방법말고 다른 방법이 있을까 고민을 하게 되었습니다.

MySQL에서 MongoDB로 변경하게된 이유

MongoDB는 비정형데이터를 다루는데 유리한 측면이 있습니다.

그렇다면 얼마나 유리할까요 ?

기준은 다음과 같습니다

허니플로우에서 사용되었던 임의의 데이터를 mock 데이터로 선정하고

표준으로 사용되는 typeORM, mongoose를 사용하여 각각 read, create를 1000번 반복하고 시간 차를 측정해보았습니다.

읽기를 천번 반복하는 로직에서 약 2배 정도의 차이를 나타냅니다.

읽기 성능 비교

readnote readspace

쓰기 성능 비교

쓰기에서는 약 6배 정도의 차이가 납니다.

writespace writenote

이러한 결과를 발생하는 원인은 MySQL과 MongoDB의 내부 동작 차이에 있습니다.

예를 들어 MongoDB의 경우 데이터를 한 번에 읽을 수 있어서 I/O 오버헤드가 적습니다.

쓰기 성능의 경우

MySQL의 경우 트랜잭션 관리 및 추가 연산이 필요한데

MongoDB의 경우 컬렉션에 삽입만 하면 된다는 점에서 스키마 유효성 검사 및 복잡한 연산이 필요하지 않고 데이터가 비정규화된 형태로 저장되어서 별도의 변환이 되지 않습니다.

이러한 차이로 읽기와 쓰기가 각각 2배, 6배의 결과를 보여주는 것 같습니다.

V2 v2

그렇게 mongoDB로 변경하면서 모듈 간 의존도를 낮췄습니다.

이제 SocketGateWay는 Collaborative Service 하나만 알고 있어도 동일한 로직을 수행할 수 있게 되었습니다.

이제 레디스를 통한 캐싱 적용을 위해서 V2의 실제 흐름을 파악해봐야할 필요가 있었습니다.

어디서 같은 작업이 반복되고 어디에 캐싱 적용을 하면 좋을지 알기 위함이었는데요

v2예상동작

저는 이러한 흐름으로 예상하고 있었습니다.

그러나 실제 통신은 다음과 같이 진행됩니다. v2실제동작

image

즉 하나의 웹소켓 쓰레드에 대해서 정상적인 요청으로 몇 명이 들어와도 bind와 write는 각각 한번만 호출됩니다.

실제 서버로그

[Nest] 1  - 12/03/2024, 3:33:00 AM   DEBUG [YjsGateway] bindPersistenceState: 시작 - docName: space:e1cac3b3-0445-41e8-9ce0-b4e2db09f70e, ydoc: {}
[Nest] 1  - 12/03/2024, 3:33:00 AM     LOG [SpaceService] ID가 e1cac3b3-0445-41e8-9ce0-b4e2db09f70e인 스페이스를 조회합니다.
[Nest] 1  - 12/03/2024, 3:33:00 AM     LOG [CollaborativeService] 스페이스 정보 조회 완료
[Nest] 1  - 12/03/2024, 3:33:00 AM     LOG [CollaborativeService] Object:
{
  "method": "findBySpace",
  "spaceId": "e1cac3b3-0445-41e8-9ce0-b4e2db09f70e",
  "found": true
}

[Nest] 1  - 12/03/2024, 3:33:00 AM   DEBUG [YjsGateway] 스페이스 데이터 분석 및 업데이트 준비 - id: e1cac3b3-0445-41e8-9ce0-b4e2db09f70e
[Nest] 1  - 12/03/2024, 3:33:00 AM   DEBUG [YjsGateway] 분석된 스페이스 데이터 - edges: {}, nodes: {"e1cac3b3-0445-41e8-9ce0-b4e2db09f70e":{"id":"e1cac3b3-0445-41e8-9ce0-b4e2db09f70e","x":0,"y":0,"type":"head","name":"테스트ABC","src":"e1cac3b3-0445-41e8-9ce0-b4e2db09f70e"}}
[Nest] 1  - 12/03/2024, 3:33:00 AM   DEBUG [YjsGateway] Yjs 스페이스 설정 시작
[Nest] 1  - 12/03/2024, 3:33:00 AM   DEBUG [YjsGateway] Yjs 스페이스 설정 완료
[Nest] 1  - 12/03/2024, 3:33:10 AM     LOG [YjsGateway] WebSocket 연결 시작
[Nest] 1  - 12/03/2024, 3:33:10 AM   DEBUG [YjsGateway] 요청된 URL: /ws/space/e1cac3b3-0445-41e8-9ce0-b4e2db09f70e
[Nest] 1  - 12/03/2024, 3:33:10 AM   DEBUG [YjsGateway] URL 분석 결과 - Type: space, ID: e1cac3b3-0445-41e8-9ce0-b4e2db09f70e
[Nest] 1  - 12/03/2024, 3:33:10 AM   DEBUG [YjsGateway] URL 유효성 검사 성공 - urlType: space, urlId: e1cac3b3-0445-41e8-9ce0-b4e2db09f70e
[Nest] 1  - 12/03/2024, 3:33:10 AM   DEBUG [YjsGateway] WebSocket 초기화 시작 - Type: space, ID: e1cac3b3-0445-41e8-9ce0-b4e2db09f70e
[Nest] 1  - 12/03/2024, 3:33:10 AM   DEBUG [YjsGateway] 스페이스 초기화 시작 - id: e1cac3b3-0445-41e8-9ce0-b4e2db09f70e
[Nest] 1  - 12/03/2024, 3:33:10 AM     LOG [CollaborativeService] 스페이스 정보 조회 시작
[Nest] 1  - 12/03/2024, 3:33:10 AM     LOG [CollaborativeService] Object:
{
  "method": "findBySpace",
  "spaceId": "e1cac3b3-0445-41e8-9ce0-b4e2db09f70e"
}

[Nest] 1  - 12/03/2024, 3:33:10 AM     LOG [SpaceService] ID가 e1cac3b3-0445-41e8-9ce0-b4e2db09f70e인 스페이스를 조회합니다.
[Nest] 1  - 12/03/2024, 3:33:10 AM     LOG [CollaborativeService] 스페이스 정보 조회 완료
[Nest] 1  - 12/03/2024, 3:33:10 AM     LOG [CollaborativeService] Object:
{
  "method": "findBySpace",
  "spaceId": "e1cac3b3-0445-41e8-9ce0-b4e2db09f70e",
  "found": true
}

[Nest] 1  - 12/03/2024, 3:33:10 AM   DEBUG [YjsGateway] 스페이스 존재 확인 - id: e1cac3b3-0445-41e8-9ce0-b4e2db09f70e
[Nest] 1  - 12/03/2024, 3:33:12 AM     LOG [SpaceController] 스페이스 경로 조회
[Nest] 1  - 12/03/2024, 3:33:12 AM     LOG [SpaceController] Object:
{
  "method": "getBreadcrumb",
  "id": "e1cac3b3-0445-41e8-9ce0-b4e2db09f70e"
}

[Nest] 1  - 12/03/2024, 3:33:12 AM     LOG [SpaceService] ID가 e1cac3b3-0445-41e8-9ce0-b4e2db09f70e인 스페이스의 경로를 조회합니다.
[Nest] 1  - 12/03/2024, 3:33:12 AM     LOG [SpaceController] 스페이스 경로 조회 완료
[Nest] 1  - 12/03/2024, 3:33:12 AM     LOG [SpaceController] Object:
{
  "method": "getBreadcrumb",
  "id": "e1cac3b3-0445-41e8-9ce0-b4e2db09f70e"
}

[Nest] 1  - 12/03/2024, 3:33:12 AM     LOG [YjsGateway] WebSocket 연결 시작
... 생략
[Nest] 1  - 12/03/2024, 3:33:14 AM     LOG [YjsGateway] WebSocket 연결 시작
... 생략
[Nest] 1  - 12/03/2024, 3:35:42 AM     LOG [YjsGateway] WebSocket 연결 시작

모든 커넥션을 종료했을 때 image

이러한 이유로 캐싱 처리에 대해서 다시 생각해보았고 레디스를 따로 도입하지 않기로 결정하였습니다.

Clone this wiki locally