[빅데이터] 1장_빅데이터를 위한 새로운 패러다임

1.1 이 책의 구성

이론을 다루는 장과 사례를 다루는 장으로 나뉜다. 이론을 다루는 장은 빅데이터 시스템을 구축하는 방법을 다루고, 사례를 다루는 장에서는 이론을 구체적인 도구에 연관시킨다.

1.2 전통적인 데이터베이스를 사용해 확장하기

간단한 웹 분석 어플리케이션을 개발한다고 하자. 고객의 웹 페이지는 페이지뷰가 발생할 때마다 애플리케이션의 웹서버에 URL 정보를 보내야한다. 웹 서버는 데이터베이스에 페이지뷰에 해당하는 row 의 값을 증가시킨다. 이제 애플리케이션에 개선되며 어떤 문제가 생기는지 살펴본다.

1.2.1 큐를 사용해 확장하기

백엔드의 데이터베이스가 부하를 견디지 못해 페이지 뷰를 증가시키는 쓰기 요청을 신속히 처리하지 못해 타임 아웃이 나고 있다고 하자.
웹 서버가 데이터베이스에 직접 접근하도록 하는 대신, 웹 서버와 데이터베이스 사이에 큐를 넣는다. 그래서, 페이지뷰 이벤트를 받을 때마다 이벤트는 큐에 추가된다. 그리고, 큐에서 한 번에 100개씩 이벤트를 꺼내 하나의 데이터베이스 갱신 요청으로 일괄 처리하는 작업자 프로세스를 추가한다.

1.2.2 데이터베이스를 샤딩하여 확장하기

이 웹 분석 애플리케이션이 계속 인기가 높아져, 데이터베이스에 과부하가 다시 걸렸다고 하자. 기존의 작업자 프로세스로는 쓰기 요청을 감당할 수 없어 갱신을 병렬 처리 하기 위해 작업자 프로세스의 수를 늘렸보았지만 도움이 되지 않았다.
그래서, 데이터베이스 서버를 여러 대 사용하고 테이블을 여러 서버에 분산시키는 수평 분할, 또는 샤딩이라고 불리는 방법을 사용한다. 즉, 각 서버는 전체 테이블의 일부를 가지는 것이다. 쓰기 부하를 여러 서버, 즉 샤드로 분산 시키게 되는 것이다.
이 애플리케이션의 인기가 높어져, 데이터베이스를 더 많은 샤드로 나누었다고 하자. 샤드 개수가 변경되어서 기존의 애플리케이션 코드에도 반영을 해야하고 이것을 깜빡하면 의도하지 않은 샤드에 기록된다.

1.2.3 내결함성 문제 발생 시작

어쨌든 샤드를 늘렸는데, 이제 데이터베이스 장비에서 디스크 장애가 발생하는 상황이 왔다. 디스크 장애가 발생한 장비가 다운된 동안엔 그 디스크에 저장된 데이터를 사용할 수 없다. 해결책으로는,

  1. 큐 / 작업자 시스템을 수정해서, 사용 불능 상태의 샤드로 들어오는 증가 이벤트는 별도의 대기 큐에 넣고 이를테면 5분 마다 한 번씩 큐의 쌓여 있는 이벤트를 일괄 처리한다.
  2. 데이터베이스의 자체 복제 기능을 사용해 마스터가 다운됐을 때 백업으로 사용할 수 있는 슬레이브를 각 샤드에 추가한다. 슬레이브는 쓰기가 불가능하지만, 적어도 고객이 애플리케이션 상태를 조회할 수 있도록은 한다.
1.2.4 데이터 오염 문제

큐 / 작업자 코드를 수정하는 도중, 실수로 모든 URL 에 대한 페이지 뷰를 1이 아니라 2씩 증가 시키는 버그를 만들었다고 하자. 데이터가 오염된다.

1.2.5 어디서부터 잘못된 건가 ?

여러분이 사용하는 데이터베이스는 스스로 데이터가 어떻게 분산되어 있는지 모른다. 이런 복잡함이 모두 데이터베이스 운영 작업과, 애플리케이션 코드 개발 작업에 더해진다.
시스템이 복잡해질수록 실수할 가능성은 커진다. 인적 내결함성 (human fault-tolerance) 는 필수 사항이다.

1.2.6 빅데이터 기술이 어떻게 되움이 되는가 ?

빅데이터용 데이터베이스 및 계산 시스템은 데이터 어떤 식으로 분산되어 있는지를 자체적으로 알고 있다. 그래서, 샤딩이나 복제본 생성 작업을 알아서 처리한다.
또 다른 핵심 기술은, 데이터를 변경 불가 형태로 만든다는 것이다. 정상 데이터를 파괴하지 않아, 전통적인 데이터베이스보다 강력한 human fault-tolerance 을 제공한다.

1.3 NoSQL 은 만병 통치약이 아니다

하둡과 같은 대용량 계산 시스템은, 대용량 일괄 처리식 계산은 병렬로 처리할 수 있지만 계산의 latency 가 오래 걸린다.
카산드라 같은 NoSQL 데이터베이스는 SQL 보다 훨씬 제한된 모델을 제공해서 확작성을 얻는다. 하지만, 이러한 제한된 데이터 모델에 애플리케이션을 끼워 넣는 것은 매우 복잡할 수 있다. 그리고 가변성을 전제로 하기 때문에 인적 내결함성을 갖추지 못하고 있다.
이 도구들을 현명하게 잘 결합하면 인적 내결함성을 갖추고 복잡성을 최소하해 확장성 있는 시스템을 만들 수 있다.

1.4 기본 원칙

데이터 시스템은 여러 조각 정보들을 조합해서 응답을 만든다. 예를 들어, 은행 계좌 잔고는 그 계좌에서 발생한 모든 거래에 대한 정보를 바탕으로 만들어진다. 더 중요한 것은, 모든 정보의 조각들이 동등한 성격이 아니라는 것이다. 어떤 정보는 다른 정보로부터 유래된다. 은행 계좌 잔고는 거래 내력을 가지고 만들어진다.
정보가 만들어진 재료가 되는 원래의 정보를 거꾸로 추적해가면 결국 다른 어떤 정보로부터도 파생되지 않은 정보에 다다른다. 이 정보를 데이터라고 한다. 데이터 시스템을 정의하면 다음과 같다. 데이터로 할 수 있는 모든 일은 현재 갖고 있는 모든 데이터를 입력으로 받는 함수로 표현할 수 있다.

1
query = function(all data)

1.5 빅데이터 시스템에 요구되는 속성

  1. 견고성과 내결함성
    복잡성을 회피해서 시스템에 대해 쉽게 생각할 수 있도록 하는 것도 빅데이터 시스템을 견고하게 만다는 작업이다. 그리고 빅데이터 시스템의 핵심에 불변성과 재계산 체계를 구축해놓으면, 복구 매커니즘이 쉽고 간결해져 사람의 실수에 대한 회복력을 지니게 된다.

  2. 짧은 읽기 / 갱신 지연 시간
    시스템의 견고성을 해치지 않고 짧은 읽기 / 갱신 지연 시간을 얻을 수 있어야한다.

  3. 확장성
    데이터나 부하가 늘어났을 때 시스템에 자원을 더 투입하는 것만으로 원래의 성능을 유지할수 있는 능력이다.

  4. 일반성
    일반적인 시스템은 폭넓은 분야의 애플리케이션에 쓰일 수 있다.

  5. 유연성
    대규모 데이터 이전을 용이하게 만다는 것은 시스템에 유연성을 부여하는 방법 중 하나이다.

  6. ad hoc query
    데이터에 대해 즉석 질의를 수행하는 것은 중요하다.

  7. 최소한의 유지 보수
    유지보수를 최소화하기 위해, 가능하면 구현 복잡도를 낮게 만들어주는 구성 요소가 필요하다.

  8. 디버깅 가능성
    빅데이터 시스템은 뭔가 잘못되었을 때 디버깅하는 데 필요한 정보를 제공해야한다.

1.6 완전 증분 아키턱쳐의 문제점

완전 증분 아키턱쳐의 특징은, 데이터베이스를 읽고 쓴다는 것과 새로운 데이터가 들어왔을 때 데이터베이스의 상태를 증분식으로 변경한다는 것이다. 예를 들어, 페이지뷰를 셀 때 새로운 페이지뷰가 발생할 때마다 해당 URL 의 페이지 뷰 수에 1을 더한다.
이제 완전 증분 아키텍처로 인해 생기는 일반적인 복잡성을 살펴본다.

1.6.1 운영 복잡성

읽기 / 쓰기를 지원하는 데이터베이는, 디스크 색인이 증분식으로 추가 및 변경되기 때문에 색인 일부가 사용되지 않은채로 남는다. 이들 미사용 영역은 공간만 차지할 뿐이고 디스크를 다 채워버리는 것을 막기 위해 회수되어야한다. 색인이 필요 없을 때 바로 회수하는 것은 너무 비용이 비싸기 때문에, 미사용 영역이 차지하는 공간은 가끔 실행되는 압밀화 과정을 통해 큰 단위로 회수된다.
서버는 압밀화를 수행하는 동안 CPU 와 디스크에 상당한 부담을 주게 되어 수행중에는 장비의 성능이 극적으로 떨어진다. 만약 동시에 너무 많은 장비가 압밀화를 수행하면 그들이 담당하고 있던 부하는 클러스터에 있는 다른 장비에 의해 처리되어야하고 이는 잠재적으로 클러스터의 나머지 부분에 과부하를 유발하여 전체 클러스터에 장애가 발생할 수 있다.
압밀화를 잘 관리하려면 한 번에 영향을 받는 노드가 너무 많아지지 않게 각 노드의 스케쥴링을 잡아줘야 한다. 운영 담당 직원이 처리할 수 있게 할 수 있지만, 어떤 성격의 복잡성이든 가장 좋은 대처 방법은 그 복잡성 자체를 아예 제거하는 것이다.
온라인 압밀화에 신경써야하는것은 완전 증분 아키텍쳐에 내재된 복잡성이다.

1.6.2 최종적 일관성을 달성할 때 수반되는 극심한 복잡성

고가용성 시스템은 장비에 장애가 발생하거나 네트워크에 부분적인 장애가 발생할 때도 질의와 갱신을 실행할수 있게 한다. 일관성이라는 키워드와 직접 경쟁한다. CAP 정리에 의하면, 네트워크가 분단된 상황에서는 같은 시스템 안에서 고가용성과 일관성을 동시에 달성할 수 없다고 한다. 그래서, 고가용성 시스템에서는 네트워크 분단이 발생하면 오래된 결과를 반환하는 경우도 있다.
네트워크 분단이 해소되면, 고가용성 시스템이 언젠가는 일관성 있는 결과를 반환하도록 하려면 (== 최종적 일관성), 애플리케이션의 도움이 필요하다. 예를 들어, 데이터베이스에 숫자를 저장하고 개수를 증가 시키는 이벤트를 받을 때마다 그 숫자의 값을 증가시키는 것이다. 이 경우, 네트워크 분단이 발생하면 대량의 데이터 손실이 발생할 수 있다.
그 원인은, 분산 데이터베이스는 모든 정보가 저장된 복제본을 여러 개 유지함으로써 고가용성을 얻고 있기 때문이다. 10을 저장하고 있는 두 대의 복제 서버가 있을 때 네트워크 분단이 발생했다고 해보자. 첫 번째 복제서버에서는 2가 증가했고, 두번째 서버에서는 1이 증가했다. 각각 12와 11을 저장하고 있는 이 서버들의 상태를 합쳐야할 때 어떤 값을 써야할까? 정답은 13 이지만, 12와 11을 봐서는 알 수 없다. 상태가 달라지기 전에 11을 저장하고 이있을 수도 있고 0을 저장하고 있을 수도 있다.
고가용성을 갖춘 상태에서 개수를 올바르게 세려면, 개수만 저장하는 것으로 부족하다. 서로 달라지는 값을 합치는데 적합한 자료구조를 사용해야하고 네트워크 분단이 해소되었을 때 올바른 값을로 수정하는 코드를 구현해야한다. 단순한 개수를 저장하는 것인데, 이렇게 복잡하다.

1.6.3 인적 내결함성 결여

증분 시스템에 데이터베이스에 저장된 상태를 지속적으로 변경한다는 것은 실수로 데이터베이스의 상태를 바꿀 수 있는 것을 의미한다. 실수는 불가피하기 때문에 언젠가 오염될 수 있다.
이를 해결하기 위해,

  1. 애프리케이션이 데이터베이스를 직접 갱신하는 동기식 아키텍쳐 말고,
  2. 이벤트를 큐에 모아 두었다가 백그라운드로 데이터베이스를 갱신하는 비동기식 아키텍쳐를 사용하면 해결할 수 있다.

모든 이벤트를 저장해 놓았기 때문에 사람이 실수로 데이터베이스를 오염시켜도 이벤트 저장소를 참조해 데이터베이스를 올바른 상태로 되돌릴 수 있다.

1.7 람다 아키텍쳐

람다 아키텍쳐의 핵심 아이디어는 빅데이터 시스템을 일련의 계층을로 구축하는 것이다 : 속도 계층 / 서빙 계층 / 일괄 처리 계층
모든 것은 다음 식으로부터 시작한다.

1
query = function(all data)

하지만, 너무 많은 양의 자원이 소모되며 불합리하게 많은 비용이 든다. 누군가의 현재 위치를 찾을 때마다 페타바이트의 데이터 집합으 읽어야된다고 생각해보자.
가장 쉬운 대안은, 사전 계산하는 것이다. 질의 함수를 사전 계산한 결과를 batch view (일괄 처리 뷰) 라고 한다.

1
2
batch view = function(all data)
query = function(batch view)

모든 데이터에 대해 함수를 실행해서 일괄처리 뷰를 생성해 놓는다. 그 후 어떤 질의의 결과가 필요할 때 일괄 처리 뷰에 대해 함수를 실행한다.
일괄처리 뷰를 생성할 때는 모든 데이터에 대해 함수를 실행해야하므로 그 시간이 오래 걸린다. 일괄처리 뷰 생성이 끝나면 일괄처리 뷰에 없는 데이터가 그동안 쌓였을 것이고, 질의는 몇 시간 전의 결과를 반환한다. 이 문제를 고치는 방법을 곧 설명한다.

1.7.1 일괄처리 계층

일과처리 계층은 마스터 복제본 ( 거대한 양의 레코드 목록 ) 을 저장하며 그 마스터 데이터 집합에 대한 일괄처리 뷰를 사전 계산한다.
일과처리 계층은은 두 가지를 할 수 있어야한다.

  1. 불변성을 지니며 증가만 하는 마스터 데이터 집합을 저장
  2. 그 데이터 집합에 대해 임의의 함수를 계산
1
2
3
function runBatchLayer() :
while(true) :
recomputeBatchViews()

일괄처리 계층은 while(tue) 루프 내에서 실행되며 지속적으롤 일괄처리 뷰를 만들어낸다.

1.7.2 서빙 계층

서빙 계층은 일괄처리 뷰를 로딩해서 무작위 읽기를 수행할 수 있도록 하는 특수한 분산 데이터베이스이다. 새로운 일괄 처리 뷰가 생기면 서빙 계층은 자동으로 일괄처리 뷰를 교체해서 더 새로운 결과를 얻을 수 있도록 한다.
일괄 갱신과 무작위 읽기를 모두 지원한다. 주목할 점은, 무작위 쓰기는 지원할 필요가 없다. 데이터베이스에서 대부분의 복잡성은 무작위 쓰기에 의해 유발되므로 이 점은 아주 중요하다.

1.7.3 속도 계층

속도 계층은 애플리케이션에 필요한 만큼 빠르게 새로운 데이터가 질의 함수에 반영되로록 보장한다. 일괄처리 계층은 한 번에 모든 데이터를 대상으로 하지만, 속도 계층은 최근 데이터만을 대상으로 한다. 새로운 데이터를 받을 때마다 실시간 뷰를 갱신한다.
즉, 람다 아키텍쳐는 다음과 같다.

1
2
3
batch view = function(all data)
realtime view = function(realtime view, all data)
query = function(batch view, realtime view)

속도 계층은 무작위 읽기와 무작위 쓰기를 지원하는 데이터베이스를 사용한다. 무작위 쓰기를 지원하기 때문에 다른 계층의 데이터베이스보다 복잡하다.
하지만, 람다 아키텍쳐에서는 데이터가 일괄처리 계층을 거쳐 서빙 계층에 도착하면 실시간 뷰에 있던 해당 데이터의 결과는 더 이상 필요없어진다. 즉, 실시간 뷰가 필요없어지면 버릴 수 있다. 이것이 복잡성 고립이다. 그 처리 결과가 그냥 일시적으로 쓰이고 마는 계층에만 복잡성이 가해진다는 것이다.
일괄 처리 계층에서는 정확한 결과를 얻는 알고리즘을 사용하고, 속도 계층에서는 근사 알고리즘 ( ex : 하이퍼로그로그 ) 을 사용하는 유연성을 얻는다. 일괄 처리 계층은 속도 계층을 무효화하므로 근사치는 정확한 값으로 고쳐지게 된다. 시스템은 최종적 정확성 이라는 속성을 지니게 된다.


빅데이터, 람다 아키텍처로 알아보는 실시간 빅데이터 구축의 핵심 원리와 기법 <네이선 마츠, 제임스 워렌>

Comments