본문 바로가기
DB

채팅 영구저장 - RDB vs NoSQL

by 오늘도 깨달았다 2022. 12. 29.
반응형

서론

현재 서비스에서 채팅을 저장할 때 In-memory Database이지만 영구저장이 가능한 Redis를 사용해서 저장을 하고있었다. 하지만 Redis는 데이터를 모두 메모리에 올리기 때문에 메모리의 크기보다 데이터가 많아지면 샤딩을 하거나, 다른 방법으로 scale up & out을 해줘야 한다.

 

서비스를 초기에 개발할 땐 메모리가 다 찰 정도의 채팅이면 웬만한 서비스 크기가 아니라면 곧바로 필요하지는 않다고 판단했고, Redis의 AOF와 RDB(snapshot) 를 같이 사용하여 데이터가 사라지지 않게 했었다.

 

현재는 서비스가 안정화 된 상태이고, 최근 며칠의 채팅 데이터만 캐싱해놓고 그 이전의 채팅을 영구저장하기위해 이 글을 포스트한다. 

요구사항

채팅 데이터가 증식할 때 서비스에 문제가 없도록 하기 위해 다음과 같은 요구사항을 정의했다.

 

1. redis에는 sorted set이라는 정렬을 할 수 있는 자료구조를 사용해서 채팅을 저장했기 때문에 시간이 지난 채팅 데이터들을 저장할 DB도 시간순으로 정렬해서 쿼리를 할 수 있어야 한다.

 

2. 채팅은 실시간으로 하기 때문에 3-5일 이내의 채팅 데이터는 그대로 redis에 캐시하고, 그 이후의 채팅 데이터는 디스크에 저장하고 필요하다면 불러와야한다.

 

3. 빠르게 증식할 위험이 있는 데이터이기 때문에 확장성이 좋아야한다.


 RDB or NoSQL

채팅 데이터를 영구저장할 때 사용할 DB를 선정하기 위해 다시한번 RDB와 NoSQL을 비교했다. 

RDB , Nosql 큰 틀에서의 차이점

위 표는 RDB와 Nosql의 대표라고 할 순 없지만 많이 사용하는 DB들의 차이점을 기술해놓은 것이다.

 

표에서 중요한 내용 정리

RDB

 

1. 구조화된 데이터를 저장하는데 큰 장점을 보이고, 트랜잭션에 엄격하기 때문에 트랜잭션 데이터를 저장하는데 적합하다.

 

2. 수평적 확장(더 많은 수의 서버 운용)보다는 수직적 확장(서버의 성능을 올리는 것)이 데이터의 정합성이나 관리 측면에서 훨씬 안전하다. 물론 샤딩이나, Master-slave 등 다양한 방식으로 수평적 확장을 할 수 있지만 관리가 쉽지 않다. 

 

3. 데이터의 형태가 지정되어있다. 즉 형태가 자주 변하지 않는 데이터를 저장하는데 장점이 있다. 

 

NoSQL

 

1. 메타데이터의 추가가 RDB에 비해 비교적 자유롭고, 스키마를 반드시 정의하지 않아도 된다. 

 

2. 수평적확장이 RDB에 비해 용이하기 때문에 RDB보다 확장성이 좋다고 할 수 있다.

 

3. 데이터마다 관계가 없기 때문에 트랜잭션 처리가 안된다. 따라서 트랜잭션처리가 꼭 필요하지 않은 데이터들에 더 적합하다. 


정리

RDB는 확장성이 좋진 않지만 데이터들의 관계를 정의함으로써 트랜잭션 처리나 구조화된 데이터를 처리하는데 장점이 있고 여러가지 데이터를 조합해야하는 쿼리가 있는 경우에 좋고 NoSQL은 데이터간의 관계를 정의하지 않기 때문에 Join이나 트랜잭션 처리는 할 수 없다. 하지만 데이터가 특정 형식에 종속되지 않아 스키마가 유연하고 수평적 확장에 RDB보다 훨씬 용이하다는 장점이 있어 대량의 데이터를 저장할 때 유리하다.

 


RDB와 NoSQL의 대표적인 차이점은 이러하고 어떤 데이터베이스를 쓰던 장단점이 있기 때문에 우리 서비스에서 필요한 요구사항과 접목시켰다. 

 

 

결론

채팅 데이터는 서비스가 커질수록 빠르게 증식하기 때문에 대량의 데이터가 될 가능성이 커서 확장성이 좋아야한다. 또한 우리 서비스에서는 채팅에서 1:1 화상통화, 거래 등 많은 기능이 들어가있는데 추후에 채팅 안에서 기능이 추가될 가능성이 높다. 기능이 추가된다면 데이터의 형태를 변경하고, 추가해야하기 때문에 대량의 데이터를 저장하는데 장점이 있고, 스키마의 형태가 지정되지 않은 NoSQL이 더 좋을 것 같다. 채팅 데이터가 한번 저장되면 변하지 않는다는 것도 한 몫 했다.

하지만 오래된 채팅이라도 채팅을 한 시간순으로 데이터를 "페이징하여" 불러오는게 가장 중요한 부분이라서 offset 기능을 제공하는 NoSQL이 있고, 성능상 비효율적이지 않다면 NoSQL 로 선정하고 성능을 비교했을 때 시간순 정렬이 감당할 수 없을 정도로 비효율적이라면 확장성이 아쉽고 비용이 좀 더 들더라도 RDB를 사용하기로 결정했다. 

 

 


 

 

NoSQL 데이터 저장 형식 

- Key Value DB
키와 값으로 이루어진, 저장과 조회라는 가장 간단한 원칙에 충실한 데이터베이스.
조금 더 넓은 의미로 Key-Value Database를 사용하는 경우 Column Family Database들도 포함하여 이야기하는 경우도 많다. 본문에서는 조금 더 세분화해서 그 둘을 구분해서 분류하였다.

  • Key-Value Database의 Key 값은 unique한 고유값으로 유지되어야 한다.
  • 테이블간 조인을 고려하지 않으므로 RDB(Relational Database)에서 관리하는 외부키나, 컬럼별 constraints등이 필요 없다.
  • 값에 모든 데이터 타입을 허용하며, 그래서 개발자들이 데이터 입력 단계에서 검증 로직을 제대로 구현하는 것이 중요하다.

Key-Value Database는, 간단한 데이터 모델을 대상으로 데이터를 자주 읽고 쓰는 애플리케이션에 적합하다. 값은 boolean이나 integer값처럼 단순한 스칼라 값이 일반적이지만, 리스트나 JSON같은 구조화된 값도 가능하다.

 

데이터베이스 예시

- Redis
- Riak
- Oracle Berkely
- AWS DynamoDB


- Wide Columnar Store
컬럼 패밀리 데이터베이스는 대용량 데이터, 읽기와 쓰기 성능, 고가용성을 위해 설계되었다. 구글에서 Big Table을 도입하고, 페이스북은 Cassandra를 개발했다.

Column과 Row와 같이 Relation Database와 동일한 용어를 사용하여 스키마를 정의한다.
컬럼 수가 많다면 관련된 컬럼들을 컬렉션으로 묶을 수 있다. 예를 들어 이름의 성과 이름을 하나로 묶고, 사무실, 핸드폰 등의 전화번호들을 하나로 묶을 수 있다. 이렇게 묶인 컬럼들을 Column Family라고 한다.
Document Database와 마찬가지로 미리 정의된 스키마를 사용하지 않으므로 개발자가 데이터를 입력하는 시점에 원하는대로 컬럼을 추가할 수 있다.

테이블간 조인을 지원하지 않는다.
관계형 데이터베이스에서 한 객체에 대한 정보를 저장하기 위해 여러 테이블에 나누어 저장한다. 예를 들면 고객의 기본 정보 테이블(이름, 주소, 연락처)와 고객의 주문 정보 테이블 등을 나누어서 저장하고 조인을 통해 이객체의 정보를 활용한다.
컬럼패밀리 데티어베이스는 일반적으로 비정규화 되어 있으며 한 객체에 관련된 모든 정보를 가능한 매우 너비가 넓은 단일 Row에 넣어서 보관한다. 따라서 한 Row에 수백만개의 컬럼을 보관하는 경우도 비정상적인 것은 아니다.

 

데이터베이스 예시

- Hbase
- Cassandra
- GCP BigTable
- Microsoft Azure Cosmos DB

- ScyllaDB

 


- Document DB
Document Database 또는 Document-Oriented Database는 위의 Key-Value Database와 같이 데이터 저장에 Key-Value Type를 사용한다.

  • 하지만 Key-Value Database와의 중요한 차이는 Document Database는 값을 문서로 저장한다는 점이다. 여기서 문서란 semi-structured entity이며 보통 JSON이나 XML 같은 표준 형식 말한다.
  • 값을 저장하기 전에 schema를 별도로 정의하지 않으며, 문서를 추가하면 그게 바로 schema가 된다.
  • 각 문서별로 다른 필드를 가질 수 있으며, 따라서 개발자가 애플리케이션에서 데이터를 입력하는 단계에서 컬럼과 필드의 관리가 제대로 이루어지도록 보장하는 것이 매우 중요하다.
    예를 들어 필수 속성(Null을 허용하지 않는 속성)에 대한 관리도 애플리케이션 레벨에서 관리가 이루어져야 한다.

데이터베이스 예시

- MongoDB
- CouchDB
- Couchbase


- Graph DB
Euler & Graph Theory에서 유래한 DB다. Nodes, Relationship, Key-Value 데이터 모델을 채용하고 있다. Neo4J, OreientDB 등의 제품이 있다.

 

 


채팅 데이터에 채팅 뿐만이 아닌 채팅방에서 할 수 있는 1:1 화상통화, 거래 등의 기능을 채팅과 함께 제공해야하기 때문에 단순 key, value 저장은 활용이 불가능할 것 같다고 판단했고, graph db는 각 관계성을 파악해서 빈도나 인사이트를 얻어낼 때 더 적합하기 때문에 배제했다. 

 

Document vs Wide-Column

 

우리 서비스의 채팅 데이터들은 document 방식이나 wide-column 방식 두가지 모두 저장할 방법이 있었다. 

그래서 구조적인 차이점에 집중했다. 

 

document DB의 데이터는 json 형태의 문서로 관리된다. 

//Airbnb 데이터 저장 예시
{
"_id": "10006546",
"listing_url": "https://www.airbnb.com/rooms/10006546",
"name": "Ribeira Charming Duplex",
"summary": "Fantastic duplex apartment with three bedrooms, located in the historic area of Porto, Ribeira (Cube)...",
"house_rules": "Make the house your home...",
"property_type": "House",
"calendar_last_scraped": {
    "$date": {
       "$numberLong": "1550293200000"
        }
    },
"amenities": [
    "TV",
    "Cable TV",
    "Wifi",
    "Kitchen",
    "Paid parking off premises",
    "Smoking allowed",
    "Microwave"
    ]
}

 

중첩구조로 구성할 수 있다는게 특징이며 document 내에 field(key , value)를 정의한다.

key에 대한 값들은 document가 될 수도 있고, 배열이 될 수도 있다. 

 

Master/slave , master-master 구조로 Scaleout 하며 샤딩 또한 가능하다. DB에 따라 자동장애 극복도 지원한다.

 

 

wide-column store는 위에서 설명한 내용과 같이 일반적으로 비정규화해서 데이터를 저장하며 한 객체에 관련된 모든 정보를 가능한 매우 너비가 넓은 단일 Row에 넣어서 보관한다. 

wide-column store는 RDB와 같이 테이블 , 열, 행 을 사용하지만 열 형식 및 이름은 동일한 테이블 내에서 행마다 다를 수 있다. 그리고 각 열은 디스크에 별도로 저장된다.

ex)

RDB는 행 기반 스토리지가 기본적인데  wide-column store는 자동으로 수직분할하며 스토리지 자체가 열 기반이다. 

ex) RDB 는 행 기반이라 테이블에 5개의 컬럼이 있어서 2개는 null이어도 해당 값이 존재하지만 , wide-column store는 열 기반 스토리지라 열에 값이 없다면 값이 존재하지 않게 되는 것이다. 밑의 그림 참고

wide-column store

다음은 wide-column store의 트랜드 차트이다. (db-engines.com)

 

cassandra가 가장 많이 사용되고 있었고 그 다음으로 Hbase , cosmos 등이 뒤를 이었다. 

 

지금까지 document와 wide-column store의 특징을 봤다.

 

이제 DB들의 확장성, 가용성, 일관성은 어떤지 궁금했다.

 

CAP 이론,PACELC 이론 등을 참고했다. 

자세한 내용은 밑에서 참고한 글을 보면 좋을 것 같다.

 

간단하게 CAP 이론은 DB들의 특성에 따라 가용성을 만족하는지, 확장성을 만족하는지 분류했지만 현실과 약간 동떨어져 있다는 평을 많이 받았고 Latency, 정상상태일 때 등을 추가하여 나온 이론이 PACELC 이론이다. 

 

 

document db는 대부분 master-slave 구조로 db가 확장됐을 때 보통 master에 데이터를 삽입한 순간 아주 잠시 slave들의 read를 blocking 하고 데이터를 복제 한 후 read를 수행하기 때문에 일관성이 좋다고 할 수 있다. 

wide-column store는 자주 쓰이는 cassandra는 ring의 형태로 db를 분산, 확장하기 때문에 master가 없다. 즉 데이터 분산이나 복구를 관장하는 서버가 따로 없다. 그렇기 때문에 어떤 노드에서든 데이터를 삽입하게 되면 ring으로 연결되어있는 동등한 node들에 데이터를 넣게된다. blocking 또한 없다. 그렇기 때문에 ring으로 연결된 노드들이 데이터를 받기 전에 select 하는 경우 가장 최신의 데이터를 받지 못할 수 있다는 단점이 있다.

hbase또한 wide-column store로 자주쓰이지만 master-slave 구조를 가지고 있기 때문에 가용성에서의 위험이 더 컸다. 

 

우리 서비스의 채팅과 매칭시켜 생각을 했을 때 3-5일 정도의 데이터를 redis에 캐싱하고 그 이전의 데이터를 저장해야 했기 때문에, 실시간으로 분산된 DB에 데이터가 일관성있게 있을 필요는 없고 어차피 추후에 데이터 저장이 완료되고 나서 저장된 DB에서 데이터를 추출할 것이기 때문에 일관성보다는 가용성이 좋은 cassandra를 선택했다. 하지만 cassandra는 시간순으로 정렬해서 쿼리를 할 수가 없었다. 그래서 cassandra의 속도나, 여러가지 아쉬운점들을 개선하고 , 시간순으로 정렬을 해서 쿼리할 수 있고, offset 기능도 지원하는 ScyllaDB를 선택했다.


 

 

 

 

참고 사이트 

http://happinessoncode.com/2017/07/29/cap-theorem-and-pacelc-theorem/

https://nicewoong.github.io/development/2018/02/11/cassandra-feature/

 

반응형

댓글