System Design Interview (1)
Introduction
작년 이맘때쯤 엔지니어링 역량을 높이기 위해 시스템 디자인을 공부해보면 어떨까라는 생각이 들었다.
데이터 엔지니어로서의 경험을 돌이켜보면, 이미 데이터베이스 또는 스토리지에 적재된 데이터를 분석 환경으로 수집하고 처리하는 작업을 주로 수행했다. 그래서 그 앞단의 소프트웨어 제품에 대해서는 잘 모르는 경우가 많았고 백엔드 엔지니어에게 수고스런 질문을 하는 일도 잦았다.
마침 지인이 시스템 디자인을 공부한다며 Grokking System Design Interview 라는 자료를 추천해주었고, 데이터 엔지니어가 시스템 디자인을 알아야 하는 이유를 고민해보고 스터디를 시작하였다. 정리하자면 총 3가지 이유로 요약할 수 있었다.
1. 데이터가 어디에 어떤 방식으로 쌓이는지 알아야 한다.
데이터 엔지니어링의 바로 앞단에는 백엔드가 위치한다. 사용자와 주고 받을 데이터의 껍데기를 정의하고, 어떤 데이터베이스를 사용할지 결정하는 곳은 데이터 엔지니어가 아닌 소프트웨어 엔지니어의 영역이다.
즉, 데이터 엔지니어가 데이터 수집 및 처리의 용이를 위해서 데이터의 규격을 바꾸거나 데이터베이스를 교체할 수는 없기 때문에, 기존의 시스템이 이렇게 설계될 수 밖에 없음을 이해할 수 있어야하고 그에 맞게 적절한 데이터 파이프라인을 선택할 수 있어야 한다.
다만 백엔드에서 사용하는 언어나 기술 스택을 모두 공부할 순 없기 때문에, 시스템 디자인을 공부하여 소프트웨어에 대한 이해도와 하위 스트림에 대한 선택지를 넓히는 것이 적절한 방법이라고 생각했다.
2. 데이터 파이프라인 시스템 또한 설계가 필요하다.
데이터베이스로 데이터가 일단 적재되면, 데이터 엔지니어는 데이터를 분석 환경으로 옮기기 위한 데이터 파이프라인을 개발하게 된다.
데이터 엔지니어가 데이터를 얻기 위해 접근할 데이터베이스는 온전히 제품을 사용하는 사용자들을 위한 것이다. 따라서 Data Ingestion을 위해 부하를 주어서는 안되며, 이로인해 발생하는 장애들을 모니터링하고 관리해야 한다.
또 니즈에 따라 배치/스트림 처리를 선택해야 하며, 스트림 처리가 필요한 경우 어떤 분산 엔진을 선택하여 확장성 있게 운영할 수 있을지 고민해야 한다. 만약 CDC 방식을 이용하고 있다면, 데이터 처리가 실패하고 방치된 경우를 잡아내기 위해 뒷단에 Data Quality를 검증해주는 시스템을 추가해주어야 한다.
마지막으로 분석 환경으로 옮겨진 데이터는 용량이 매우 클 확률이 높다. 따라서 수집 이후의 ETL 처리는 대부분 분산 환경에서 이루어지며, 따라서 분산 환경에 대한 이해가 필요하다.
3. 데이터 또는 ML 제품을 서비스로 환원해야 한다.
데이터 엔지니어라도 가끔 백엔드 업무를 할 때가 있다. 데이터를 활용한 백오피스 도구가 필요한 경우, 인력 부족으로 ML 엔지니어링 업무도 함께 수행해야 하는 경우, 데이터 분석 결과를 사용자에게 제공하는 경우 등이 있다.
이때는 소프트웨어를 개발해야 하기 때문에 시스템 디자인 지식이 필요하며, 실제로도 배포 및 캐싱 전략을 세우는데 많은 도움이 되었다.
예를 들면 학습된 ML 아티팩트를 서버로 어떻게 배포할지, CPU와 GPU 리소스를 어떻게 분배할지, SLA를 맞추기 위해 비동기와 캐싱을 활용하는 방식 등을 시스템 디자인으로부터 도움받을 수 있었다.
System Design Interview
이하의 내용은 Grokking System Design Interview 자료를 한글로 작성한 글이며, 상황에 따라 의역이 있을 수 있습니다.
많은 소프트웨어 엔지니어들이 주로 다음 3가지 이유로 인해 System Design Interview(SDI)를 걱정하게 된다.
- 정답이 없는 설계 문제를 해결하도록 요구되는 SDI의 구조화되지 않은 특성
- 큰 규모의 시스템을 개발해 본 경험의 부족
- SDI를 준비할 기회의 부족
코딩 인터뷰와 마찬가지로 SDI에 충분히 노력을 기울이지 않는다면 Google이나 Amazon과 같은 최고 기업에서는 인터뷰를 통과하기 어려울 수도 있다. 반면 SDI에 능숙하다면, 복잡한 시스템을 다룰 수 있는 능력을 면접에서 보여줄 수 있기 때문에 더 좋은 오퍼를 받을 수 있다.
이러한 시스템 디자인 문제를 풀기 위해서는 다음과 같은 점진적인 방식을 적용해볼 수 있다.
Step 1: 요구 사항을 명확히 하기
시스템 디자인 관련 질문들은 정답이 정해져 있지 않고, 또 하나의 정답만 존재하는 것이 아니기 때문에 풀고자 하는 문제의 범위를 명확히 하는 것이 중요하다.
시스템의 목표를 정의하는데 충분한 시간을 들인다면 인터뷰에서 더 좋은 결과를 낼 수 있다. 또한 30~40분이라는 짧은 시간 내에 큰 시스템을 설계해야 하기 때문에, 시스템의 어떤 부분에 집중해야 할지 파악하는 것도 중요하다.
만약 Twitter와 같은 서비스를 만든다고 치자. 그러면 Twitter를 설계하기 위해 다음과 같은 질문들을 던져 볼 수 있을 것이다.
- 서비스를 사용하는 유저들은 트윗을 게시하고 다른 사람들을 팔로우할 수 있을까?
- 유저의 타임라인을 생성하고 보여줄 수 있을까?
- 트윗에 사진과 동영상을 담을 수 있을까?
- 백엔드에만 집중할 것인가 아니면 프론트엔드 또한 개발할 것인가
- 유저들은 트윗을 검색할 수 있는가
- 지금 유행하는 주제들을 보여줄 수 있을까?
- 새로운 트윗에 대한 알람을 보낼 수 있을까?
Step 2: 시스템 인터페이스 정의하기
시스템에 요구되는 API를 정의하고 요구사항에 잘못된 것은 없는지 점검해야 한다.
- postTweet(user_id, tweet_data, tweet_location, user_location, timestamp, …)
- generateTimeline(user_id, current_time, user_location, …)
- markTweetFavorite (user_id, tweet_id, timestamp, …)
Step 3: 규모 어림 잡기
디자인하고자 하는 시스템의 규모를 예상하고 Scaling / Partitioning / Load Balancing / Caching 등의 전략을 사용할 수 있어야 한다.
- 하루 또는 매초 얼마나 많은 트래픽이 발생하는지
- 얼마나 많은 데이터를 저장해야 하는지
- 사용할 수 있는 네트워크 대역폭이 얼마나 되는지
Step 4: 데이터 모델 정의하기
시스템에 필요한 데이터의 엔티티를 초기에 정의할 수 있다면, 시스템을 이루는 구성 요소 간에 데이터가 어떻게 흘러야 할지를 명확히 할 수 있다. 시스템의 다양한 엔티티, 엔티티 간의 상호 작용 및 저장, 전송, 암호화 등과 같은 데이터 관리의 여러 측면을 사전에 확정하는 것이 좋다.
- User: UserID, Name, Email, …
- Tweet: TweetID, Content, TweetLocation, …
- UserFollow: UserID1, UserID2
- FavoriteTweets: UserID, TweetID, Timestamp
또한 필요에 따라 데이터베이스의 성격( SQL v.s. NoSQL)을 고려하여 문제를 해결할 수 있어야 한다.
Step 5: 디자인 고도화하기
시스템을 구성하는 주요 요소들을 5~6개의 박스를 이용해 다이어그램을 그려보자. 실제 문제를 풀기 위해 필요한 구성 요소들을 충분히 고민했는지 확인할 수 있다.
Twitter의 경우 모든 읽기/쓰기 요청을 처리하기 위하여 여러 애플리케이션 서버가 필요하며, 앞단에 Load Balancer를 두어 트래픽을 분산시킨다. 쓰기에 비해 읽기 트래픽이 훨씬 더 많다고 가정한다면, 이러한 시나리오를 해결하기 위해서는 서버를 별도로 분리하는 방법도 있을 것이다.
백엔드에는 모든 트윗을 저장할 수 있고 대량의 읽기를 지원할 수 있는 효율적인 데이터베이스가 필요하다. 또, 사진과 비디오를 저장하기 위한 분산 파일 저장 시스템도 필요하다.
Step 6: 세부적으로 디자인하기
앞서 SDI에는 정답이 없으며 다양한 해결 방법이 존재할 수 있다고 했다. 따라서 우리는 다양한 접근 방식과 그 장단점을 설명할 수 있어야 하고, 시스템의 제약 조건을 염두에 두면서 여러 접근 방식 중 어떤 것을 선택할지 제시할 수 있어야 한다.
- 막대한 양의 데이터를 저장할 때, 여러 데이터베이스에 분산시키기 위해서는 데이터를 어떻게 분할해야 할지
- 트윗 또는 팔로우가 많은 사용자는 어떻게 처리해야 할지
- 사용자의 타임라인에 가장 최근(혹은 관련 있는) 트윗이 노출되도록 하려면 어떻게 저장 방식을 최적화해야 할지
- 속도를 높이기 위해 캐시를 얼마나 많이, 어느 계층에 도입해야 할지
- Load Balancing이 필요한 컴포넌트가 무엇인지?
Step 7: 병목 규정하고 해결하기
설계한 시스템에서 발생할 수 있는 병목에 관해 설명할 수 있어야 하고 해결 방법을 제시할 수 있어야 한다.
- 단일 장애 지점이 있는지? 이것을 극복할 수 있는 방법은?
- 유저의 트래픽을 받아내기 위해 얼마나 많은 Replica를 운영해야하는지?
- 전체 시스템이 종료되지 않으려면 얼마나 많은 Replica를 준비해야하는지?
- 서비스의 성능이나 장애를 모니터링할 수 있는 방법에는 어떤 것들이 있는지
System Design Basics
대규모 시스템을 설계할 때 다음 사항들을 생각해 보아야 한다.
- 사용 가능한 아키텍처 요소에는 어떤 것들이 있는지
- 아키텍처 요소들 간에 어떤 방식으로 작동하는지
- 아키텍처 요소들을 어떻게하면 잘 활용할 수 있는지, trade-off는 무엇인지?
물론 필요로하지 않은데도 확장성에 투자하는 것은 비지니스 적으로 옳지 못하다. 하지만 사전에 설계에 대한 고민을 충분히 한다면 미래에 귀중한 시간과 자원을 절약할 수 있다.
이번 챕터에서는 확장 가능한 시스템을 설계하기 위한 주요 개념들을 알아보고 이를 통해 분산 시스템을 조금 더 이해할 수 있도록 한다.
Key Characteristics of Distributed Systems
분산 시스템이 목표로 하는 주요한 특징으로는 확장성(Scalabilty), 신뢰성(Reliablity), 가용성(Availability), 효율성(Efficiency) 그리고 관리 용이성(Manageability)이 있다.
Scalability
- 시스템, 프로세스, 네트워크를 키우고 수요의 증가를 관리할 수 있는가
- 많은 분산 시스템은 늘어나는 작업량을 감당하기 위해 Scalable을 고려하여 발전함
- 확장성 있는 시스템은 데이터의 용량이 커지고 작업량이 늘어나도 성능의 손실이 없어야 함
- 물론 일반적으로 시스템이 확장성 있게 설계되어도 관리나 환경 비용으로 인해 시스템이 작을 수 있음
- 또한 어떤 작업은 시스템의 결함이나 불가항력에 의해 어쩔 수 없이 분산 처리가 불가능할 수 있음
- 또한 분산처리를 하더라도 성능에 한계가 있을 수 있음
- 확장성 있는 아키텍처는 이러한 상황을 미연에 방지하고 모든 노드가 참여하여 작업하도록 벨런싱
Horizontal vs. Vertical Scaling
- Horizontal Scaling
- 기존의 pool에 새로운 장치를 추가하여 확장하는 것 (scale out / in)
- ex) Cassandra, MongoDB
- Vertical Scaling
- 작은 장치을 더 큰 장치로 교체하여 확장하는 것 (scale up / down)
- 다운타임이 발생할 수 있고, 장치의 규모에 한계가 있음
- ex) MySQL
Reliability
- 정의: 주어진 기간 내에 시스템이 다운될 확률
- Redundancy: Replica의 개수를 늘림으로써 하나라도 살아있을 확률을 높임
- 단일 장애 지점을 극복할 수 있는 방법
Availability
- 정의: 주어진 기간 동안 요구되는 역할을 얼마나 오랫동안 수행할 수 있을지에 대한 비율
- 긴 시간 동안 유지되고, 짧은 시간 내에 복구될 수록 available하다.
- reliable하면 available하지만, available하다고 해서 반드시 reliable하지는 않다.
- 시스템이 자주 다운되지만 빠르게 복구되는 경우
Efficiency
latency (response time)
- 하나의 요청에 대해 얼마나 빠르게 응답할 수 있는지
- non-blocking
throughput (bandwidth)
- 초당 얼마나 많은 요청을 처리할 수 있는지
- asynchronous
Serviceability or Manageability
- 분산 시스템 설계 시 운영 및 유지 관리가 쉬운지도 고려해야 함
- 시스템의 복구 시간이 길면 availability가 감소
- 문제 발생 시
- 감지가 빠르고
- 진단 및 문제의 원인 파악이 쉬우며
- 결함을 수정하기가 용이한지가 중요
Redundancy and Replication
Redundancy
- 신뢰성 및 성능을 향상 시키려는 목적으로 시스템의 중요한 구성 요소 또는 기능을 복제하는 것
- 서버의 복제 뿐만 아니라 파일 또는 데이터의 복제도 의미함
- 시스템에서 단일 장애 지점을 제거하는데 핵심적인 역할을 하며 필요시 백업 또한 제공
Replication
- 정보를 공유하여 중복 리소스 간의 일관성을 보장하는 것을 의미
- reliability, fault-tolerance, accessibility 등을 향상시킴
- 일반적으로 원본과 복제본 간의 Master-Slave 관계를 띔
- Master는 업데이트를 받은 후 이를 각 Slave로 전달
- 각 Slave는 업데이트를 성공적으로 반영했다는 메시지를 Master에게 전달
Load Balancing
Load Balancer(LB)는 어플리케이션, 웹사이트, 데이터베이스에서 응답 성능을 높이기 위해 클러스터 단위의 서버로의 트래픽을 나누어 주는 분산 시스템이다.
- Scailability: 트래픽이 늘어나면 애플리케이션의 replica를 생성하여 대응 가능
- Reliability: 애플리케이션 서버가 단일 장애 지점(Single Point of Failure)이 되는 것을 방지
- Efficiency: 트래픽이 각 서버로 분산되므로 서버의 반응성(Responsiveness)이 향상
Benefits
- 유저는 빠르고 중단되지 않는 서비스를 경험할 수 있다.
- 서비스 공급자 입장에서 더 적은 downtime과 높은 throughput을 경험할 수 있다.
- 더 많은 요청을 처리할 수 있고 유저의 대기 시간을 줄일 수 있다.
- 시스템 관리자는 덜 실패하고 덜 부하받는 컴포넌트를 경험할 수 있다.
LB Algorithm
- Health Check
- 기본적으로 서버의 건강을 주기적으로 체크하고, 만약 건강하지 않은 경우(unhealthy) pool에서 제거하여 트래픽을 보내지 않음
- Least Connection Method
- 활성화 된 커넥션의 개수가 가장 적은 노드로 트래픽을 할당
- Least Response Time Method
- 평균 응답 시간이 가장 짧은 노드로 트래픽을 할당
- Least Bandwidth Method
- 초당 대역폭이 가장 적은 노드로 트래픽을 할당
- Round Robin Method
- 노드 리스트를 순회하며 차례대로 트래픽을 할당
- Weighted Round Robin Method
- Capacity가 큰 노드에 weight를 주어 더 많은 트래픽을 할당
- IP Hash
- IP 주소를 hash + modulo ( % node 개수 )하여 트래픽을 할당
Redundant LB
- 로드 밸런서는 그 자체로 단일 장애 지점이 될 수 있다.
- 두번째 로드 밸런서를 준비하여 첫번째 로드 밸런서와 서로 healthy를 주고 받는다.
- 메인 로드 밸런서가 죽으면 두번째 로드 밸런서가 작업을 인계한다.
Caching
LB가 수평적 확장을 통해 도움을 준다면, 캐싱은 이미 만들어진 리소스를 잘 사용할 수 있도록 한다. 캐싱은 하드웨어, OS, 웹 브라우저, 웹 어플리케이션 등 거의 대부분의 컴퓨팅 계층에서 사용된다.
Application server cache
요청에 대한 응답을 처리할 때, 서버는 로컬에 캐시된 데이터가 만약에 존재한다면 반환하고 만약 없다면 쿼리를 통해 디스크로부터 데이터를 받아온다. 캐시는 메모리(매우 빠름) 또는 로컬 디스크(네트워크 스토리지 보다는 빠름)에 위치할 수 있다.
노드의 개수를 늘린다면 각 노드별로 그들의 캐시를 갖게된다. 그러면 만약 LB가 무작위의 노드에게 요청을 분배한다면 캐시 적중률은 떨어질 것이다. 이런 문제를 해결하기 위해 글로벌 캐시와 분산 캐시 두가지 방법이 있다.
Content Distributed Network (CDN)
CDN은 대용량의 static media를 제공하는 사이트에 적용되는 일종의 캐시이다. 전형적인 CDN은 static media의 일부가 요청으로 들어오면 locally available할 때 콘텐츠를 제공한다. 만약 locally available하지 않으면 CDN은 즉시 백엔드 서버로 파일을 요청하고 이를 로컬에 캐시한 후 유저에게 콘텐츠를 제공한다.
만약 구축 중인 시스템이 자체 CDN을 보유할 만큼 그리 크지 않은 경우 NginX와 같은 경량 HTTP 서버를 사용하여 별도의 하위 도메인에서 static media를 제공함으로써 향후 전환을 쉽게 할 수 있다. 추후에 CDN으로 DNS를 cut-over(한순간에 기존 시스템을 새로운 시스템으로 전환) 하면 된다.
Cache Invalidation
캐시하는 것은 좋지만 source of truth에 대한 캐시 일관성을 유지하는 것이 필요하다. 데이터베이스의 데이터가 변경된다면 그 데이터의 캐시는 만료되어야한다. 그렇지 않으면 application 작동에 정합성 문제가 발생할 것이다.
이러한 문제를 해결하기 위해 3가지 방법이 존재한다.
- Write-through Cache
- 데이터를 데이터베이스에 저장하면서 캐시에도 저장
- 데이터 유실에 신경써야함
- 쓰기 요청 하나 당 두번씩 작업을 해야하기 때문에 높은 지연 시간이 단점
- Write-around Cache
- 데이터를 캐시를 하고 데이터베이스에 저장
- 마찬가지로 높은 지연 시간이 단점
- Write-back Cache
- 데이터를 먼저 캐시하고 이후 시간이 지나면 영구적으로 저장
- 지연 시간은 짧지만 데이터 유실 가능성이 있음
Cache eviction policies
- First In First Out (FIFO)
- 얼마나 자주 또는 얼마나 많이 접근했던지 간에 가장 먼저 등록된 block을 해제
- Last In First Out (LIFO)
- 가장 최근에 등록된 block을 해제
- Least Recently Used (LRU)
- 가장 오래전 사용된 캐시를 해제
- Most Recently Used (MRU)
- 가장 최근에 사용된 캐시를 해제
- Least Frequently Used (LFU)
- 가장 덜 사용된 캐시를 해제
- Random Replacement (RR)
- 무작위로 캐시를 해제
Sharding or Partitioning
데이터 파티셔닝은 대용량의 데이터를 여러 부분으로 나누는 기술이다. 데이터를 나누는 이유로는, 어떤 시점부터는 서버를 수직적으로 증가시키기 보다 더 많은 장치를 추가하여 수평적으로 스케일링하는 것이 값싸고 적절하기 때문이다.
Partitioning Methods
- Horizontal Partitioning
- row를 다른 테이블에 적재
- Vertical Partitioning
- 테이블을 다른 서버에 적재
- 구현이 간단하며 어플리케이션에 미치는 영향이 적다
- 서비스가 커지면 기능 별 DB를 여러 서버에 걸쳐 분할해야하는 문제가 생긴다
- Directory Based Partitioning
- 위 두 방식의 단점을 보완하기 위한 느슨한 결합 방식은, 현재 분할 방식을 알고 있는 lookup 서비스를 생성하고 DB access code로 부터 추상화하는 것
- 특정 데이터 엔티티가 어디에 있는지 알기 위해 각 tuple key와 DB 서버 간의 매핑을 보유한 디렉토리 서버를 쿼리
- 이러한 느슨한 결합 방식은 어플리케이션에 영향을 주지 않고 DB 풀에 서버를 추가하거나 파티셔닝 방식을 변경하는 등의 작업 수행 가능
Partitioning Criteria
- Key or Hash-based Partitioning
- 엔티티의 key에 hash function을 적용한 후 modulo
- hash 함수는 uniform allocation을 보장
- 새로운 서버가 추가되면 데이터의 재분배가 필요하고 이로인해 downtime 발생
- 따라서 consistent hashing을 사용하기도 함
- List Partitioning
- 새로운 record를 추가하고 싶을 때, 이미 만들어 놓은 파티션 리스트에서 선택
- 예시로 유저가 사는 위치에 따라 파티셔닝
- Round-robin Partitioning
- i 번째 튜플을 n 파티션에 할당 (i mod n)
- Composite Partitioning
- list partitioning을 적용한 후, hash 기반의 partitioning 수행
Common Problems of Sharding
Sharding된 데이터베이스는 테이블이나 row에 대한 연산을 더 이상 하나의 서버로만 처리할 수 없기 때문에 여러 제약사항이 발생하게 된다.
- Joins and Denormalization
- 하나의 서버에서 join 연산을 수행하는 것은 간단하지만, 데이터베이스가 파티션되어 있고 여러 머신에 나누어져 있다면 join을 여러 데이터베이스 샤드들에 걸쳐 수행하는 것이 적절하지 못함
- 이러한 join은 특히 데이터가 여러 서버에서 컴파일되기 때문에 비효율적임
- 이 문제의 해결방법은 join을 필요로하는 쿼리가 미리 수행되도록 데이터베이스를 denormalization하는 것
- 서비스는 데이터 정합성 등 denormalization으로 인한 위험을 감당해야 함
- Referential Integrity
- partition된 데이터베이스에서 샤드 간 쿼리를 수행하는 것이 적합하지 않듯이, shard된 데이터베이스에서 foreign key와 같이 데이터 무결성 제약조건을 지키는 것은 굉장히 어려움
- 대부분의 RDBMS는 서로 다른 서버에서 데이터베이스 간의 foreign key 제약을 지원하지 않음
- 따라서 application 코드 레벨에서 참조 무결성을 지키도록 해야함
- 이런 경우에는 어플리케이션이 허상(dangling) 참조를 지울 수 있도록 sql 작업을 실행해야 함
- Rebalancing
- 기존의 sharding 전략을 변경해야할 경우가 있음
- 데이터 분포가 uniform 하지 않은 경우
- 하나의 shard에 부하가 몰리는 경우
- 위와 같은 경우에는 DB의 shard를 늘리거나 기존에 사용하던 shard들을 리벨런싱해야 함
- 이는 기존의 파티셔닝 전략이 수정되어 모든 데이터가 새로운 곳으로 이동해야함을 의미
- downtime 없이 이를 수행하는 것은 매우 어려움
- directory-based partitioning 전략을 사용하는 것은 시스템의 복잡성을 증가시키고 새로운 단일장애지점을 생성하는 등 리벨런싱을 하는데 매우 즐거운 경험을 선사함^^
- lookup service는 파티셔닝 전략을 인지하고 데이터베이스 접근 코드를 추상화
- 어플리케이션에 영향 없이 DB 서버를 추가하거나 파티셔닝 전략을 변경 가능
- 하지만 새로운 단일 장애 지점이 됨
- 기존의 sharding 전략을 변경해야할 경우가 있음
Indexes
인덱스는 데이터베이스에서의 개념으로 잘 알려져 있다. 빠르든 늦든 데이터베이스의 성능이 더 이상 안정적이지 못할 때가 올텐데, 그때 가장 먼저 해볼 수 있는 대처로는 데이터베이스의 인덱스를 손보는 것이다.
인덱스 생성의 목적은 데이터베이스의 특정 테이블을 빠르게 탐색하여 원하는 row들을 빠르게 찾아내는 것이다. 인덱스는 테이블에서 하나 또는 여러 컬럼을 이용해 생성 가능하며, ordered record에 효율적으로 접근하고 빠른 random lookup을 제공한다.
Example: A Library Catalog
- 대충 도서관에서 책 빨리 찾는 방법 설명
How do Indexes decrease write performance?
- 인덱스가 데이터를 찾는데 걸리는 시간을 어마어마하게 줄여주지만, 데이터를 추가하고 수정하는 시간이 오래 걸려 그 자체로 크기가 클 수 있음
- row를 추가하거나 수정하는데는 데이터 쓰기 뿐만 아니라 인덱스를 업데이트하는 작업도 필요
- 이는 쓰기 시간을 줄이기 때문에 모든 insert, update, delete 연산을 저하시킴
- 이런 이유로 필요없는 인덱스를 생성하는 것을 지양하고 사용하지 않는 인덱스는 제거해야 함
- 읽기보다 쓰기가 더 많이 이루어지는 경우에는 성능이 저하되지만, 읽기 성능을 높이는 것이 보통 가치있음
Proxies
프록시 서버는 클라이언트와 백엔드 서버 사이의 중간 서버이다. 클라이언트는 웹 페이지, 파일, 연결 등과 같은 서비스를 요청하기 위해 프록시 서버에 연결을 요청한다. 즉, 프록시 서버는 다른 서버에서 리소스를 찾는 클라이언트의 요청을 중개하는 소프트웨어 또는 하드웨어이다.
일반적으로 프록시는 요청을 필터링하고, 요청을 기록하거나 때로는 요청을 변환(헤더 추가/제거, 암호화/해독 또는 리소스 압축을 통해)하는 데 사용된다.
프록시 서버의 또 다른 장점은 캐시를 통해 많은 요청을 처리할 수 있다는 점이다. 여러 클라이언트가 특정 리소스에 접근하는 경우에는 프록시 서버가 이를 캐시하여 원격 서버로 이동하지 않고도 모든 클라이언트에 이를 제공할 수 있다.
SQL vs. NoSQL
NoSQL 데이터베이스가 속도와 확장성으로 인해 인기를 얻고 있음에도 불구하고, 여전히 SQL 데이터베이스가 더 나은 성능을 발휘할 수 있는 상황은 존재한다. 따라서 사용 목적에 따라 올바른 데이터베이스를 선택하는 것이 무엇보다 중요하다. 필요하다면 관계형 데이터베이스와 비관계형 데이터베이스 모두 사용할 수 있어야 한다.
SQL 데이터베이스를 사용하는 이유
MySQL, PostgresQL 등
1. ACID를 보장
ACID는 트랜잭션이 데이터베이스와 상호 작용하는 방식을 정확하게 규정하여 이상 현상을 방지하고 데이터베이스의 무결성을 보호한다.
일반적으로 NoSQL 데이터베이스는 확장성과 처리 속도를 위해 ACID 준수를 희생하지만, 많은 전자 상거래 및 금융 애플리케이션에서는 ACID를 준수하는 SQL 데이터베이스가 여전히 선호되는 선택지이다.
2. 구조화된 데이터
데이터가 구조화되어 있고, 자주 변경되지 않을 경우에는 SQL 데이터베이스를 사용하지 않을 이유가 없다.
NoSQL 데이터베이스를 사용하는 이유
MongoDB, CouchDB, Cassandra, HBase 등
1. 제한되지 않는 데이터 유형
최근 기업에서는 구조가 거의 또는 전혀 없는 대용량 데이터를 다루는 경우가 많다. NoSQL 데이터베이스는 저장할 수 있는 데이터 타입에 제한을 두지 않으며, 필요에 따라 새로운 타입을 추가할 수 있다. 문서 기반 데이터베이스를 사용하면 데이터의 타입을 미리 정의하지 않고도 데이터를 한 곳에 저장할 수 있다.
2. 클라우드 컴퓨팅과 스토리지의 활용
클라우드 기반 스토리지를 이용하면 비용을 절감할 수 있지만, 데이터를 여러 서버에 분산시켜야 한다. 클라우드에서 상용 하드웨어를 사용하면 추가 소프트웨어 설치의 번거로움이 줄어들고, Cassandra와 같은 NoSQL 데이터베이스는 큰 어려움 없이 즉시 여러 데이터 센터에 걸쳐 확장 가능하도록 설계되어 있다.
3. 신속한 개발
NoSQL은 미리 준비할게 없으므로 개발을 신속하게 진행할 수 있다. 버전 간에 다운 타임 없이 데이터 구조를 자주 업데이트하는 등의 빠르고 반복적인 시스템 작업의 경우, 관계형 데이터베이스를 사용하면 속도가 느릴 수 밖에 없다.