2017년 6월 20일 화요일

정리: A Few Hidden Treasures in Java 8

유투브의 다음 영상에서 얘기한 내용들을 정리해본다.
https://www.youtube.com/watch?v=GphO9fWhlAg&list=WL&index=10

영상에 등장하는 인도인 강사가 나오는 강의를 몇 개 상당히 재밌게 본 기억이 있다. 유용한 내용도 많아 도움이 많이 된다.

강의 내용을 간단히 정리하면 다음과 같다.

1. string joining


List에 값을 넣고 이를 한 문장으로 해서 보여줄 때 ','를 사용하여 단어를 구분할 수 있다.
예제는 다음과 같다.

>>Tom, Jerry, Jane, Jack

위와 같이 표현하려고 할 때, 기존의 코드에서는 for문을 사용해 마지막에는 ','가 들어가지 않게 if문을 사용하는 방법을 사용한다.

Java 8 에서는 joining을 사용해서 이를 간단하게 처리할 수 있다.

import java.util.*;
import static java.util.stream.Collectors.*; 
public class Sample {
   public static void main(String[] args) {
      List names = Arrays.asList("Tom", "Jerry", "Jane", "Jack");
      System.out.println(
         names.stream()
            .map(String::toUpperCase())
            .collect(joining(", ")));
   }
}

2. static interface methods


interface에서 static method를 선언하여 사용할 수 있는 기능이다. 예제 코드는 다음과 같다.

import java.util.*;
import static java.util.stream.Collectors.*;

public class Sample {
   interface Util {
      public static int numberOfCores() {
         return Runtime.getRuntime().availableProcessors();
      }
   }

   public static void main(String[] args) {
      System.out.println(Util.numberOfCores());
   }
}

3. default methods


interface는 보통 메소드의 선언만 있지 구현체는 없다. 그러나 default method를 사용하면 해당 메소드에 구현체를 넣을 수 있다. 다음 예제를 보면 interface의 메소드에 "default"를 주어 메소드를 구현한 것을 볼 수 있다. 컴파일 시, 에러가 발생하지 않고 정상적으로 컴파일이 된다.

import java.util.*;
import static java.util.stream.Collectors.*;

interface Fly {
   default void takeOff() { System.out.println("Fly::takeOff"); }
   default void turn() { System.out.println("Fly::turn"); }
   default void cruise() { System.out.println("Fly::cruise"); }
   default void land() { System.out.println("Fly::land"); }
}

public class Sample {   
   public static void main(String[] args) {
      System.out.println("OK");
   }
}

또한 default 메소드에서 다른 메소드를 호출하게 하여 해당 인터페이스를 상속하여 구현한 메소드를 호출할 수 있게 할 수 있다.

...
default void land() { System.out.println("Fly::land");  getState();}
int getState();
...

default method에는 다음과 같은 4가지 Rule이 있다.
  1. you get what is in the base interface
  2. you may override a default method
  3. if a method is there in the class hierarchy then it takes precedence
  4. if there is no method on any of the classes in the hierarchy, but two of your interfaces that implements has the default method to solve this use rule 3.
위 Rule 1은 다음과 같다.

import java.util.*;
import static java.util.stream.Collectors.*;

interface Fly {
   default void takeOff() { System.out.println("Fly::takeOff"); }
   default void turn() { System.out.println("Fly::turn"); }
   default void cruise() { System.out.println("Fly::cruise"); }
   default void land() { System.out.println("Fly::land"); }
}

interface FastFly extends Fly {
}

class SeaPlane implements FastFly {
 
}

public class Sample {
   public void use() {
      SeaPlane seaPlane = new SeaPlane();
      seaPlane.takeOff();
      seaPlane.turn();
      seaPlane.cruise();
      seaPlane.land();
   }
   public static void main(String[] args) {
      new Sample().use();
   }
}

위 코드의 결과 "Fly::takeOff"등 Fly 인터페이스에서 구현된 내용들이 출력된다. 이것이 규칙 1번에 해당된다.

Rule 2번은 defaut method를 override하는 속성을 얘기한다.

...
interface FastFly extends Fly {
   default void takeOff() { System.out.println("FastFly::takeOff") };
}
...

위와 같이 코드를 적용할 경우, override한 메소드에서 "FastFly::takeOff"를 출력하게 된다.

Rule 3번은 class hierarchy에 해당 메소드가 있으면 그걸 사용한다는 의미이다.
예제 코드를 보면

...
class Vehicle {
   public void land() { System.out.println("Vehicle::land"); }
}

class SeaPlane extends Vehicle implements FastFly {
 
}
...

위와 같은 코드일 경우 SeaPlane 인스턴스에서 land를 호출 시, "Vehicle:land"가 출력된다.

Rule 4번은 class hierarchy에 메소드가 없고 두 개의 interface에 해당 메소드가 모두 구현되어 있을 경우의 해결책으로 Rule 3을 사용하라는 의미이다.

예제코드는 다음과 같다.

...
class Sail {
   public void cruise() { System.out.println("Sail::cruise"); }
}

class SeaPlane extends Vehicle implements FastFly, Sail {
 
}
...

위 코드에서 FastFly와 Sail을 동시에 implements를 하면 cruise메소드로 인해 충돌이 발생하여 컴파일 에러가 발생한다. 이와 같을 경우 Rule 3을 활용하여 다음과 같이 할 수 있다.

...
class SeaPlane extends Vehicle implements FastFly, Sail {
   public void cruise() {
      System.out.println("SeaPlane::cruise");
   }
}
...

그러면 에러가 발생하지 않고 정상적으로 기능하게 된다. 하지만, FastFly의 cruise 메소드를 호출하려면 다음과 같이 한다.

...
class SeaPlane extends Vehicle implements FastFly, Sail {
   public void cruise() {
      System.out.println("SeaPlane::cruise");
      FastFly.super.cruise();
   }
}
...

위 코드에서 super를 사용한 이유는 super를 사용하지 않으면 static 메소드를 호출하려고 하기 때문이다. super를 사용해야 default method를 호출할 수 있다.

4. sorting


Java 8에서는 좀 더 추상화된 기능을 통해 손쉽게 소팅하게 해준다.
다음 예제 코드를 보자.

import java.util.*;
import static java.util.Comparator.comparing;

public class Sample {
   public static List createPeople() {
      return Arrays.asList(
         new Person("Sara", Gender.FEMALE, 20),
         new Person("Sara", Gender.FEMALE, 22),
         new Person("Bob", Gender.MALE, 20),
         new Person("Paula", Gender.FEMALE, 32),
         new Person("Paul", Gender.MALE, 32),
         new Person("Jack", Gender.MALE, 2),
         new Person("Jack", Gender.MALE, 72),
         new Person("Jill", Gender.FEMALE, 12)
      );
   }

   public static void printSorted(List people, Comparator comparator) {
      people.stream()
                 .sorted(comparator)
                 .forEach(System.out::println);
   }

   public static void main(String[] args) {
      List people = createPeople();
   
      printSorted(people, comparing(Person::getName));
   }
}

위와 같은 예제를 실행하면 이름의 알파벳 순서대로 출력이 된다. (Bob, Jack, Jack, Jill.. 순으로)

그런데 소팅을 나이에 따라 하고 싶으면 다음과 같이 하면 된다.

...
printSorted(people, comparing(Person::getAge));
...

그러면 나이가 가장 젊은 것부터 순서대로 정렬된다.

여기에서 더 나아가서 나이로 정렬한 다음에 나이가 동일한 데이터가 다수 있으면 그 중에서도 이름으로 정렬을 해야할 경우가 있다.

이때엔 다음과 같이 comparing을 사용한다.

...
printSorted(people, comparing(Person::getAge).thenComparing(Person:getName));
...

정렬순서를 반대로 바꾸고 싶으면 reversed 를 호출해주면 된다.

...
printSorted(people, comparing(Person::getAge).thenComparing(Person:getName).reversed());
...

이와 같이 소팅을 참 쉽게 처리할 수 있다.

5. grouping


4의 예제 코드를 가지고 아래의 코드를 추가하여 나이 데이터로 그룹화 할 수 있다.

import static java.util.stream.Collectors.*;
...
   public static void main(String[] args) {
      List people = createPeople();
      System.out.println(
         people.stream()
                    .collect(groupingBy(Person::getAge));
   }
...

위와 같이 하면 나이가 같은 데이터를 하나의 그룹으로 묶어서 처리하게 된다.
==> 32=[Paula -- FEMALE -- 32, Paul -- MALE -- 32], 2=[Jack -- MALE -- 2], ...

여기에서 그룹화된 데이터 중, 이름만을 사용하겠다고 하면 다음과 같이 할 수 있다.

      System.out.println(
         people.stream()
                    .collect(groupingBy(Person::getAge,
                       mapping(Person::getName, toList())));

그러면 결과는 다음과 같다.
==> 32=[Paula, Paul], 2=[Jack], ...

6. Combining Predicates and Functions


Predicate를 Function을 사용해서 사용하는 법과 다수의 Predicate를 결합하여 사용하는 방법에 대하여 알아보자.

예제 코드는 다음과 같다.

import java.util.*;
import java.util.function.Predicate;

public class Sample {
   public static void print(int number, Predicate predicate, String msg) {
       System.out.println(number + " " + msg + ":" + predicate.test(number));
   }
 
   public static void main(String[] args) {
      Predicate isEven = e -> e % 2 == 0;
   
      print(5, isEven, "is even?");
   }

}

위와 같은 예제 코드를 사용하여 Predicate를 Function과 함께 사용할 수 있다.
출력된 결과는 다음과 같다.
==> 5 is even?:false

여기서 Predicate 여러개를 and, or 와 같은 연산을 적용하여 함께 사용할 수 있다.

...
   public static void main(String[] args) {
      Predicate isEven = e -> e % 2 == 0;
      Predicate isGT4 = e -> e > 4;
   
      print(5, isGT4.and(isEven), "is > 4 && is even?");
      print(5, isGT4.or(isEven), "is > 4 && is even?");
   }
...


7. Map's convenience functions


Map이 제공하는 사용하기 편한 function들을 사용하라는 내용이다.

Map sqrt = HashMap();

위와 같은 Map 인스턴스가 있을 때, sqrt에 4의 제곱근 값 2가 없을 경우 이를 넣어주는 코드를 보통 생각하면

if( !sqrt.containsKey(4))
   sqrt.put(4, Math.sqrt(4));

와 같이 코드를 만들 수 있다. 그런데 이런 경우엔 Map이 가지고 있는 함수를 사용하면 쉽게 할 수 있다.

sqrt.computeIfAbsent(4, Math::sqrt);

8. Parallelizing streams


stream으로 연산 처리 시, parallel 하게 처리하는 부분에 대해 설명한다.

import java.util.*;

public class Sample {
   public static int doubleIt(int number) {
      System.out.println(number + " : " + Thread.currentThread());
      return number * 2;
   }

   public static void main(String[] args) {
      List numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
   
      System.out.println(
         numbers.stream()
                       .map(Sample::doubleIt)
                       .reduce(0, Integer::sum));
   }
}

위와 같은 코드를 실행했을 때, 하나의 Thread로 순차적으로 값을 2배로 만들고 값을 합산한다.
이를 parallel 하게 사용하려면 다음과 같이 코드를 작성한다.

...
      System.out.println(
         numbers.parallelStream()
                       .map(Sample::doubleIt)
                       .reduce(0, Integer::sum));
...

위와 같이 호출할 경우, Thread를 ForkJoinPool을 사용하여 parallel하게 처리한다. 즉, 시스템의 코어 수만큼의 Thread를 사용하여 연산을 처리하게 된다.

여기서 thread의 개수를 더 많이 늘려서 처리를 하고 싶을 경우엔 옵션을 사용해 JVM 레벨에서 처리할 수 있다.
java.util.concurrent.ForkJoinPool.common.parallelism=100

그러면 Thread 100개를 가지고 연산을 처리할 수 있다.

위 옵션이 없을 경우엔 시스템의 코어 개수(예로 8개라면)만큼의 Thread로 연산을 처리하게 되므로 위 코드의 numbers에 들어간 값이 100개라면 한 번에 8개씩 처리가 된다. 그러나 옵션을 사용해 100개의 Thread를 사용하면 한 번에 모두 처리할 수 있다. 



정리

이상으로 정리를 해보았는데 왠만한 내용은 모두 들어가 있는 듯하다. 그래도 동영상을 보고 싶으신 분은 아래 url로 가면 된다.
https://www.youtube.com/watch?v=GphO9fWhlAg&list=WL&index=9


2016년 12월 18일 일요일

HBase write 성능 튜닝 - Kafka topic partition 개수를 늘려서

대체적인 구성은 다음과 같이 이전에 잡아놓은 구성과 같다.
1. Flafka를 사용.
2. Kafka partition 개수에 맞게 flume 설정의 kafka sink 개수와 kafka source, hbase sink 개수를 설정.
3. Table을 pre-split 한다. (16개로 pre-split 하였음.)
다른 점은 HBase 설정을 변경한 점도 있지만 그것 외에 다른 점은 다음과 같다.
1. Kafka topic의 partition 개수를 edge node들의 총 디스크 수보다 크게 잡는다.
      - Edge node가 3개에 총 15개의 디스크가 있다. 이에 따라 partition 개수를 20개로 주었다.
      - 디스크 총 개수에 맞춰서 Partition개수를 맞추는 것은 놀고 있는 디스크가 생기기 않게 하기 위함이다.
전에는 최대 write 성능이 worker node를 3대로 하든 6대로 하든 초당 16만~17만 정도였다. (WAL을 끄면 30만)
그러나 위와 같이 kafka partition을 늘려주니 hbase 성능이 초당 30만 건 정도의  write할 수 있었다. 

HBase table pre-split region 개수 구하기

HBase에 write를 하다보면 어떤 region에는 write를 하지 않는 경우가 생기게 된다. 
그게 하나 이상의 노드가 되면 해당 노드들은 작업을 하지 않게 되고 다른 노드에 write가 몰리면서 부하가 생기게 된다.
이처럼 write 작업이 되지 않는 구멍이 안생기도록 하기 위해 hbase table의 region을 미리 분할하는데 이를 pre-split이라고 한다.
그러면 pre-split하기 적절한 region 개수는 어떻게 구해야 할까?
그것을 다음과 같은 방식으로 할 수 있다.
우선 다음과 같은 hbase의 설정 값을 계산식에서 사용한다.
1. Region 서버의 자바 힙 크기 = 32GiB
2. hbase.regionserver.global.memstore.upperLimit =0.4
3. hbase.hregion.memstore.flush.size = 128MiB
위의 1과 2의 값을 곱하면 region 서버의 자바 힙에서 write를 위해 사용하는 힙 크기를 구할 수 있다.
이를 memstore에서 flush 하는 크기로 나누면 region 개수를 구할 수 있다.
( 1 * 2) / 3 = 102.4
따라서 위의 값을 가지고 연산하면 102.4가 나오므로 약 102개의 region으로 나누는게 좋다는 걸 알 수 있다.

2016년 11월 30일 수요일

mybatis-guice에 bonecp 연동

mybatis-guice는 mybatis sql mapper와 google guice를 함께 사용하는 프레임워크다.

제 생각대로 mybatis-guice의 장점을 간단하게 정리하자면 다음과 같다.

mybatis는 알려진 대로 sql문을 xml 파일로 따로 관리할 수 있게 해주는 라이브러리인데 여기에 guice를 사용하여 xml을 사용하지 않고 java코드에서 sql문을 관리할 수 있게 해준다. xml 파일로 관리 하는 것 자체가 짜증스럽고 mybatis로 시작하는 것 자체가 어렵게 느껴진다면 mybatis-guice가 좀 더 간단하고 편안하게 다가올 수 있다.

다음 url에서 mybatis-guice에 대한 더 많은 내용을 확인할 수 있다.
http://www.mybatis.org/guice/


이번 글에서는 이 mybatis-guice에 DB connection pool 라이브러리인 bonecp를 연동하는 방법에 대해 간다하게 알아보겠다. mybatis-guice 홈페이지에 자세한 내용이 없어 여기에 간단하게 남기려고 한다.



위의 코드에서와 같이 간단하게 bonecp를 연동해서 사용할 수 있다.
간단하지만 홈페이지엔 어떻게 하는 건지 잘 나와 있지 않아 쪼~금 애를 먹었다. 암튼 알아두면 용이할 듯.

나머지 mybatis-guice를 사용하는 방법은 홈페이지를 참고하면 된다.

2016년 11월 6일 일요일

삼성전자 spark-cep 테스트

삼성전자 spark-cep란?

삼성전자에서 오픈소스로 만든 spark 기반의 cep 엔진이다. Esper 처럼 쿼리문에 WINDOW, SLIDE 기능이 있어 5분 또는 10분 간격으로 처리 구간을 지정하여 쿼리를 실행할 수 있게 해준다.
Github에 오픈소스로 있으면 다음 url에서 확인할 수 있다.

테스트 목적

삼성의 spark-cep를 테스트한 목적은 현재 이글루의 rule base 엔진을 spark-cep를 사용하여 대체가능한 지에 대해 확인하기 위해서이다.
이를 위해 다음과 같은 기술적인 부분들에 대한 확인이 필요하다.
  1. 실시간으로 다수의 rule을 처리할 수 있는가?
  2. 처리할 수 있다면 그 성능은 빠른가?
  3. Rule의 적용 및 수정이 용이한가?
따라서 이 테스트의 목적은 위 세가지에 대해 기능 및 성능을 확인하여 spark-cep를 활용할 수 있는 지를 판단하기 위함이다.

테스트 구성

spark-cep에서는 레디스를 사용하므로 레디스를 구성해야 했다.
레디스 클러스터를 다음과 같이 노드 sn1, sn2, sn3 세 개에 구성하였다.


그런데 테스트 시에 spark-cep에서 레디스 클러스터에 접속할 수가 없어 확인해보니 spark-cep 자체에서 레디스 클러스터를 지원하지 않게 되어 있었다.
레디스 클러스터 구성을 삭제하고 다음과 같이 하나의 노드만 사용하도록 하였다.


테스트 결과

우선, 위 테스트 목적의 세 가지 항목에 대한 답은 다음과 같다.
  1. 없다. 구조 상 1개만 가능하다. 아니면 하나의 쿼리에 여러가지를 처리할 수 있게 만들어야 한다.
  2. 1에서 처리가 되지 않으므로 성능 테스트를 진행할 수 없었다.
  3. Standard한 sql 문을 사용하므로 적용 및 수정에 용이하다.
위 내용에 대해 몇 가지 덫붙이자면, spark-cep란 오픈소스로는 WINDOW와 SLIDE 쿼리문을 사용하여 시간간격을 지정해서  데이터를 분석할 수 있지만 다수의 쿼리를 처리할 수는 없었다.
그리고 레디스를 활용하여 데이터 분석에 대해 성능적으로 도움을 주는 부분이 있을 거라 생각했지만 해당 부분은 spark의 원래 기능인 DataFrame을 그대로 사용하고 있어 spark의 원기능이 제공해주는 성능에서 크게 벗어나지 않고 있음을 알 수 있었다.
레디스는 분석한 결과를 저장하는 용도로 사용하고 있다.
spark 버전도 1.5 버전을 사용하고 있어 현재 cloudera 장비에 설치된 1.6 버전에서는 구동이 잘 되지 않았다. 그래서 장비에 spark 1.5 버전을 따로 올려서 테스트하였다.
spark-cep 코드를 컴파일할 때에도 1.6 버전을 사용하면 에러가 발생하여 빌드를 할 수가 없었다.
현재 spark가 2.0 버전에서는 structured streaming 기능이 추가되어 spark-cep에서 제공하는 WINDOW, SLIDE 쿼리문 같은 기능을 사용할 수 있게 되었다. 이런 상황에서 spark를 활용한 실시간 분석을 사용한다면 2.0에서 코드를 개발해서 하는 게 현재 spark-cep의 분석처리 성능보다 더 나을 거라고 보인다.
spark-cep도 코드를 마지막으로 커밋한 게 2015년 11월 4일로 1년이 넘었다. 이를 봐서는 여기서 더 발전시켜 나갈 의향이 없어보인다.
또한 머신러닝으로 실시간 이상 현상을 탐지하는 보안 기술을 가진 제품이 나오는 추세에 cep를 사용하는 것 자체가 시대에 뒤떨어지고 기술적, 보안적으로 확장성이 떨어진다고 생각된다. 

2016년 10월 5일 수요일

Hadoop 3.0 새로 추가된 기능들에 대해 살펴보자.

Hadoop 3.0 alpha 버전이 릴리즈되었다.
3.0에 추가된 새로운 기능들과 그 특성에 대해 한번 알아보자.

Minimum required Java version increased from Java 7 to Java 8

모든 Hadoop JAR 파일들이 Java 8으로 컴파일되었다. Java 7을 아직 사용 중이라면 Java 8으로 업그레이드해야 한다.

Support for erasure encoding in HDFS

Erasure encoding은 replication에 비해 storage를 크게 절약하면서도 데이터를 견고하게 저장할 수 있게 한다. 디폴트 3x 의 오버헤드를 가지는 기존 replication 방식에 비해 1.4x 정도로 오버헤드가 줄게 된다.
방식은 RAID에서 사용하는 Erasure encoding 방식과 동일하다. 파일의 데이터 저장은 3개의 디스크에 나누어서 이뤄지고 3개의 디스크에 저장된 bit들의 parity bit를 따로 저장한다. 나중에 3개의 디스크 중 하나에서 장애가 발생하면 나머지 디스크들에 있는 bit들과 parity bit를 사용하여 장애가 발생한 디스크의 bit를 복구하여 사용하는 방식이다.
이 방식을 기존의 서비스를 운영하는 시스템에 적용하려면 기존의 모든 데이터를 Erasure encoding을 적용하여 구성해야 하므로 그에 따른 비용을 고려해서 적용해야할 것으로 보인다. 그럼에도 불구하고 적용을 한 이후에 기존에 사용하던 시스템의 저장공간이 2배로 늘어나게 되므로 하드웨어 비용과 확장성을 봤을 때 정말 매력적인 기능이 되지 않을까 싶다.

YARN Timeline Service v.2

YARN Timeline Service v.2 에 대해 살펴보기 전에 YARN Timeline Server가 어떤 역할을 하는 지에 대해 알아보자.
YARN Timeline Server는 YARN에서 실행되는 application의 현재 정보 및 히스토리 정보를 관리한다.
  • 완료된 애플리케이션들에 대한 일반적인 정보 관리
    • 애플리케이션 레벨의 일반적인 정보로 queue-name, 사용자 정보, 컨테이너 리스트 등등으로 리소스 매니저에 의해 history-store에 저장된다. 그리고 web-UI에서 보여준다.
  • 실행 중이거나 완료된 애플리케이션의 per-framework 정보
    • 애플리케이션 또는 프레임워크에 특정한 정보를 의미한다. Hadoop MapReduce를 예로 들면 map task, reduce task의 개수 등이 이에 해당된다.
    • Timeline Server에 TimelineClient를 사용하여 특정 정보를 올릴 수 있고 REST API를 통해 정보를 쿼리할 수 있다.
YARN Timeline Server가 어떤 역할을 하는 지를 알아보았다. 그러면 v.2에 대해 알아보자.
v.2는 v.1에 비해 scalability와 reliability가 향상되었다. 그리고 flow와 aggregation 정보를 제공함으로써 Usability 를 향상시켰다.

  • Scalability
    • v.1에서는 writer/reader 그리고 storage가 한 개의 인스턴스로 제한되어 작은 클러스터에서 더 크게 확장할 수가 없었다. v.2에서는 확장되고 분산된 writer와 storage를 사용한다.
    • Collector(Writer)와 reader를 분리한다. Collector는 각 YARN 애플리케이션마다 한 개가 할당된다. Reader는 분리된 인스턴스로 REST API를 통해 쿼리처리만 하도록 한다.
    • Backing storage로 HBase가 사용된다. 데이터 read/write는 HBase를 통해 이뤄진다.
Timeline Service v.2 architecture

  • Usability improvements
    • 사용자들은 YARN 애플리케이션의 flows 혹은 YARN 애플리케이션들의 논리적 그룹 수준의 정보를 얻기를 원한다. v.2에서는 이러한 정보들을 제공해준다.
    • 다음 그림은 각 YARN 애플리케이션 간에 관계를 보여준다.
Flow Hierarchy

그러나 아직은 security 처리가 되지 않아 테스트 용도로만 사용하기를 권하고 있다.

Shell script rewrite

Hadoop shell script가 오랫동안 가지고 있던 버그를 수정함과 동시에 새로운 기능들이 추가되었다. 주의할 점은 기존 설치된 쉘 버전과는 호환성이 유지되지 않는다는 점이다.
자세한 사항은 Unix Shell GuideUnix Shell API 문서를 참고하기 바란다.

MapReduce task-level native optimization

Map Collector의 output을 내는 부분을 C/C++로 만들어 JNI를 통해 사용하도록 수정되었다. 본 수정으로 인해 셔플이 많이 일어나는 작업같은 경우는 30% 이상의 성능을 향상시키게 된다.

Support for more than 2 NameNodes.

기존의 HDFS의 고가용성은 하나의 NameNode에 하나의 Standby NameNode를 두고 edits 로그를 세 개의 JournalNode들에 저장하는 방식으로 사용되었다. 
그러나 좀 더 높은 수준의 fault-tolerance가 요구되어지면서 다수의 Standby NameNode를 동시에 운영할 수 새로운 기능이 추가되었다. 예를 들어 3개의 NameNode와 5개의 JournalNode를 구성하면 해당 클러스터는 두 개 노드의 장애에 견딜 수 있게 된다. 
- Active NameNode는 1개이다. -

Default ports of multiple services have been changed.

Hadoop 서비스에서 사용되던 디폴트 포트들이 수정되었다. Linux의 ephemeral port 범위(32768-61000)를 사용할 때, 서비스가 올라오면서 다른 애플리케이션과의 충돌로 포트에 바인드를 못하는 경우가 발생한다. 충돌이 발생하지 않게 디폴트 포트들이 다음과 같이 변경되었다.
  • Namenode ports: 50470 --> 9871, 50070 --> 9870, 8020 --> 9820 
  • Secondary NN ports: 50091 --> 9869, 50090 --> 9868 
  • Datanode ports: 50020 --> 9867, 50010 --> 9866, 50475 --> 9865, 50075 --> 9864
  • KMS: 16000 --> 9600

Support for Microsoft Azure Data Lake filesystem connector

Hadoop-compatible filesystem으로 Microsoft Azure Data Lake를 지원한다. 
Azure Data Lake라는 게 대략 살펴보니 클라우드 스토리지를 제공하면서 데이터 분석을 할 수 있게 해주는 서비스로 보인다. Azure Data Lake를 Hotonworks, Cloudera 플랫폼에서도 호환이 되도록 지원하고 있다.


Intra-datanode balancer

하나의 데이터 노드에서는 여러 개의 디스크를 관리한다. 정상적인 운영에선 디스크들에 동일한 데이터량이 쓰이게 되므로 문제가 없다. 그러나 디스크를 추가하거나 교체하게 되면 해당 데이터 노드에 심각한 데이터 불균형 현상이 발생하게 된다. 이런 현상은 HDFS balancer에서 처리되고 있지 않다. HDFS balancer는 inter- 에는 신경쓰지만 intra- 부분에 신경쓰지 않는다.
위와 같은 문제점을 처리하기 위한 "hdfs diskbalacer"라는 커맨드가 추가되었다. 해당 커맨드의 자셍한 내용은 HDFS Disk Balancer에서 확인할 수 있다.


Reworked daemon and task heap management

하둡 데몬들과 맵리듀스 태스크들을 위한 힙 관리 관련 일련의 수정이 이뤄졌다.
HADOOP-10950 => 데몬 힙 사이즈를 설정하는 새로운 방법을 소개한다. 호스트의 메모리 사이즈에 근거하여 auto-tuning이 가능하다. 그리고 HADOOP_HEAPSIZE는 deprecated 되었다.
MAPREDUCE-5785 => map과 reduce의 힙 사이즈를 설정을 단순화하였다. 따라서 Java option으로 map과 reduce의 힙 사이즈 설정을 해 줄 필요가 없어졌다. 기존에 존재하고 있는 설정은 이 변경사항에 영향을 미치지 않는다.



참고자료

http://hadoop.apache.org/docs/r3.0.0-alpha1/index.html









2016년 8월 30일 화요일

내가 쓰는 git 명령어

git을 사용하다 보면 명령어를 잊어버리게 된다.

그래서 여기에 간략하게 적어두고 필요하면 다시 보고자 한다.


repository에 push 하기 => git push origin master
add된 상태의 파일을 add 취소하기=> git rm --cached filename