1. 프로그래머스 SQL 1문제
- 상품별 오프라인 매출 구하기
GROUP BY, SUM 사용 -> 집계함수에 익숙치 않음. 더 연습 필요
https://github.com/jthugg/websocket-chat
GitHub - jthugg/websocket-chat: 🚀 웹소켓을 이용한 아이디어 프로젝트 - 채팅편 2023.12 ~ 2024.4
🚀 웹소켓을 이용한 아이디어 프로젝트 - 채팅편 2023.12 ~ 2024.4. Contribute to jthugg/websocket-chat development by creating an account on GitHub.
github.com
- 동시성 처리의 필요성
- 채팅 방 참여정보는 일관된 데이터 정합성이 요구됩니다.
- 동시에 채팅 방 입/퇴장 요청이 몰렸을 경우 트랜잭션과 Lock을 기반한 동시성 처리가 용이합니다.
- 쓰기 성능의 확보
- 채팅 메시지는 읽기 작업 만큼 쓰기 작업 빈도가 많이 발생합니다.
- MySQL과 같은 RDBMS는 쓰기 작업에 데이터 정합성을 위해 Lock을 사용하고 디스크에 동기화 하는 과정에서 디스크 I/O가 발생합니다.
- MongoDB는 일반적인 쓰기작업에서 락을 사용하지 않고 메모리에 데이터를 적재해 I/O 작업을 최소화 할 수 있어 쓰기작업에서 소요시간을 줄일 수 있습니다.
- 빠르게 증식하는 데이터
- 채팅 메시지는 주기적인 삭제 전략이 없는 경우 시간이 흐름에 따라 빠르게 데이터 규모가 증가합니다. 데이터 규모 증가에 따른 수평확장이 용이해야 합니다.
- MongoDB는 수평확장(샤딩)이 용이합니다. 적재되는 데이터가 증가함에 따라 Auto Sharding이 가능하고 샤드 노드 추가 시 데이터의 리밸런싱이 필수적이지 않습니다.
- mongodb는 write에 강점 -> 왜 강점이 있는지, 그리고 기존에는 1:9 정도로 read / write가 발생했지만
채팅 메시지는 얼마의 비율로 진행되는지?
1. mongodb는 write에 강점
- Memory first 저장 == 메모리 우선 저장 + 비동기적으로 디스크에 flush (WiredTiger 엔진)
- Document 단위 쓰기 == join 없이 단일 문서 단위로 write -> rdb보다 lock 범위가 작고 빠름
- Lock granularity(락의 범위) == mongodb는 예전에는 collection-level lock이었지만 현재는 document-level lock(병렬 처리에 유리)
- Sharding(샤딩) == 수평 분산 저장이 쉬움 -> write 스케일 아웃이 수월(채팅 내역의 경우에는 갑자기 폭팔적인 대규모 트래픽 가능)
--- mongodb는 mongos라는 쿼리 라우터를 내장(트래픽 분산 + 모든 shard를 볼 필요가 없음)
또한 운영중에 수평 분산(샤딩) 적용 가능(내장하고 있어서). 하지만 rdb는 이걸 app layer에서 직접 구현 필요
shard key를 통해서 샤딩 가능하지만 한번 적용하면 수정 못하기 때문에 주의 필요
2. 일반 시스템의 read/write 비율 vs 채팅 시스템의 read/write 비율
- 일반적인 웹 서비스는 read:write 비율이 9:1 정도
- 채팅 서비스는 반대
채팅메시지는 실시간으로 지속적으로 발생(write)
write한걸 read하는 것도 많지만, 둘의 비율은 거의 비슷
read:write 비율은 5:5나 4:6
-> 특히나 단체 채팅방에서 대량 유저가 동시에 말하는 경우, 쓰기(write 부하가 훨씬 크게됨)
----- 샤딩과 파티셔닝
파티셔닝(수직, 수평) -> 하나의 db에 기존 하나의 tb로 유지되던 것을 서로 다른 tb로 분할해서 사용하는것. (수직은 col을 나누는것, 수평은 row를 나누는것)
샤딩 -> 동일한 스키마를 가지는 데이터를 여러개의 db에 분산하여 저장하는것. (수평 파티셔닝과 비슷한 방법이지만, 같은 서버인지, 다른 서버인지가 차이) -> 샤딩은 db 차원의 수평 확장
- 유저 정보는 rdb가 맞음.(왜 맞는지 이유도 적기)
1. 유저 정보는 왜 rdb?
rdb를 사용하는 이유
- 정합성: 이메일, 닉네임 등 유니크 제약조건 필요
- 트랜잭션 필요: 회원가입, 탈퇴시에 여러 연관 엔티티 처리
- 채팅 뿐만 아니라 다른 엔티티에서도 사용
- 데이터 변경이 잦고 관리 중심
-> 유저 데이터는 관계형으로 정규화 되어야지 유지보수와 확장에 유리하다.
- 채팅방 정보는 rdb, mongodb 중에 뭐가 맞는지? -> mongodb를 사용하는 경우 동시 요청이 오면 어떻게 처리되는지?
mongodb는 기본적으로 트랜잭션 적용 x -> 지원은 하지만 트랜잭션을 nosql에서 사용할 시 성능 문제가 엄청남.
1. 채팅방 정보는 rdb, mongodb 중에 뭐가 맞는지?
- 참가자 수 제한(mongodb는 배열크기 제한, rdb는 테이블 관계로 무제한)
- 동시 요청 처리(document-level lock -> 배열 삽입, 삭제시 충돌 위험 but, rdb는 트랜잭션과 락으로 안전)
- 일관성(mongodb는 application-layer에서 제약 필요. but, rdb는 db-level에서 consistency 보장)
mongodb도 트랜잭션 지원은 되지만
동시성 이슈에 약하고 느리다는 단점
극단적으로 채팅방에 1억명 동시 입장시에 document 크기 한계 + 병렬 삽입 가능으로 정합성 깨질 위험이 큼
(lock을 통해 얼마나 안정적으로 정합성, 동시성, 순서를 보장할 수 있느냐가 문제 -> db에서 처리를 못해주기 때문에 application-layer에서 따로 로직을 작성해야함. == 유지보수적인 문제 존재)
- 만약 rdb에 데이터를 저장할 경우 -> 새로운 사람이 들어왔을 때에는 어떻게 하는가?
1. "홍길동 님이 입장하였습니다."
2. rdb가 업데이트 됨.
3. 사람은 어떤 사람이 채팅방에 있을까 하고 설정창을 누름
4. 이때마다 http 요청이 날라가서 확인함.
5. 하지만 누를때마다 이 요청이 날라가게 되면 엄청난 트래픽이 있을 것을로 예상
-> 레디스 캐싱을 통해 or 레디스에 최신 데이터가 있다는 것을 저장하고 있을때만(roomid, new_participant == true) 이런 경우에만 새로운 요청 보내기
why? -> 기본적으로 mongodb의 업데이트는 느림. + 트랜잭션 보장이 잘 안됨. -> 만약 정원이 1500명인데 1499명인 방에 한번에 1억명이 입장 요청 -> 하나의 방에 1억명이 존재? == 에러
즉, 순서와 일관성이 요구된다고 생각하여 rdb를 사용하는게 옳다고 생각 -> 우리 프로젝트는 대규모 트래픽이 들어왔을 때를 가정하여 만드는 프로젝트이기 때문
RDB와 MongoDB를 분리하여 사용하는 이유 – 대규모 트래픽을 고려한 채팅 시스템 설계
프로젝트 목적
우리 프로젝트는 대규모 트래픽을 안정적으로 처리할 수 있는 채팅 시스템을 목표로 한다. 특히, 수만 개의 채팅방과 수많은 동시 접속자 환경을 가정하여, 성능·확장성·정합성·동시성을 고려한 데이터 저장 구조가 필요하다.
데이터베이스 분리 전략
| MongoDB | 채팅 메시지 (로그성 데이터) | 대량 쓰기 대응 + 수평 확장 |
| RDB (MySQL) | 유저 정보, 채팅방 정보, 채팅방 참가자 정보 등 | 정합성 보장 + 트랜잭션 + 제약 조건 처리 |
MongoDB를 메시지 저장소로 선택한 이유 (Write 성능 + 수평 확장성)
1. MongoDB는 Write에 강점
- Memory-First 저장: 메모리에 먼저 쓰고 비동기로 flush (디스크 I/O 최소화)
- Document 단위 쓰기: 단일 document만 수정하므로 Lock 범위가 작음 → 병렬 쓰기 처리에 유리
- Document-Level Lock: 하나의 메시지 insert는 다른 메시지 저장과 충돌하지 않음
- 샤딩 내장: roomId 기준으로 자동 분산 저장 및 라우팅 가능
- mongos 라우터가 해당 샤드로만 쿼리 전달 → 전체 조회 불필요
- Auto-Sharding 가능: 운영 중에도 샤딩 적용 가능 (Chunk Migration + Auto Balancing)
2. 채팅 시스템 특성상 Write 비중이 큼
- 일반 시스템은 Read:Write = 9:1
- 채팅 시스템은 Read:Write ≒ 5:5 또는 4:6
- 단체 채팅방의 폭발적 트래픽을 고려하면 쓰기 성능이 병목의 핵심 → MongoDB 적합
RDB를 유저/참여자/채팅방 정보 저장에 선택한 이유 (정합성 + 동시성 제어)
1. 동시성 처리와 정합성 보장
- 채팅방 입/퇴장 시 정원 제한, 중복 방지, 입장 로그 처리 등이 동시에 필요
- MongoDB는 배열 기반 참가자 관리 시, Document-Level Lock 충돌 위험 + 정합성은 앱에서 직접 관리해야 함
- RDB는 Row-Level Lock + 트랜잭션으로 충돌 시 rollback → 순서 보장 + 중복 방지 + 안정성 확보
2. 정합성 중심의 데이터는 관계형이 적합
- 유저 정보는 이메일, 닉네임 등 제약 조건 필요
- 유저 ↔ 채팅방 간 N:N 관계는 별도 테이블로 구조화되어야 유연하고 확장 가능
- 다양한 서비스 기능(알림 설정, 권한 제어 등)과 연결되므로 정규화된 구조 필요
채팅방 참여자 목록 조회 시 성능 최적화
- RDB에서 채팅방 참여자 목록을 조회해야 하지만, 설정창 진입 시마다 HTTP 요청이 날아가는 구조는 비효율적
- → Redis 캐시 도입
- 참가자 변경 발생 시 room:{roomId}:participantsStale=true로 마킹
- 설정 창 진입 시 stale=true면 RDB에서 조회 후 최신화, false면 캐시 데이터 사용
MongoDB로 모든 데이터를 저장하지 않는 이유
- MongoDB는 단일 document 처리에는 빠르지만,
- 정원 제한
- 중복 입장 방지
- 참가자 + 로그 동시 업데이트 등에서는
→ 복수 document 처리 불가, 트랜잭션 약함, 정합성 보장 어려움
특히 대규모 유저 동시 입장(예: 정원이 1500명인데 1억 명 입장 시도)의 극단 상황을 가정했을 때,
MongoDB는 document 크기 한계(16MB), 배열 충돌, 데이터 유실 위험이 있으며,
이러한 정합성과 순서를 안전하게 보장하는 데에는 RDB가 훨씬 유리하다.
결론
우리 프로젝트는 대규모 트래픽을 고려한 설계가 핵심이다.
따라서 채팅 메시지와 같이 로그성·쓰기 위주의 데이터는 MongoDB로 분산 처리하고,
정합성과 동시성 제어가 중요한 유저/채팅방/참여자 정보는 RDB로 안정성 있게 관리하는 구조를 채택했다.
이러한 분리 설계는 성능, 확장성, 정합성을 동시에 만족시키기 위한 최적의 전략이라 생각
'일기' 카테고리의 다른 글
| [개발일기] 09.16 - 자바 JVM은 어떻게 동작할까? (0) | 2025.09.16 |
|---|---|
| 6.9 - 오늘의 기록 (채팅 이해, 다양한 방법으로 구현) (0) | 2025.06.09 |
| 5.23 오늘의 기록 (2) | 2025.05.23 |
| 5.21 - 오늘의 일기 (채팅 고도화) (0) | 2025.05.21 |
| 5.20 - 오늘의 일기 (mongo db) (0) | 2025.05.20 |