블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 

대충보는 Storm #2-Storm 설치와 HelloStorm 작성하기

조대협(http://bcho.tistory.com)


Apache Storm Spark

앞서 데이타 스트리밍 처리에 대해서 설명했다. 스트리밍 처리에 대표적인 오픈소스 프레임웍으로는 Apache Storm Apache Spark이 있는데ㅔ, Spark은 최근에 나온 것으로 스트리밍 처리뿐 만 아니라 조금 더 보편적인 분산 컴퓨팅을 지원하는데, Storm의 경우 나온지도 오래되었고 무엇보다 안정성 부분에서 아직까지는 Spark보다 우위에 있기 때문에, Storm을 중심으로 설명하고자 한다

HelloStorm

Storm의 내부 구조 개념등을 설명하기에 앞서, 일단 깔아서 코드부터 돌려보고 개념을 잡아보자


HelloStorm 구조

HelloWorld 처럼 간단한 HelloStorm을 만들어보자. 만들어보려고 하는 Storm 프로그램은 다음과 같다.



<그림. HelloStorm 개념 구조>


HelloSpout 이라는 클래스는, Storm에 데이타를 읽어오는 클래스로 이 예제에서는 자체적으로 데이타를 생성해낸다. Storm으로 들어오는 데이타는 Tuple이라는 형식을 따르는데, Key/Value 형식의 데이타 형을 따른다. 여기서는 키(필드명)“say”, 데이타는 “Hello” 라는 문자열을 가지고 있는 데이타 tuple을 생성한다.

HelloSpout에서 생성된 데이타는 HelloBolt라는 곳으로 전달이 되는데, HelloBolt 클래스는 데이타를 받아서 처리하는 부분으로 간단하게 들어온 데이타에서 “say” 라는 필드의 데이타 값을 System.out으로 출력해주는 역할만을 한다.


개발하기

이클립스를 사용하여, maven project를 생성한다.



다음으로 pom.xml을 작성한다.


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

 

  <groupId>com.terry.storm</groupId>

  <artifactId>hellostorm</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <packaging>jar</packaging>

 

  <name>hellostorm</name>

  <url>http://maven.apache.org</url>

 

  <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

  </properties>

 

<dependencies>

  <dependency>

    <groupId>junit</groupId>

    <artifactId>junit</artifactId>

    <version>3.8.1</version>

    <scope>test</scope>

  </dependency>

  <dependency>

        <groupId>org.apache.storm</groupId>

        <artifactId>storm-core</artifactId>

        <version>0.9.3</version>

  </dependency>

 

</dependencies>

 

<build>

  <plugins>

    <plugin>

      <artifactId>maven-assembly-plugin</artifactId>

      <version>2.2.1</version>

      <configuration>

        <descriptorRefs>

          <descriptorRef>jar-with-dependencies</descriptorRef>

        </descriptorRefs>

        <archive>

          <manifest>

            <mainClass />

          </manifest>

        </archive>

      </configuration>

      <executions>

        <execution>

          <id>make-assembly</id>

          <phase>package</phase>

          <goals>

            <goal>single</goal>

          </goals>

        </execution>

      </executions>

    </plugin>

  </plugins>

</build>

 

</project>

<그림. pom.xml>


이 예제에서는 storm 0.9.3을 사용했기 때문에 위와 같이 storm-core 0.9.3 dependency 부분에 정의하였다.

다음으로 데이타를 생성하는 HelloSpout을 구현하자


package com.terry.storm.hellostorm;

 

import java.util.Map;

 

import backtype.storm.spout.SpoutOutputCollector;

import backtype.storm.task.TopologyContext;

import backtype.storm.topology.OutputFieldsDeclarer;

import backtype.storm.topology.base.BaseRichSpout;

import backtype.storm.tuple.Fields;

import backtype.storm.tuple.Values;

 

public class HelloSpout extends BaseRichSpout {

          private static final long serialVersionUID = 1L;

          private SpoutOutputCollector collector;

         

          public void open(Map conf,TopologyContext context,SpoutOutputCollector collector){

               this.collector = collector; 

          }

         

          public void nextTuple(){

                 this.collector.emit(new Values("hello world"));

          }

         

          public void declareOutputFields(OutputFieldsDeclarer declarer){

                 declarer.declare(new Fields("say"));

          }

         

}

<그림. HelloSpout.java>


HelloSpout 실행이 되면, 필드 “say” 값이 “hello world” 데이타를 생성해서 다음 워크 플로우로 보낸다.

nextTuple() 이라는 함수에서 외부에서 데이타를 받아들여서 다음 워크 플로우로 보내는 일을 하는데, 여기서는 외부에서 데이타를 받아들이지 않고 자체적으로 데이타를 생성하도록 한다. 데이타를 뒤에 워크플로우에 보내는 함수는 emit인데, emmit부분에 “hello world”라는 value 넣어서 보내도록 하였다. 그렇다면 필드의 값은 어떻게 정의 하느냐? 필드값은 declareOutputField라는 함수에 정의하는데, 데이타의 필드는 “say” 정의하였다.


다음으로 이 HelloSpout에서 생성할 데이타를 처리한 HelloBolt를 구현해보자


package com.terry.storm.hellostorm;

 

import backtype.storm.topology.BasicOutputCollector;

import backtype.storm.topology.OutputFieldsDeclarer;

import backtype.storm.topology.base.BaseBasicBolt;

import backtype.storm.tuple.Tuple;

 

public class HelloBolt extends BaseBasicBolt{

 

        public void execute(Tuple tuple, BasicOutputCollector collector) {

               // TODO Auto-generated method stub

               String value = tuple.getStringByField("say");

               System.out.println("Tuple value is"+value);

        }

 

        public void declareOutputFields(OutputFieldsDeclarer declarer) {

               // TODO Auto-generated method stub

              

        }

 

}

<그림. HelloBolt.java>


HelloSpout에서 생성된 데이타는 HelloBolt 들어오는데, 데이타가 들어오면 execute라는 메서드가 자동으로 수행된다. 이때, Tuple 통해서 데이타가 전달된다. 여기서는 tuple에서 필드이름이 “say” 값을 tuple.getStringByField(“say”) 이용해서 꺼내서 System.out으로 출력했다.

눈치가 빠른 사람이라면 벌써 알아차렸겠지만, 데이타를 다음 플로우로 보내고자 할때는 앞의 HelloSpout에서 한것처럼, execute 메서드내에서 데이타 처리가 끝난후에, collector.emit 이용해서 다음 플로우로 보내고, delcareOutputField에서 데이타에 대한 필드를 정의하면 된다.

데이타를 생성하는 Spout 데이타를 처리 하는 Bolt 구현했으면 둘을 연결 시켜줘야 한다. 이를 연결시켜주는 것이 Topology인데, HelloTopologyLocal 클래스를 구현해 보자


package com.terry.storm.hellostorm;

 

import backtype.storm.Config;

import backtype.storm.LocalCluster;

import backtype.storm.topology.TopologyBuilder;

import backtype.storm.utils.Utils;

 

public class HelloTopologyLocal {

        public static void main(String args[]){

               TopologyBuilder builder = new TopologyBuilder();

               builder.setSpout("HelloSpout", new HelloSpout(),2);

               builder.setBolt("HelloBolt", new HelloBolt(),4).shuffleGrouping("HelloSpout");

              

               Config conf = new Config();

               conf.setDebug(true);

               LocalCluster cluster = new LocalCluster();

              

               cluster.submitTopology("HelloTopologyLocal", conf,builder.createTopology());

               Utils.sleep(10000);

               // kill the LearningStormTopology

               cluster.killTopology("HelloTopologyLocal");

               // shutdown the storm test cluster

               cluster.shutdown();          

        }

 

}

<그림. HelloTolologyLocal.java>


나중에 개념에서 자세하 설명하겠지만, Topology 데이타를 생성하는 Spout 처리하는 Bolt간에 토폴로지 데이타 흐름을 정의하는 부분이다. Spout Bolt들을 묶어 주는 부분이다.

먼저 TopologyBuilder 이용해서 Topology 생성하고, setSpout 이용해서 앞에서 구현한 HelloSpout 연결한다.

다음으로, setBolt 이용해서 Bolt Topology 연결한다. 후에, HelloSpout HelloBolt 연결해야 하는데, setBolt시에, SuffleGrouping 메서드를 이용하여, HelloBolt HelloSpout으로 부터 생성되는 데이타를 읽어들임을 명시한다.

builder.setBolt("HelloBolt", new HelloBolt(),4).shuffleGrouping("HelloSpout");

이렇게 Topology 구성되었으면이 Topology 실제로 실행해야 하는데, Topology 어떤 서버에서 어떤 포트등을 이용해서 실행될지는 Config 정의할 있지만, 여기서는 간단한 테스트이기  때문에 별도의 복잡한 Config 정보는 기술하지 않았다.

다음으로 이렇게 만들어진 Topology Storm 클러스터에 배포해야 하는데, Storm 개발의 편의를 위해서 두가지 형태의 클러스터를 제공한다. 개발용 클러스터와 실운영 환경용 클러스터를 제공하는데, 여기서는 LocalCluster cluster = new LocalCluster();

라는  것을 사용하였다.

LocalCluster 개발환경용 클러스터로, 개발자의 환경에서 최소한의 서버들만을 기동하여 개발한 토폴로지를 테스트할 있게 해준다. 이렇게 Cluster 생성했으면 cluster.submitTopology 이용하여 개발한 토폴로지를 배포한다. 토폴로지가 배포되면 자동으로 토폴로지가 실행이 된다. HelloSpout 계속해서 데이타를 생성하고, HelloBolt 생성된 데이타를 받아서 System.out.println으로 출력하게 되는데, 10초후에 멈추게 하기 위해서, Sleep 10초를 준다. 토폴로지 코드를 실행하는 쓰레드는 Sleep으로 빠질지 모르지만 토폴로지에서 생성된 HelloSpout HelloBolt 쓰레드는 백그라운드에서 작업을 계속 진행한다.

10초후에는 killTopology 이용해서 해당 토폴로지를 제거하고 shutdown 이용해서 Storm 클러스터를 종료시킨다.

실행하기

여기까지 구현했으면 첫번째 Storm 프로그램을 기동해보자. 다음과 같이 maven 명령어를 이용하면 실행이 가능하다.

C:\dev\ws\java_workspace\com.terry.storm>mvn exec:java -Dexec.mainClass=com.terry.storm.hellostorm.HelloTopologyLocal -Dexec.classpath.Scope=compile

실행을 해보면, HelloSpout 데이타를 생성하고, HelloBolt 이를 받아서 화면에 출력하는 것을 있다.


6292 [Thread-16-HelloSpout] INFO  backtype.storm.daemon.task - Emitting: HelloSpout default [hello world]

6292 [Thread-22-HelloBolt] INFO  backtype.storm.daemon.executor - Processing received message source: HelloSpout:5, stream: default, id: {}, [hello world]

Tuple value ishello world

6292 [Thread-10-HelloBolt] INFO  backtype.storm.daemon.executor - Processing received message source: HelloSpout:6, stream: default, id: {}, [hello world]

Tuple value ishello world

ZooKeeper 에러 대응하기

종종 환경에 따라서 실행이 안되면서 다음과 같은 에러가 출력되는 경우가 있는데


3629 [main] INFO  org.apache.storm.zookeeper.ZooKeeper - Initiating client connection, connectString=localhost:2000 sessionTimeout=20000 watcher=org.apache.storm.curator.ConnectionState@7bfd25ce

3649 [main-SendThread(0:0:0:0:0:0:0:1:2000)] INFO  org.apache.storm.zookeeper.ClientCnxn - Opening socket connection to server 0:0:0:0:0:0:0:1/0:0:0:0:0:0:0:1:2000. Will not attempt to authenticate using SASL (java.lang.SecurityException: 로그인 구성을 찾을 없습니다.)

3650 [main-SendThread(0:0:0:0:0:0:0:1:2000)] ERROR org.apache.storm.zookeeper.ClientCnxnSocketNIO - Unable to open socket to 0:0:0:0:0:0:0:1/0:0:0:0:0:0:0:1:2000

3655 [main-SendThread(0:0:0:0:0:0:0:1:2000)] WARN  org.apache.storm.zookeeper.ClientCnxn - Session 0x0 for server null, unexpected error, closing socket connection and attempting reconnect

java.net.SocketException: Address family not supported by protocol family: connect

        at sun.nio.ch.Net.connect(Native Method) ~[na:1.6.0_37]

        at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:532) ~[na:1.6.0_37]

        at org.apache.storm.zookeeper.ClientCnxnSocketNIO.registerAndConnect(ClientCnxnSocketNIO.java:277) ~[storm-core-0.9.3.jar:0.9.3]

        at org.apache.storm.zookeeper.ClientCnxnSocketNIO.connect(ClientCnxnSocketNIO.java:287) ~[storm-core-0.9.3.jar:0.9.3]

        at org.apache.storm.zookeeper.ClientCnxn$SendThread.startConnect(ClientCnxn.java:967) ~[storm-core-0.9.3.jar:0.9.3]

        at org.apache.storm.zookeeper.ClientCnxn$SendThread.run(ClientCnxn.java:1003) ~[storm-core-0.9.3.jar:0.9.3]


이 에러는 Storm Zookeeper와 연결을 할 수 없어서 나는 에러인데, LocalCluster 모드로 기동할 경우,Storm embedded Zookeeper를 기동해서 이 Zookeeper와 연결되어야 하나. IPV6로 연결을 시도하기 때문에, (ZK IPV4 Listen하는데) 발생하는 문제로

java로 실행할때 "-Djava.net.preferIPv4Stack=true" 옵션을 주면, JVM IPV6를 사용하지 않고, V4를 사용하기 때문에, ZooKeeper IPV4로 뜨고, Storm IPV4로 연결을 시도하기 때문에 문제가 없어진다.

지금까지 간단하게나마 첫번째 Storm 프로그램을 작성해서 실행해보았다.

다음에는 Storm 이루는 컴포넌트 구조와 아키텍쳐에 대해서 설명하도록 한다

 

람다 아키텍쳐의 소개와 해석

조대협 (http://bcho.tistory.com)


람다 아키텍쳐란

람다 아키텍쳐는 트위터에서 스트리밍 컴퓨팅에 있었던Nathan Marz에 의해서 소개된 아키텍쳐로, 실시간 분석을 지원하는 빅데이타 아키텍쳐이다.

아키텍쳐에 대한 자세한 내용은 http://lambda-architecture.net/ 에 소개되어 있다.


문제의 정의

아키텍쳐에 대한 이해를 돕기 위해서 예를 들어 설명해보자.

 페이스북과 SNS 애플리케이션 SNS가 있다고 가정하자. 이 애플리케이션은 모바일 애플리케이션이며, 글쓰기, 읽기, 댓글 달기, 스크롤 하기, 페이지 넘기기등 약 1000여개의 사용자 이벤트가 있다고 가정하자.

 사용자 수는 대략 1억명이며, 매일 이 각 사용자의 행동 패턴을 서버에 저장하여, 일별로, 사용자 이벤트의 개수를 통계로 추출한다고 하자.

클라이언트 디바이스로 부터 올라오는 데이타는 다음과 같다

  • 사용자 : 조대협

  • 날짜 : 2015년 1월 5일



<그림 1. 클라이언트에서 올라오는 데이타 포맷>

이런 환경에서, 기간별 특정 이벤트 추이, 가장 많이 활용되는 이벤트 TOP5 등의 통계 정보를 실시간으로 보고 싶다고 가정하자

가장 단순한 접근은 RDBMS에 저장하고 쿼리를 수행하는 방법이다.


<그림 2. 로그 데이타를 RDBMS에 저장한 포맷>

RDBM에 저장하고 SQL 쿼리문을 돌리면 되겠지만, 문제는 간단하지 않다. 1000개의 컬럼에, 1억명이 사용하는 시스템이다. 즉. 하루에 최대 1000개의 컬럼 짜리, 1억개의 레코드가 생성이 된다것이다.한달이면 30억개의 레코드이다.

이런 많은 데이타를 동적 SQL로 실행하였을때 그 수행시간이 많이 걸린다.


배치를 활용

그러면 이런 시간이 많이 걸리는 문제를 어떻게 해결하면 좋을까? 이를 위한 전통적인 접근 방식은 배치(BATCH)를 활용하는 것이다. 배치는, 어떤 특정 정해진 시간에, 계산을 미리 해놓는 것이다.

즉 데이타를 모아 놓았다가.밤마다.그날짜의 사용자들의 이벤트들의 합을 매일 계산해놓은 테이블을 만들어 놓으면 된다.



<그림 3. 일별 배치로 생성된 이벤트 데이타 테이블>

자아, 이렇게 배치로 테이블을 만들어 놓으면, 특정 기간에 각 이벤트별 통계를 내기가 쉬워 진다. 1년분의 데이타라하더라도 365 행 밖에 되지 않기 때문에, 속도 문제가 해결이 된다.

실시간 데이타의 반영

테이블 조인

이렇게 배치 테이블을 생성하면, 성능에 대한 문제는 해결이 되지만, 데이타가 배치 주기에 따라 최대 1일의 편차를 두게 된다. 즉 실시간 반영에 대한 문제가 발생한다.

그렇다면 어떻게 해결을 해야 할까? 해결은 배치 테이블과 그날의 데이타 테이블을 두개를 같이 사용하면 된다.

즉 어제까지의 데이타는 일별 배치로 생성된 테이블을 사용하고, 오늘 데이타 부분은 사용자별로 기록된 로그 테이블을 사용하여 두 테이블을 조인 하면, 오늘의 지금 순간의 통계값까지 볼 수 있다.

 


<그림 4. 테이블 조인을 이용한 실시간 데이타 통계 추출 >


실시간 집계 테이블의 활용

하루에 쌓이는 데이타량이 얼마 되지 않는다면 문제가 되지 않겠지만, 이 시나리오에서 하루에 쌓이는 데이타는 일 최대 1억건이 된다. 즉, 오늘 쌓이는 데이타 테이블을 조인 하면 1억개의 행에 대한 연산이 발생하여 적절한 성능을 기대하기 어렵다. 

그렇다면, 배치는 매일 돌리되, 오늘 데이타에 대한 통계 값을 실시간으로 업데이트 하는 방법을 생각해볼 수 있다. 

아래 그림과 같이 로그서버에서 클라이언트에서 받은 로그를 원본 데이타 테이블에 계속 저장을 하고, 오늘 통계에 대한 실시간 집계 테이블에, 글쓰기, 글 읽기 등 개별 이벤트의 값을 계산해서 더해 주면 된다.

 


<그림 5. 실시간 집계 테이블>

이렇게 하면, 실시간 집계 테이블과, 배치 테이블을 조인하여 빠르게 실시간 통계를 볼 수 있다.

즉 일별 실시간 통계는 다음 그림과 같이 당일전의 배치뷰와 당일의 실시간뷰를 합쳐서 통계를 낸 형태가 된다.

 


<그림 6. 실시간 통계를 뽑기 위한 테이블들의 관계>


람다 아키텍쳐를 활용

이 개념을 람다 아키텍쳐로 해석해보자. 데이타 흐름을 도식화 해보면 다음과 같다.

 


<그림 7. 람다 아키텍쳐의 개념>


먼저 배치 처리를 위해서, 로그 서버는 모든 로그 데이타를 저장소에 저장하고, 배치 처리 계층에서 일일 또는 일정한 시간을 주기로 배치 처리로 계산을 해서 배치 뷰(배치 테이블)을 만든다.

그리고 다른 흐름으로 실시간 처리쪽에 데이타를 전송해서 실시간 집계를 해서 실시간 집계 테이블을 만든다.

마지막으로, 이 두개의 뷰를 합쳐서 통계를 만든다.

배치뷰는 배치로 돌때만 쓰기가 가능하고 평상시에는 데이타를 읽기만 가능하게 한다. 이를 통해서 데이타가 변경되거나 오염(Corrupt)되는 것을 막을 수 있다.

실시간 뷰는 실시간으로 데이타를 쓰고, 읽을 수 있는 시스템을 사용한다.

위의 문제 정의 예제에서는 컬럼의 개수를 카운트 정도하는 간단한 예를 들었지만, 실제 빅데이타 분석에서는 단순 통계뿐 아니라 복잡한 수식이나 다단계를 거쳐야 하는 데이타 파일의 가공이 필요하기 때문에 복잡한 프로그래밍이 가능한 처리(배치/실시간)이 필요한데, 이 처리 계층에는 프로그램을 이용하려 알고리즘을 삽입할 수 있어야 한다.

이러한 특성에 맞춰서 각 데이타 처리 흐름에 솔루션을 맵핑 해보면 다음과 같다.



<그림 8. 람다 아키텍쳐에 대한 솔루션 맵핑> 


저장소는 대량의 데이타를 저비용으로, 안정성 있게 (유실이 없게) 저장할 수 있는 것이 필요하다. 그리고 이런 대량의 데이타를 배치로 처리할 때 되도록이면 빠른 시간내에 복잡한 알고리즘을 적용해서 계산할 수 있는 계층이 필요한데, 이러한 솔루션으로 제시되는 솔루션이 하둡의 HDFS(Hadoop File System)과 하둡의 MR (Map & Reduce)이다.

이렇게 계산된 배치 데이타를 저장할 장소가 필요한데, 하둡에서는 이런 데이타를 저장하고 고속으로 액세스할 수 있도록 HBase라는 NoSQL을 제공한다.

실시간 처리는 복잡한 알고지즘을 빠르게 데이타를 처리할 수 있는 솔루션이 필요한데, 대표적으로 Apache Storm등이 있으며, 빠른 읽기와 쓰기를 지원해야 하기 때문에, Redis와 같은 In-memory 기반의 NoSQL이 적절하게 추천되고 있다.

일반적으로 람다 아키텍쳐를 소개할때, 제안되는 솔루션의 형태이기는 하나, 람다 아키텍쳐는 특정 솔루션을 제안하는 아키텍쳐이기 보다는 데이타의 처리 기법을 소개하는 솔루션에 종속성이 없는 레퍼런스 아키텍쳐이다.

그래서 다른 솔루션 조합을 고려해볼 수 있는데, Dr.dobbs (http://www.drdobbs.com/database/applying-the-big-data-lambda-architectur/240162604)

에 소개된 솔루션 조합과 필자가 추천하는 조합을 추가해서 보면 다음과 같다.


<그림 9. 람다 아키텍쳐의 솔루션 조합>


여기서,필자가 Dr.Dobbs의 추천 솔루션 이외에, 배치 뷰와 실시간 뷰 쪽에, RDBMS를 추가하였는데, 배치뷰에 추가한 Amazon RedShift의 경우 아마존 클라우드 서비스에서 제공되는 Postgres 기반이 서비스로, 최대 16PB(페타바이트)까지의 용량을 지원한다. 이미 빅데이타라고 부를만큼의 충분한 데이타 사이즈를 지원할 뿐더라, RDBMS 기반의 SQL을 이용하여 유연한 데이타 조회가 가능하며, 리포트를 출력하기 위한 기존의 BI 툴과도 호환이 잘되서 많은 개발에 관련된 부분을 덜 수 있다. 실제로 통계 리포팅에서 가장 많은 시간이 소요되는 작업이, 비즈니스쪽 요구에 맞는 리포트를 만드는 작업이다.어떤 테이블과 그래프를 이용해서 데이터에 대한 의미를 보여줄 지는 단순한 리포팅 작업이라고 치부하기에는 매우 중요한 작업이며, 다양한 비즈니스 요건에 맞는 뷰를 보여 주기 위해서는 BI툴과의 연동은 많은 장점을 제공한다.

위에서 설명한 람다 아키텍쳐를 계층(Layer)로 나눠서 소개 하면 다음 그림과 같다.

실시간 데이터를 처리하는 부분을 스피드 레이어라고 부르며, 배치 처리는 배치 저장소와 배치 처리 부분을 배치 레이어라고 명명하고, 배치에 의해서 처리된 요약 데이터를 제공하는 부분을 서빙 레이어(Serving Layer)라고 한다.

 


<그림 10. 계층별로 추상화된 람다 아키텍쳐>

배치 레이어의 의미

배치 레이어의 저장소에는 가공전의 원본 데이터를 모두 저장한다. 데이터가 처리된 후에도 저장소에 데이터를 삭제 하지 않는다.

이렇게 원본 데이터를 저장함으로써, 배치 뷰의 데이터가 잘못 계산되었거나, 유실 되었을때, 복구가 가능하고, 현재 데이터 분석에서 없었던 새로운 뷰(통계)를 제공하고자 할 때 기존의 원본 데이터를 가지고 있음으로써, 기존 데이터에 대해서도 새로운 뷰의 통계 분석이 가능하다.


람다 아키텍쳐의 재구성

RDBMS를 활용한 유연성 증대 방안

이러한 람다 아키텍쳐는 대용량 데이터 처리와 실시간 정보 제공을 위한 장점을 가지고 있음에도 불구하고 대부분 하둡이나 NOSQL등의 솔루션을 조합해서 구현하는 경우가 대부분이기 때문에, 유연성 측면에서 문제점을 가지고 있다.

예를 들어 배치 뷰를 HBase를 사용하고, 실시간 뷰를 Redis를 사용할 경우, 상호 솔루션간 데이터 조인이 불가능할 뿐더러, 인덱스나 조인,그룹핑, 소팅 등이 어렵다. 이러한 기능이 필요하다면 각각 배치 처리와 실시간 처리 단계에 추가적으로 로직을 추가해서 새로운 뷰를 만들어야 한다.

쉽게 설명하면, 일반적인 NoSQL은 키-밸류 스토어의 개념을 가지고 있다.

그래서, 위의 그림3과 같은 테이블이 생성되었다 하더라도, 특정 컬럼 별로 데이터를 소팅해서 보여줄수 가 없다. 만약 소팅된 데이터를 표현하고자 한다면, 소팅이 된 테이블 뷰를 별도로 생성해야 한다.

참고 : NoSQL 데이터 모델링 패턴

http://bcho.tistory.com/665 , http://bcho.tistory.com/666

그래서 이런 문제점을 보강하기 위해서는 위에서도 잠깐 언급하였듯이 실시간 뷰와 배치 뷰 부분을 RDBMS를 사용하는 것을 고려해볼 수 있다. 쿼리에 특화된 OLAP 데이터 베이스를 활용하는 방법도 있고, 또는 HP Vertica 등을 활용할 수 있다. (HP Vertica는 SQL을 지원하지만, 전통적인RDBMS가 데이터를 행 단위로 처리하는데 반하여, Vertica는 데이터를 열 단위로 처리해서 통계나 쿼리에 성능이 매우 뛰어나다. 유료이지만 1테라까지는 무료로 사용할 수 있으니 뷰 테이블 용도 정도로 사용하는데는 크게 문제가 없다.)


데이터 분석 도구를 이용한 새로운 분석 모델 개발

분석 통계 데이터를 제공하다 보면, 저장소에 저장된 원본 데이터를 재 분석함으로써 추가적인 의미를 찾아낼 수 있는데, 이 영역은 데이터 과학자의 영역으로, 저장소에 있는 데이터를 통해서 새로운 데이터 모델을 추출해 내는 방식이다.

예를 들어, 글읽기 이벤트와 글쓰기 이벤트간의 상관 관계를 파악해내거나, 요일별 이벤트 변화량등을 분석해낼 수 있는데,

  1. 이 저장소에 R이나 MetLab과 같은 데이터 분석 도구를 이용하여, 샘플(표본) 데이터를 추출해서 데이터의 상관 관계를 파악해보고,

  2. 이러한 분석을 통해서 새로운 통계 모델을 설계하고 검증해볼 수 있다.

  3. 만약 이러한 모델이 적절하다면 알고리즘을 구현하고 이를 빅데이타 엔지니어에게 넘겨 준다.

  4. 빅데이타 엔지니어는 데이터 과학자에게서 받은 알고리즘을 람다 아키텍쳐의 각 레이어에 배치된 솔루션에 알맞은 형태로 구현한다.


 


<그림 11. 새로운 데이터 모델의 개발>

이러한 과정의 반복을 통해서, 분석 시스템은 지속적으로 발전되어가면서 데이터에 대한 더 많은 인사이트를 제공할 수 있게 된다.


결론

간단하게나마 람다 아키텍쳐에 대해서 알아보았다.

람다 아키텍쳐는 꼭 빅데이타에 적용하거나, 또는 하둡을 이용해야 하는 아키텍쳐가 아니다. RDBMS나 CSV 파일 등, 어떤 데이터 형태라도 기본은 배치를 이용한 집계 테이블과 실시간 뷰 테이블을 조인한다는 개념이기 때문에, 솔루션에 억메이지 말고, 적절한 시나리오를 찾아서 적용할 수 있도록 하면 좋겠다.


참고 : 

http://www.drdobbs.com/database/applying-the-big-data-lambda-architectur/240162604

http://www.infoq.com/articles/lambda-architecture-scalable-big-data-solutions