Daylogs/DB

효과적인 DynamoDB 디자인 및 활용

ohgyun 2021. 2. 18. 15:55

발생일: 2020.02.18

키워드: nosql, dynamodb, 다이나모디비

문제:

이번엔 디비를 NoSQL로 AWS의 DynamoDB를 사용해서 구성해보려고 한다.

 

아키텍처 디자인을 어떻게 해야할까 고민하던 차에, A가 리인벤트 동영상을 추천해줬는데 내용이 좋다.

 

youtu.be/8rEsuvdL17s

 

아래는 보면서 정리해둔 내용.

 

해결책:

 

- 한 애플리케이션에 한 개의 테이블


- 파티션 키는 데이터를 골고루 분산하는 용도로 (데이터 분배 결정)
    - 고유 값이 많은 속성(카디널리티가 높은 속성)
    - 균일한 비율로 무작위로 요청되는 속성


- 정렬 키는 기존 RDS에서 인덱스를 사용하는 느낌으로 (쿼리, 다양화)
    - 1:n, m:n 관계 모델링에 활용
    - 효율적/선택적 조회
    - 범위 조회


- GSI (Global Secondary Index)
    - 별도의 속성으로 파티션과 정렬키 설정 가능
    - 인덱스 크기 제한 없고, 기존 테이블 대상으로 추가/삭제 가능
    - Eventual consistent read 만 가능
    - 테이블 당 5개까지 가능
    - 테이블과 별도의 읽기/쓰기 용량(RCU/WCU) 할당
    - 즉, 테이블 capacity는 충분한데 GSI capacity 가 부족하면 병목 발생


- LSI (Local Secondary Index)
    - 테이블과 동일한 파티션키. 파티션 내에서 정렬
    - 인덱스는 10기가 단위로 데이터와 함께 저장
    - 테이블을 생성할 때만 설정할 수 있음 (추가/삭제 불가)
    - 테이블에 할당된 RCU/WCU 사용
    - Eventual + Strong consistent 선택 가능
    - 테이블 당 5개까지 가능


- 읽기 용량 단위
    - 1 RCU = 초당 4KB 처리 단위
        - Eventual consistent = 2회
        - Strong consistent = 1회
    - 4KB로 올림 처리
        - 예: item 크기가 7KB 면 8KB로 올림
    - 즉, 가능하면 한 번에 많이 읽어와야 함


- 쓰기 용량 단위
    - 1 WCU = 초당 1KB 처리
    - 1KB 단위로 올림 처리
        - 예: 100B 를 써도 1KB로 처리
    - 즉, 가능하면 한 번에 많이 써야 함


- 조회 (Get, Query)
    - eventual, strong consistent 선택 가능
    - 1MB 단위로 응답
        - 결과량이 1MB를 넘을 땐 LastEvaluatedKey 를 ExclusiveStartKey 로 활용
    - PK 조회 (동일한 값 조회)
        - 예: PK = 1
    - SK 조회 (범위 값 조회)
        - 예: SK in 10~20, SK beginwith 'foo.'
    - LSI 조회 (테이블 용량 소진)
        - 예: PK = 1, LSI_SK = foo@bar
    - GSI 조회 (별도 용량)
        - GSI_PK: name = 'foo', GSI_SK: age > 20


- 조회 (Scan)
    - 전체 테이블 필터 적용해서 선택적 조회
    - 병렬 스캔 가능하지만 RCU 유의해야 함


- 모델링 (집계 처리의 예) 

    - 예를 들어, 음원의 상세 정보와 월별 다운로드 수를 함께 집계한다면,
    - 음원 정보 아이템
        - { PK: 'song-129', SK: 'Details', Title, Artist, ... }
    - 음원 집계 아이템
        - { PK: 'song-129', SK: 'Month-2020-02', Month: '2020-02', Downloads: 482320 }
    - 여기서 가장 많이 다운로드 된 음원을 찾으려고 한다면, GSI 설정
        - GSI_PK: Month = '2020-02', ScanIndexForwarded = False, Limit = 1 로 조회


- 모델링 (키를 다양하게 조합하고 싶은 경우)
    - PK = 아이디, SK = 집계를 원하는 공통 이름으로 설정 (타입), 속성은 타입에 따라 다름
    - 예: 
        - { PK: 'emp-100', SK: 'master', 이름, 기타 기본 정보}
        - { PK: 'emp-100', SK: 'state:CA', 이름, 캘리포니아 근속 정보 }
        - { PK: 'emp-100, SK: 'state:NY', 이름, 뉴욕 근속 정보 }
        - { PK: 'emp-100', SK: 'prevtitle:A', 이름, 이전 타이틀 }
        - { PK: 'emp-100', SK: currtitle:B, 이름, 현재 타이틀 }
        - SK + 이름 조합을 GSI로 설정


- 모델링 (결합 정렬키)
    - 계층 관계를 적용해서 조회하고 싶다면, 정렬키 이름을 계층으로 설정
    - 예:  SK: 'foo#bar#baz'


- 쓰기 샤딩
    - 예를 들어, 한 디바이스에 요청이 몰릴 때
    - 디바이스가 쓸 때, 샤드 개수를 서픽스로 붙여서 PK로 할당
    - 디바이스 - 샤드 개수를 기록


- 비용
    - 쓰기가 가장 비싼 작업
    - TTL 을 활용한 삭제와 테이블 삭제는 무료
    - 모든 데이터를 보관하려고 하지 말고, 장기간 보관이나 큰 데이터는 S3를 활용
    - eventually consistent read 는 50% 저렴. 앱 단에서 잘 설계해서 활용하면 좋음
    - 프리티어의 유효기간이 없음
        - 25GB 스토리지 + 25 WCU, 25 RCU
        - 월 2억 건 정도의 요청량임
    - 규모가 커졌을 땐 예약 용량을 활용해서 절약하는 방법 있음


- 필요한 데이터만 읽기
    - GSI는 Sparse Index 임 (즉, 데이터가 있는 것만 인덱스를 만듦)
    - 아이템에 타입에 따라 필요한 정보를 넣은 후, GSI를 활용


- 한 번에 최대한 많이 쓰고, 필요한 것만 읽는 것이 좋음


- 필요에 따라 auto scaling 쓰면 절약할 수 있음

 


논의:

한 애플리케이션에 한 개의 테이블을 추천하다고 하는데, 적합한 애플리케이션의 단위는 뭘까?

발표에서는 음원 사이트를 예로 들었는데, 이 테이블에 유저 정보도 포함된 걸까?

 

글쎄... 지금 생각으론, 주요 서비스 단위로 생각해보면 어떨까 싶다.

예를 들어, 음원서비스라면 이런 식으로 테이블을 나눠야 하지 않을까....?

- 유저: 사용자 기본 정보, 즐겨찾기, 아티스트

- 음원

- 아티스트

- 플레이리스트

 

음원과 아티스트는 한 테이블에 넣는 게 좋을까... 애매하다.

 

 

2020.02.20 추가

이후에 좀 더 리서치를 해봤다.

- 발표에서 의미했던 건, 도메인 또는 MSA 당 한 개의 테이블이라고 이해하면 좋을 것 같다.

- 즉, 위에서 언급했던, 유저, 음원, 아티스트, 플레이리스트는 한 개의 테이블로 구성해도 좋겠다.

 

다이나모디비와 싱글 테이블 디자인에 대한 건 나중에 정리.

반응형