-
Notifications
You must be signed in to change notification settings - Fork 3
백엔드 MySQL에서 MongoDB로 개선한 근거 및 Redis로 캐싱을 하지 않는 이유
저희는 협업 프로그래밍을 위해 WebSocket 방식을 사용하였습니다.
이러한 WebSocket을 사용하기 위해 nest에서 지원하는 GateWay를 이용하여 구현하였습니다.
초기 로직은 다음과 같습니다.
SocketGateWay에서 Spcae와 Note에 따라서 분기가 나뉘고
Space Service와 Note Service는 각각 TypeORM을 통해 MySQL에 데이터를 읽고 씁니다.
그러나 Space와 Note같은 경우 따로 어떠한 관계를 맺고 있지 않고, 비정형화된 데이터라서 MySQL를 사용하는 방법말고 다른 방법이 있을까 고민을 하게 되었습니다.
MongoDB는 비정형데이터를 다루는데 유리한 측면이 있습니다.
그렇다면 얼마나 유리할까요 ?
기준은 다음과 같습니다
허니플로우에서 사용되었던 임의의 데이터를 mock 데이터로 선정하고
표준으로 사용되는 typeORM, mongoose를 사용하여 각각 read, create를 1000번 반복하고 시간 차를 측정해보았습니다.
읽기를 천번 반복하는 로직에서 약 2배 정도의 차이를 나타냅니다.
쓰기에서는 약 6배 정도의 차이가 납니다.
이러한 결과를 발생하는 원인은 MySQL과 MongoDB의 내부 동작 차이에 있습니다.
예를 들어 MongoDB의 경우 데이터를 한 번에 읽을 수 있어서 I/O 오버헤드가 적습니다.
MySQL의 경우 트랜잭션 관리 및 추가 연산이 필요한데
MongoDB의 경우 컬렉션에 삽입만 하면 된다는 점에서 스키마 유효성 검사 및 복잡한 연산이 필요하지 않고 데이터가 비정규화된 형태로 저장되어서 별도의 변환이 되지 않습니다.
이러한 차이로 읽기와 쓰기가 각각 2배, 6배의 결과를 보여주는 것 같습니다.
V2
그렇게 mongoDB로 변경하면서 모듈 간 의존도를 낮췄습니다.
이제 SocketGateWay는 Collaborative Service 하나만 알고 있어도 동일한 로직을 수행할 수 있게 되었습니다.
이제 레디스를 통한 캐싱 적용을 위해서 V2의 실제 흐름을 파악해봐야할 필요가 있었습니다.
어디서 같은 작업이 반복되고 어디에 캐싱 적용을 하면 좋을지 알기 위함이었는데요
저는 이러한 흐름으로 예상하고 있었습니다.
그러나 실제 통신은 다음과 같이 진행됩니다.
즉 하나의 웹소켓 쓰레드에 대해서 정상적인 요청으로 몇 명이 들어와도 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 연결 시작
모든 커넥션을 종료했을 때
이러한 이유로 캐싱 처리에 대해서 다시 생각해보았고 레디스를 따로 도입하지 않기로 결정하였습니다.
- 🎨 Canvas 라이브러리, 비교와 고민
- 💫 CSS, JS 없이도 SVG 모핑을 구현할 수 있다니
- 💥 CRDT를 프로젝트에 적용하기 - Yjs 데이터타입 정의
- 🐳 CI-CD-개선-경험-및-Docker‐compose-활용
- 🔬 FPS 테스트로 성능 최적화 고민 해결하기
- 👩🚀 Konva.js로 스페이스 줌 기능 구현하기
- 🤔 Palette메뉴 육각형 구현체에 대한 간단한 고민
- 🧑💻 React에서 Y.js를 사용하기
- 🤜 React에서 Dialog를 매끄럽게 관리하기
- 🐝 WebRTC, WebSocket, SocketIO 기술 선정의 근거와 이유
- 🥛 동시편집 마크다운 에디터 구현기
- 📱 모바일 환경을 위한 노드.간선 조작 기능을 지원해보자
- 🧱 몹프로그래밍으로 설계한 기본 컴포넌트
- 💨 백엔드 MySQL에서 MongoDB로 개선한 근거 및 Redis로 캐싱을 하지 않는 이유
- 🪄 우여곡절 재탄생한 Gooey 인터랙션
- ✨ 인터랙션 구현기: 홀드, 그리고 이동