순서

1)    HAMA with K-Means

2)    HAMA  BSP Inside  &  Zookeeper


하마(Apache Hama) 는 빅데이터 분석(머신러닝) 을 위한 솔루션을 제공합니다. 
흔히 알고있는 하둡(Hadoop) 의 맵리듀스 또한 빅데이터 분석 하는데 사용되는데 맵리듀스의 특성상
속도 및 구현에 있어서 굉장히 비효율적인 모습을 보여주는 부분이 있는데, 그런 가려운 부분을 화끈하게
해결
해줍니다.  

하마는 기본적으로  데이터 저장은  하둡 HDFS 를  사용하며  Zookeeper 를 이용하여 분산된 락을 구현하며  
BSP / 그래프 구조를 통하여 각종 알고리즘을 구현할수있게 합니다. 

- 하둡의 맵리듀스 구조 ( 데이타 - 매핑 - 리듀싱 - 결과 저장)

맵 노드간에 혹은 리듀스 노드간에 정보 교환하지 않습니다. 오로지 맵 -> 리듀스 

 - 하마 BSP 구조 ( 데이터 - 로컬 계산 - 노드 상호간 통신 -  블럭 ) 

위의 그림에 보다시피,하둡은 Iterative 작업을 수행하기에 좋지 않습니다. 반면 하마는 그것에 최적화 되어있습니

                        - K-meas 알고리즘 적용시 머하웃( 하둡 맵리듀스 기반) 과의 속도 차이입니다.

위의 표에 보다시피 머신러닝을 할때 ,  엄청난 속도차이를 볼수있습니다. 하둡 맵리듀스 (머하웃) 와는 비교가 안되게 빠릅니다. 자신이 활용하고자 하는 목표 ( 현재 에코 시스템, 계산복잡도로 인한 속도, 러닝커브, 라이브러리 활용성) 에 따라서 선택해야함.  요즘은 딥러닝이 뜨고 있는데  딥러닝을 위해서도 충분히 활용가능하며 BSP + GPGPU 를 적극활용하면 실시간성에 더욱 가까히 갈수있겠습니다. 


K-Means 알고리즘 이란 ? 

머신러닝의 큰 바운더리로  1. 추천시스템  2. 분류  3. 군집을 들수있는데 이 알고리즘은 군집분석을 할때  활용되는 기본 알고리즘 입니다. 군집이란 , 쉽게 말해 도서관에서 여러분이 어떤 기준에 따라서 책을 그룹핑할때 하는 행동 . 그때 책을 군집화 한다라고 말할수있습니다. 군집할때는 어떤 데이터간의 유사성을 정량적으로 측정하여 수치화하는게 핵심이며, 수치화가 된후 K-Means 알고리즘으로 자동 그루핑 할수있습니다. K-Means 는 말 그래도 K 개로 그루핑하라~ 와 같은데 각 군집의 평균데이터를 구해주며 평균데이터와 비교하여 , 개개의 요소들을 특성을 규정할수있습니다.  굉장히 클리어한 알고리즘이며 간단한데에 비해 많은곳에서 주요 알고리즘으로 사용됩니다.

수행과정을  보면 

1) 임의의 K개의 군집수를 결정하고, 각 군집에 초기치 또는 군집 중심을 1개씩 할당하여 위치 설정한다.
2) 각각의 데이터에 대해 K개의 위치까지의 거리를 구하고 가장 가까운 군집에 소속시킨다.(유클리드 거리를 이용)
3) 군집으로 나뉘어진 데이터를 기준으로 새로운 군집 중앙의 위치를 최소가 되도록 재설정한다.
4) 새롭게 구한 군집 중앙의 위치가 기존과 동일하면 알고리즘 종료하고 다르면 두 번째부터 재수행한다.

이 과정을 통하여 K개의 군집으로 데이터를 구분하고 K값에 따라 clustering에 많은 영향을 받는다


[출처] K-means 알고리즘|작성자 푸키

Apache Hama 로 구현한 K-Means  

하마를 통한 K-Means 알고리즘이 하나의 컴퓨터에서 알고리즘을 구현한것과 다른 점은 오직 여러대의 컴퓨터간에 통신하면서 수행된다는 점입니다. 스텝마다 각자의 컴퓨터에서 계산을 수행한후에 다른 컴퓨터로 결과를 서로 넘겨서 재 조정되면서 알고리즘이 진행됩니다.

아래는 X,Y 좌표에서 가까운것 끼리 군집을 만들기위한 소스와 설명입니다.
먼저 소스 부터 보고 
한단계식 이미지를 통해 확인해가며 학습해보겠습니다.

1. Mean 포인트 ( 그룹의 중간 포인트, 그룹별 하나씩 있음) 

2. 일반 포인트  ( 모든  그룹으로 나누어질 값들  예) 책종류, 영화 장르구분, 다음 아고라 댓글  정치성 분류 )

아래 코드는 하마가 설치된 모든 컴퓨터에서 실행된다.

public void bsp(final BSPPeerProtocol bspPeer) throws IOException,KeeperException, InterruptedException {

	if (isMaster(bspPeer)) {    
                //  1)  초기화 ( 랜덤포인트들 생성 ,  Mean 포인트 생성) , 마스터노드에서만 실행
		masterInitialize(bspPeer); 

	}

	while (true) {    //  K-Means 알고리즘 시작 	

		bspPeer.sync();    // 2) 일단 모든 컴퓨터가 여기까지 실행될때까지 모두 대기!!!! 	
		boolean converged = processMessages(bspPeer);  // 3)다른노드로부터 온 데이터 업데이트 					

		if (converged) {    // 4) 원하는 만큼 중간값이 정해졌으면 해당 노드 (군집) 종료!!
			break;
		}	

		assignmentStep(bspPeer);  // 5) 포인트들을 Mean 포인트에 재 할당	
		// 6)  Mean 값 과 재할당된 포인트들 사이의  유클리드 거리 평균에 따라 위치 조정 
               updateStep(bspPeer);

	} 			

	double wcss = wcss(bspPeer);
	LOG.info("My WCSS is " + wcss);
	writeFinalOutput(bspPeer);	// 7) 결정된 Mean 값을 출력함 ( 하둡 HDFS 나 Hbase 에 )

}


private boolean processMessages(BSPPeerProtocol bspPeer) throws IOException {
			
	boolean converged = true;			
	BSPMessage msg;

	while ((msg = bspPeer.getCurrentMessage()) != null) {
		if (isPointMessage(msg)) {    
			addPoints(msg);    // 8) 다른 노드로부터 온 메세지가 포인트이면 삽입
		} else if (isMeanMessage(bspPeer, msg)) {
			converged = updateMeanMap(msg) && converged; // 9) 다른 노드로부터 온 메세지가 Mean 포인트일때
		} else {
			throw new RuntimeException("Unknown msg tag: " + new String(msg.getTag()));
		}
	}
			
	LOG.info("New Mean Map = " + peerMeanMap);
	return converged;
}



// 10) Mean 포인트들과 포인트들을 계산하여 , 각각의 Mean 포인트에서 가까운 포인트들끼리 그룹핑한다.
private int assignmentStep(final BSPPeerProtocol bspPeer) throws IOException {

	final Map<String, List<Point3D>> peerNewPoints = new HashMap<String, List<Point3D>>		

	for (String peer : peerMeanMap.keySet()) {
		peerNewPoints.put(peer, new ArrayList<Point3D>());
	}

	int changeCount = 0;			

	for (Iterator<Point3D> pointItr = points.iterator(); pointItr.hasNext();) {

		final Point3D obs = pointItr.next();	
		double min = Double.MAX_VALUE;
		String minPeer = null;		
		for (Map.Entry<String, Point3D> peer : peerMeanMap.entrySet()) {

			double distance = obs.distance(peer.getValue());
			if (distance < min) {
				min = distance;
				minPeer = peer.getKey();
			}
		}
		
		if (minPeer.equals(bspPeer.getPeerName())) {
			//I don't send updates for points I already own
			continue;

		}		

		//Remove the point from my collection as I no longer own it.
		pointItr.remove();
		changeCount += 1;
		peerNewPoints.get(minPeer).add(obs);	

	}

	//Notify other clusters of new points	
	for (Map.Entry<String, List<Point3D>> peerPoints : peerNewPoints.entrySet()) {

		if (peerPoints.getValue().size() == 0) {
			continue;
		}	

		LOG.info("Send " + peerPoints.getValue().size() + " to " + peerPoints.getKey());
		bspPeer.send(peerPoints.getKey(), 
		pointToByteMessage(new PointMessage(POINT_MSG_TAG, peerPoints.getValue())));
	}
	return changeCount;

}


// 10) 새로 그룹핑된 포인트들의 중간값을 계산하여 새로운 Mean 포인트를 계산한후에 브로드캐스트.
private void updateStep(BSPPeerProtocol bspPeer) throws IOException {			

		LOG.info("My point count is now: " + points.size());

		if (0 == points.size()) {
			//Catch initial case where we have no points, and thus can't change our mean.
			return;
		}
		broadcastMyMean(bspPeer, calculateCenter(points));
	}

private void broadcastMyMean(BSPPeerProtocol bspPeer, Point3D mean) throws IOException {

	final BSPMessage msg = pointToByteMessage(new PointMessage(bspPeer.getPeerName(), mean));

	for (String peer : bspPeer.getAllPeerNames()) {
		bspPeer.send(peer, msg);    // 11) 각각의 노드로 재 군집된 포인트들을 보내줍니다.
	}

}

private Point3D calculateCenter(List<Point3D> points) {  // 12) 포인트들의 센터를 다시 정합니다.
	double x = 0;
	double y = 0;
	double z = 0;
	
	for (Point3D p : points) {
		x += p.x / points.size();
		y += p.y / points.size();
		z += p.z / points.size();

	}

	return new Point3D(x, y, z);
}

자 다음은 위의 소스에서 일어나는 일을 이해하기 쉽게 그림으로 그려보았습니다.  감에 의지하여 그린것이라 거리차등이 정확하지 않습니다. :-)   시작해보지요.  

아래 그림은 소스에서 1) 번에 해당합니다. 
빨강색  / 연두색 / 노란색  포인트들이 초기 Mean (군집) 포인트 입니다. 3개의 초기점이 점점 포인트들 사이에 3개의 중간지점으로 이동될 것입니다. ( 당연히 회색 포인트들은 이동하지 않음) 


그림에서 0~5번까지의 순서에 의해 알고리즘이 진행됩니다.

소스에서 5) 6) 번에 해당합니다.  서버1에서 계산을 한후에  서버2 / 서버 3 으로  각 서버(군집) 에 해당하는 포인트들을 보내주며 

Mean 포인트들은 모든 노드가 공유합니다. (각 노드는 노드별로 자신의 포인트 들을 가지고 있게 됩니다. 즉 같은 군집들끼리 ) 

- 빨강색 / 연두색 / 노란색 점들 간의 그룹들이 설정되었습니다. (점선테두리) 

- 빨강색 그룹의 포인트들은 서버2로 이동될것이고, 노란색 그룹의 포인트들은 서버3으로 이동됩니다.

- 연두색 Mean 포인트는 위치가 재조정되며  서버2,서버3 에 조정된 위치를 알려줍니다.



자 한스텝이 진행되었습니다. 모든 노드는 각각의 노드들이 한 스텝을 완성할때까지 블럭됩니다.
모두 진행된후에는 모두 블럭을 풀고  받은 데이터로 자신의 Mean 값과 포인트들을 업데이트 합니다.

-  이동되어진 포인트들의 모습입니다.


다시 반복합니다.

- 빨강색 / 연두색 / 노란색 점들 간의 그룹들이 설정되었습니다. (점선테두리) 

- 빨강색 / 노란색 Mean 포인트들이 자신의 그룹 포인트들읜 중간값으로 이동합니다.

- 이동된 Mean 포인트들의 위치를  서로 공유합니다.


- 빨강색 과 노란색의 Mean포인트 위치가 그룹들의 중간으로 이동하였습니다.


- 빨강색 / 노란색의 Mean 포인트의 위치가 이동하고 나니,  어떤 포인트들은 연두색에 더 가까워 졌습니다.

- 연두색에 더 가까워진 포인트들은 서버1으로 보내줍니다. 


- 연두색으로 넘어간 포인트의 모습입니다.


자 먼가 그룹이 이루어져있지요? 

이렇게  블럭 - 재 그룹핑 - Mean 포인트 위치조정 -  각각의 노드로 브로드캐스트 을 반복해서 적당하게 그룹핑되면 종료합니다.

하둡 맵리듀스 기반의  K-means 알고리즘을 구현해놓은 머하웃 라이브러리를 잠깐 공부해보았는데, 머신러닝은 이런 알고리즘 구현이 어려운게 아니라,  머신러닝 학습을 시키기위해 데이터를 가공하거나,  어떤것이 군집되어야하는지 결정하는데 더 어려운거 같았습니다.  이상 K-Means 알고리즘을 살펴보았으며  저 또한  빅데이터/머신러닝에 대한 전문가가 아니기때문에  부족한 부분이 많았을거라고 생각됩니다만 이 글을 통해서 머신러닝에 대한 흥미를 일깨우는데 일조했다면 그것으로 소기의 목적을 이루었다고 생각합니다.  


https://github.com/willmore/hama-kmeans   ( 위에 예제에 사용된 소스) 

http://en.wikipedia.org/wiki/K-means_clustering

http://www.slideshare.net/udanax/k-means-29238792

https://hama.apache.org/

'HAMA' 카테고리의 다른 글

HAMA 쉘 분석  (0) 2015.09.30
HAMA 시작하기  (0) 2015.09.30


도서관에 있는 책들과 인터넷 서핑을 통해 모은 정보들을 간략하게 정리해 봅니다. 

부담없이 쭈욱 읽어가면 될듯..  프린트해서 심심할때 읽어보셔도~



자바 애플리케이션 성능 튜닝의 도(道)
http://d2.naver.com/helloworld/184615 펌

이 글은 월간 "마이크로소프트웨어" 2012년 9월호에 "자바 애플리케이션 성능 튜닝의 도(道)"라는 제목으로 실린 글입니다. 편집 과정을 거치며 일부 내용이 책에 실린 내용과 다를 수 있습니다.

자바 애플리케이션의 성능을 튜닝하는 작업은 자바 및 JVM에 대한 지식과 수많은 튜닝 기법, 다양한 환경과 상황에 대한 경험 등을 필요로 한다. 그러나 이 모든 내용을 짧은 지면에서 소개하기에는 무리이니 이 글에서는 성능 튜닝 작업의 상세한 내용보다는 튜닝에 필요한 배경 지식과 튜닝 순서, JVM의 각종 옵션 및 튜닝 접근 방법 등의 간략한 소개를 통해 성능 튜닝의 전반적인 흐름과 방법론에 대해 살펴보도록 하자. 특히 자바 애플리케이션의 여러 도메인 중에서 인터넷 서비스를 위한 웹 애플리케이션에 중점을 두고 설명해나가겠다.

모든 애플리케이션이 튜닝을 필요로 하는 것은 아니다. 충분한 성능을 내고 있다면 굳이 추가적인 노력을 들일 필요는 없다. 하지만 방금 디버깅을 마친 애플리케이션이라고 해서 항상 목표 성능만큼 동작해 줄 것이라고 기대할 수는 없고 그렇게 기준치 이하로 성능이 미달될 때 튜닝이 필요해진다. 구현 언어에 상관없이 애플리케이션을 튜닝하는 것은 상당한 전문적인 지식과 높은 집중을 요구하는 일인데다, A라는 애플리케이션을 튜닝했을 때 사용했던 방법을 B라는 애플리케이션을 튜닝할 때 재활용할 수 있는 것도 아니다. 애플리케이션마다 고유한 동작이 있고 컴퓨터 자원을 사용하는 형태가 다르기 때문이다.

애플리케이션을 튜닝하기 위해서는 애플리케이션 작성 지식보다 좀 더 근본적이고 포괄적인, 예를 들면 버추얼머신이나 OS, 컴퓨터 아키텍처 등에 대한 지식이 필요하다. 이런 지식을 바탕으로 애플리케이션 도메인에 집중해야 수월한 튜닝이 가능하다.

자바 애플리케이션 튜닝이란 경우에 따라 GC 같은 JVM 옵션 값 변경만으로 충분할 수도 있고 아예 코드를 수정해야 할 때도 있다. 어느 방법을 선택하든 우선 자바 애플리케이션 수행 과정을 모니터링해야 한다.

그래서 이 글에서는 '어떻게 모니터링을 하는가', '어떻게 JVM 옵션을 주어야 하는가', '코드 수정 필요 판단은 어떻게 하는가'를 중심으로 살펴보도록 하겠다.

자바 애플리케이션 성능 튜닝에 필요한 지식

JVM상에서 동작하는 자바 애플리케이션의 튜닝을 위해서는 JVM의 동작 과정에 대한 이해가 필요하다. 여기서 말하는 JVM 동작 과정에 대한 지식이란 크게 Garbage Collection(이하 GC)에 대한 것과 HotSpot에 대한 지식을 꼽을 수 있다. 물론 GC나 HotSpot 지식만으로 모든 자바 애플리케이션에 대한 성능 튜닝을 할 수 있는 것은 아니지만 성능에 영향을 미치는 대부분의 요소는 이 두 가지에 속한다.

JVM의 원활한 동작 환경을 만들기 위해서는 OS가 각 프로세스에 자원을 분배하는 방식에 대한 이해가 필요하다. 자바 애플리케이션 성능 튜닝을 위해서는 JVM 자체는 물론 OS나 하드웨어에 대한 이해도 필요하다는 것이다. OS 관점에서 볼 때 JVM 또한 하나의 애플리케이션 프로세스라는 점을 염두에 두도록 하자. 덧붙여 자바 언어 도메인에 대한 지식도 중요하다. Lock이나 Concurrency에 대한 이해는 물론 클래스 로딩이나 객체 생성에 대한 지식 또한 중요도가 높다.

자바 애플리케이션 성능 튜닝을 할 때는 이러한 지식들을 종합해 접근해야 한다.

성능 튜닝 과정

<그림 1>은 찰리 헌트와 비아누 존 두 사람의 공동저서인 'Java Performance'에서 인용한 순서도로 자바 애플리케이션 성능 튜닝 과정을 표현한 것이다.

javaapplication1

그림 1 자바 애플리케이션 성능 튜닝 과정

자바 애플리케이션 성능 튜닝 과정은 한 번에 통과하는 과정이 아니라 튜닝 완결까지 몇 번이고 계속 반복할 수도 있다. 기대 성능 수치 설정 또한 마찬가지다. 튜닝 과정을 통해 기대 성능 수치를 하향해야 할 때도 있고 오히려 기대 성능 수치를 상향할 때도 있다.

JVM 배포 모델이란 하나의 JVM에서 자바 애플리케이션을 동작시킬 것인지 여러 JVM에서 자바 애플리케이션을 동작시킬 것인지 결정하는 것으로 가용성, 응답 반응성, 관리 편의성 등에 따라 변경될 수 있다.

JVM이 여러 서버에서 동작하는 경우에도 한 서버에서 여러 개의 JVM을 동작하도록 하거나 서버마다 각각의 JVM을 동작하게 할 수도 있다.

물론 하나의 서버에 몇 개의 JVM이 동작할 것인가는 서버의 코어 개수와 애플리케이션의 특성 등에 따라 결정되겠지만 응답 반응성 관점에서 양자를 비교해볼 때, 같은 애플리케이션일 경우 2GB의 힙을 사용하는 경우가 8GB 크기의 힙을 사용하는 것보다 풀 GC에 걸리는 시간이 짧아 응답 반응성에 유리하다. 하지만 8GB 힙을 사용하면 2GB보다 풀 GC 발생 간격이 그만큼 줄어들 것이고 내부 캐시를 사용하는 애플리케이션이라면 히트율을 높여 응답 반응성을 높일 수 있다.

즉 하나의 장점을 선택했을 때 그 선택에 뒤따르는 단점을 극복할 수 있는 방법을 고려해야 적합한 배포 모델을 결정할 수 있다.

JVM 선택이란 32bit JVM을 사용할 것이냐 64bit JVM을 사용할 것이냐에 대한 결정이다. 동일 조건이라면 32bit JVM을 선택하는 것이 좋다. 32bit JVM이 64bit JVM보다 수행 성능이 좋기 때문이다. 32bit JVM은 논리적 최대 사용 가능 힙 크기가 4GB로, 이보다 큰 크기의 힙을 사용할 필요가 있을 때 64bit JVM을 사용하는 것이 좋다(단 32bit OS/64bit OS 모두 실제 사용 할당 크기는 2~3GB 정도다).

표 1 성능 비교 자료(출처)

BenchmarkTime [sec]Factor
C++ Opt231.0x
C++ Dbg1978.6x
Java 64-bit1345.8x
Java 32-bit29012.6x
Java 32-bit GC1064.6x
Java 32-bit SPEC GC893.7x
Scala823.6x
Scala low-level672.9x
Scala low-level GC582.5x
Go 6g1617.0x
Go Pro1265.5x

이제 작성한 애플리케이션을 가동해 성능을 측정하자. 이 과정에서 시스템 모니터링 도구나 프로파일링 도구를 사용해 GC 튜닝, OS 설정 변경, 코드 수정 등의 작업을 한다.

응답 반응성을 위한 튜닝과 처리량을 위한 튜닝은 별개의 작업일 수 있다. 단위 시간당 처리량이 많더라도 풀 GC 등을 위해 때때로 긴 'stop the world' 현상이 발생한다면 응답 반응성이 낮아지게 된다. 또한 일정 부분 트레이드 오프가 발생할 수 있음을 고려해야 한다. 이런 트레이드 오프는 응답 반응성과 처리량 사이의 관계에만 있지는 않음을 염두에 두자. 적은 메모리 사용을 위해 CPU 자원을 더 사용해야 하거나 응답 반응성이나 처리량 손실을 감수해야 할 수도 있고 반대의 경우도 발생한다. 그러므로 우선순위를 설정해 접근해야 한다.

<그림 1>의 순서도는 Swing 애플리케이션을 포함한 포괄적인 자바 애플리케이션에 대한 성능 튜닝 접근 방법이기에 인터넷 서비스를 위한 서버 애플리케이션을 작성할 때는 적합하지 않다. <그림 1>을 바탕으로 인터넷 서비스에 맞는 절차를 만들면 <그림 2>와 같은 순서도가 된다.

javaapplication2

그림 2 인터넷 서비스 자바 애플리케이션 권장 튜닝 절차

<그림 2>를 참고해 각각의 절차를 수행하기 위해 필요한 일을 알아보도록 하자.

JVM 옵션

웹 애플리케이션 서버 위주로 JVM 옵션 지정 방법을 설명하겠다. 모든 경우라고 할 수는 없지만 대부분의 웹 서버 애플리케이션에서 가장 좋은 GC 알고리즘은 Concurrent Mark Sweep GC다. 이는 낮은 딜레이가 중요하기 때문인데, 물론 Concurrent Mark Sweep을 사용할 경우에는 fraction이 발생해 경우에 따라 매우 긴 Stop the World 현상이 발생할 수도 있다. 하지만 이 역시 New 영역의 크기나 fraction ratio를 조정해 해결할 수 있는 경우가 많다.

전체 힙 사이즈의 크기 지정만큼 New 영역의 크기 지정 또한 중요하다. XX:NewRatio 옵션을 이용해 전체 힙 크기 중 New 크기의 비율을 지정하거나 XX:NewSize 옵션을 사용해 원하는 크기만큼의 New 영역 크기를 지정하는 것이 좋다. 대부분의 객체는 생존 시간이 길지 않기 때문에 New 영역 크기 지정이 중요해진다. 웹 애플리케이션에서 캐시 데이터를 제외한 대부분의 객체는 HttpRequest에 대한 HttpResponse가 만들어지는 시간에 생성된다. 보통 이 시간은 1초를 넘지 않기에 객체의 생존 시간도 1초가 되지 않는다. 만약 New 영역의 크기가 크지 않다면 새로 생성되는 객체의 자리를 위해 Old 영역으로 이동돼야 하고 Old 영역에 대한 GC 비용은 New 영역에 대한 GC 비용보다 상당히 크기 때문에 충분한 New 영역 크기를 잡아줘야 한다.

다만 일정 수치 이상으로 New 영역의 크기가 커지면 오히려 응답 반응성이 떨어지는 문제가 발생할 수 있으므로 주의하자. New 영역에 대한 GC는 기본적으로 어느 한 서바이버 영역에서 다른 서바이버 영역으로 복사하는 것이기 때문이다. 또한 Old 영역뿐만 아니라 New 영역에 대한 GC를 할 때에도 Stop the World 현상은 발생한다. New 영역이 커지면 상대적으로 서바이버 영역의 크기도 커져 그만큼 복사해야 할 데이터의 크기도 늘어난다. 이런 특성을 감안해 New 영역의 크기를 정할 때는 HotSpot JVM의 OS별 NewRatio를 참고하는 것이 좋다.

표 2 OS와 옵션별 NewRatio

OS & option디폴트 –XX:NewRatio
Sparc –server2
Sparc –client8
x86 –server8
x86 –client12

NewRatio를 지정하면 전체 힙 크기 중에서 1/(NewRatio + 1) 만큼이 New 영역의 크기가 된다. Sparc server의 NewRatio가 유독 작은 것을 알 수 있는데 기본값을 정하던 당시 x86보다 Sparc 시스템을 하이엔드 용도로 사용했기 때문이다. 요즘은 x86 서버 사용이 흔해졌고 성능 또한 향상됐기 때문에 Sparc server에 준하는 값인 2 또는 3 정도를 지정하는 것이 좋다.

NewRatio 대신 NewSize와 MaxNewSize를 지정할 수도 있다. NewSize에서 지정한 값만큼 New 영역이 생성됐다가 MaxNewSize에서 지정한 만큼 New 영역이 커진다. Eden이나 서바이버 또한 지정된 또는 기본 비율에 따라 같이 커진다. Xs와 Xmx 크기를 같게 하는 것처럼 NewSize와 MaxNewSize 또한 같게 지정하는 것이 좋다.

NewRatio와 NewSize를 지정했을 때는 둘 중 큰 값을 사용하기 때문에 힙이 생성됐을 때 최초의 New 영역의 크기는 다음과 같다.

min(MaxNewSize, max(NewSize, heap/(NewRatio+1)))

전체 힙과 New 영역의 적합한 크기를 한 번에 알 수는 없다. 웹 서버 애플리케이션을 기준으로 <표 3>과 같은 JVM 옵션으로 자바 애플리케이션을 가동해보는 것을 권한다. 이 옵션들로 성능을 모니터링한 후 더 적합한 GC 알고리즘이나 옵션으로 변경하자.

표 3 모니터링 후 옵션변경 예시

종류옵션
동작 모드-sever
전체 힙 크기-Xms와 –Xmx의 값을 같게
New 영역 크기-XX:NewRatio 2~4 정도의 값
-XX:NewSize=?
–XX:MaxNewSize=?
NewRatio 대신 NewSize를 지정하는 것도 좋다.
Perm 크기-XX:PermSize=256m
-XX:MaxPermSize=256m
성능에 영향을 미치지 않으므로 동작에 문제가 없을 정도만 지정한다.
GC 로그-Xloggc:$CATALINA_BASE/logs/gc.log
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
GC로그를 남기는 것은 특별히 Java 애플리케이션 수행 성능에 영향을 미치지 않는다. 가급적이면 GC 로그를 남기는 것이 좋다.
GC 알고리즘-XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
일반적으로 권할만한 설정일 뿐이다. 애플리케이션 특성에 따라 다른 선택이 더 좋을 수 있다.
OOM 에러 발생 시 힙 덤프 생성-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=$CATALINA_BASE/logs
OOM 발생 이후 조치-XX:OnOutOfMemoryError=$CATALINA_HOME/bin/stop.sh 또는
-XX:OnOutOfMemoryError=$CATALINA_HOME/bin/restart.sh 힙 덤프를 남긴 뒤, 관리 정책에 맞게 적합한 동작을 취할 수 있도록 한다.

애플리케이션 성능 측정

애플리케이션 성능을 측정하기 위해 파악해야 할 정보는 다음과 같다.

  • TPS(Transaction Per Second)/OPS(Operation Per Second): 개념적으로 해당 애플리케이션의 성능을 이해하는데 필요한 정보다.
  • RPS(Request Per Second): 엄밀한 의미에서 응답 반응성과는 다르지만 RPS를 응답 반응성으로 이해해도 큰 무리는 없다. 사용자가 원하는 결과를 보기 위해 기다려야 하는 시간을 알 수 있다.
  • RPS 표준편차: 가급적 고른 RPS가 나오도록 할 필요가 있다. 편차가 발생한다면 GC 튜닝이나 연동 시스템에 대한 점검이 필요하다.

정확한 성능 수치를 위해 충분히 워밍업된 상태에서 측정하는 것이 필요하다. HotSpot JIT에 의해 바이트 코드가 컴파일된 상태가 되기를 기대하기 때문인데, nGrider를 이용해 통상 10분 이상 특정 기능에 대한 부하를 준 뒤 성능 수치를 측정하는 것이 좋다.

본격적인 튜닝

본격적으로 사례별 접근 방법을 알아보자.

Stop the World 시간이 길다

Stop the World 시간이 긴 이유는 적합하지 않은 GC 옵션 때문일 수도 있지만 잘못된 구현 때문일 수도 있다. 프로파일러나 힙 덤프 결과를 통해 힙을 차지하고 있는 객체의 종류와 생성 개수를 확인해보고 적합여부를 판단한다. 불필요한 객체가 많이 생성돼 있다면 코드를 수정하는 것이 좋다.

객체 생성 과정에 특별한 문제가 없다면 GC 옵션을 변경하자. 적합한 GC 옵션 조정을 위해서는 충분한 시간 동안 확보한 GC 로그가 필요하다. 어떤 상황에서 긴 Stop the World가 일어나는지 파악하자.

GC는 객체를 얼마나 많이 생성하느냐보다는 생성된 객체가 얼마나 오래 남아있는가가 더 중요하다. 즉 객체가 보다 빨리 GC 대상이 될수록 Stop the World 시간은 줄어들 가능성이 높다.

객체가 빨리 GC 되게 만드는 팁은 다음과 같다.

  • 객체의 크기를 가급적 작게 유지한다.
  • Collection이나 기타 Container 형태의 자료구조 안에서 배열의 크기를 변경하는 작업은 가급적 피하자.
  • SoftReference는 사용하지 않는 게 좋다.

CPU 사용률이 낮다

TPS가 낮은데 CPU 사용률도 낮다면 blocking time이 원인이다. 이 경우 연동 시스템의 문제나 동시성(concurrency) 문제일 수 있다. 스레드 덤프 결과 분석이나 프로파일러를 이용해 확인할 수 있다. 상용 프로파일러를 이용하면 매우 정밀한 lock 분석을 할 수 있지만 대부분의 경우 jvisualvm에 있는 CPU 분석만으로도 충분한 결과를 얻을 수 있다.

CPU 사용률이 높다

TPS가 낮은데 CPU 사용률만 높다면 효율적이지 못한 구현 때문일 가능성이 높다. 이 경우 프로파일러를 이용한 병목 지점 파악이 유효하다. jvisualvm이나 eclipse의 TPTP, JProbe 등을 이용해 분석하자.

튜닝 접근 방법

애플리케이션을 튜닝할 때는 먼저 성능 튜닝이 필요한지 파악해야 한다. 성능 측정 과정은 매우 고되고 언제나 좋은 결과를 얻을 수 있다는 보장도 없기 때문에 충분한 목표 성능을 만족하고 있다면 굳이 튜닝을 하지 않는 것이 효율적이다.

  • 문제는 단 한 곳에 있고 그 하나만 수정하면 된다: 파레토 이론은 성능 튜닝에도 적용된다. 문제는 반드시 하나라는 의미보다는 가장 성능에 영향을 미치는 하나에만 집중해 접근할 필요가 있다는 뜻으로 해석하자. 하나에 집중해서 해결하고 난 다음에 다른 문제 해결을 위해 노력하도록 하자.
  • 풍선 효과: 무엇을 얻기 위해 무엇을 포기해야 하는지 결정해야 한다. 캐시를 적용해 응답 반응성을 높일 수는 있지만 캐시의 크기가 커지면 풀 GC 시간이 길어질 수 있다. 적은 메모리 사용량을 선택하면 대개 처리 용량이나 응답 반응 시간이 나빠진다. 하나를 선택하면 하나를 포기해야 한다는 것을 염두에 두고 우선순위를 정해 선택하자.

여기까지 자바 애플리케이션 성능 튜닝 방법을 정리해 봤다. 성능 측정에 대한 구체적인 절차를 만들다보니 세부적인 정보를 제외하고 설명하기도 했지만 자바 웹 서버 애플리케이션을 튜닝하기 위한 대부분의 경우는 만족시킬 수 있을 것이라 생각한다.

참고: 성능 튜닝 도구

성능 튜닝 작업을 위해서는 Java 애플리케이션의 성능을 측정하고 실행 상태를 모니터링할 다양한 도구가 필요하다. JDK에 내장된 명령 도구인 jstat, jmap, jstack, jhat도 유용하지만 그 외에도 다양한 도구가 있어 소개한다.

프로파일링 도구

JProbe, Yourkit 등의 상용 제품이 유명한데 대부분의 프로파일링 도구는 상용제품으로, 오픈소스나 공개된 프로파일링 도구는 거의 없는 형편이다.

  • Eclipse TPTP: 현재는 개발이 중단된 상태이나 공개된 프로파일링 도구 중 꽤 쓸만한 편이다.
  • JVisualVM: JDK에 포함된 기본 도구로 GC 분석, 힙 덤프 및 스레드 덤프 생성, 스레드 모니터링 등의 다양한 용도로 사용할 수 있다. 내장된 샘플러 도구를 통해 간단한 프로파일링이 가능하다.

성능 측정용 도구

성능 측정용 도구로는 HP의 LoadRunner가 가장 유명하다. 그러나 상용제품으로 꽤 비싼 가격이므로 본문에서 언급한 nGrinder를 소개한다.

  • nGrinder: NHN에서 제작해 공개한 오픈소스로 기존 오픈소스 성능 측정 도구인 Grinder의 불편한 점을 보완하고 통합 환경을 제공한다.

GC 로그 분석 도구

본문의 <표 3>처럼 GC 로그를 남겼다면 다양한 GUI 도구를 이용해 GC 추이를 분석할 수 있다.

  • Hpjmeter: HP에서 개발 배포하는 자바 성능 분석 도구로 Heap Dump 분석, 모니터링 등의 여러 기능을 가지고 있는 멀티툴이지만 GC 로그를 매우 깔끔하게 보여주므로 GC 로그 분석기로도 사용하기 좋다.
  • GC Viewer: 오픈소스로 개발된 GC 로그 뷰어다.
  • IBM Pattern Modeling and Analysis Tool for Java Garbage Collector: IBM developerworks에서 개발해 공개한 GC 로그 뷰어다.
  • JVisualVM의 VisualGC plugin: JVisualVM 내에 탑재된 GC 모니터링 플러그인이다. 현재의 GC 동작을 모니터링하기에 유용하다.

힙 덤프 분석 도구

Stop the World 시간이 길거나 기타 이유로 성능이 나쁘다고 여겨질 때 힙 덤프를 얻어 분석하는 것도 효과적이다.

  • Eclipse Memory Analyzer: 흔히 이클립스 MAT이라고 부르는 이클립스 기반의 메모리 분석기다. 이클립스 플러그인으로 설치해 사용할 수도 있고 이클립스 RCP로 된 스탠드 얼론 프로그램으로 사용할 수도 있다.
  • IBM HeapAnalyzer: IBM developerworks에서 개발해 공개하고 있는 힙 메모리 분석기다.

------------------------------------------------------------------------------------


자바 성능 의 9가지 오해 (http://www.infoq.com/articles/9_Fallacies_Java_Performance)


1. Java is slow  (?)


90년대 말이나 2000년대 초까지는 자바가 느린 것이 사실일 수도 있다. 하지만 현재는 JVM과 JIT Compiler의 향상으로 상당한 속도 향상을 가져왔다. 많은 경우 C++ 만큼의 성능 향상을 가져왔다.


2. A single line of Java means anything in isolation (?)


다음 코드를 보자 

MyObject obj = new MyObject();

자바개발자가 보기에 명백히  객체를 할당하고 적당한 생성자를 작동시키는게 틀림없다. 네가 저 코드에서 무엇인가 성능개선점이 있다고 확신하여  지지고 볶고하지마라..저 코드라인은 전혀 실행되지 않을 수도있다.  이 얘기를  왜하냐면 미리 직관적으로 성급한 자바 성능개선을 하지 말라는거다. 대신 항상 네 코드를 빌드하고 네 코드의 성능상 중요지점이 어디인지 아는데 관심을 기울이라는 얘기이다. 


3. A microbenchmark means what you think it does (?)


위에 보다시피 적은 규모의 코드에 대해서 성능향상을 꾀하려고하는것은 바보짓이다. 큰 단위에서 바라보라.

microbenchmarks 는 굉장하게 어렵다. 자바 플랫폼은 굉장히 복잡하다. 대부분의 microbenchmarks 는

뻘짓이다.


4. Algorithmic slowness is the most common cause of performance problems (?)


네가 짠 알고리즘,로직이 성능구린것의 주된 이유가 될 가능성은 별로 없다.
대신해서 가비지컬렉션, 데이타베이스 엑세스 및 잘못된 설정이 솔루션을 망칠 가능성이 대부분이다.
알고리즘 및 자료구조는 나중에 살펴봐라.


5. Caching solves everything (?)


"컴퓨터 과학에서의 모든 문제들은 하나의 레이어를 추가해서 풀수있다." <--- 굉장한 명언이다.

어떤 구린시스템이 있을때 그 시스템 면전에서 욕하고 협박하지 말고 앞단에 하나의 레이어를 덧대는것을 생각해보라. 물론

이 해법은 꽤 복잡하다 (전체 아키텍처를 알아야하거나 다음 개발자에게 더 안좋은 상황을 선사할수도 있겠다..)

Caching 레이어를 추가할때 항상 이 레이어가 제대로 될 것인지 생각하라. 


6. All apps need to be concerned about Stop-The-World (?)


가비지컬렉션 로그 쓰는것자체가 쓰레드에 굉장한 부담이 될수있다는것을 알고 써라.


7. Hand-rolled Object Pooling is appropriate for a wide range of apps (?)


In summary, object pooling should only be used when GC pauses are unacceptable, and intelligent attempts at tuning and refactoring have been unable to reduce pauses to an acceptable level.


8. CMS is always a better choice of GC than Parallel Old (?)


CMS 가 너의 정확한 GC 전략이라고 결정짓기 전에 너는 STW 가 받아드려질수 없거나 조절될수 없다는것을 확신하라.
JDK7 기준 5가지 GC 전략
  • Serial GC
  • Parallel GC
  • Parallel Old GC(Parallel Compacting GC)
  • Concurrent Mark & Sweep GC(이하 CMS)
  • G1(Garbage First) GC
CMS 소개(http://helloworld.naver.com/helloworld/helloworld/1329)

 stop-the-world 시간이 매우 짧다. 모든 애플리케이션의 응답 속도가 매우 중요할 때 CMS GC를 사용하며, Low Latency GC라고도 부른다.그런데 CMS GC는 stop-the-world 시간이 짧다는 장점에 반해 다음과 같은 단점이 존재한다.
  • 다른 GC 방식보다 메모리와 CPU를 더 많이 사용한다.
  • Compaction 단계가 기본적으로 제공되지 않는다.

따라서, CMS GC를 사용할 때에는 신중히 검토한 후에 사용해야 한다. 그리고 조각난 메모리가 많아 Compaction 작업을 실행하면 다른 GC 방식의 stop-the-world 시간보다 stop-the-world 시간이 더 길기 때문에 Compaction 작업이 얼마나 자주, 오랫동안 수행되는지 확인해야 한다.


9. Increasing the heap size will solve your memory problem (?)


JVM 의 다른 파라미터들을 튜닝하거나 힙사이즈를 바꾸기전에 객체할당과 생존기간에 대한 매커니즘을 이해하라. 



-------------------------------------------------------------------------------------


GC 튜닝  (http://rajalo.tistory.com/entry/java-GC-%EA%B4%80%EB%A0%A8)


1. RMI 사용시 주기적인 FULL GC  발생 

: 주기적으로  system.gc() 를 호출하는데 이것은 STOP-THE WORLD 식의 FULL GC 를  유발한다. 

해결책: 1.  아예 실행안되게 2. 인터벌을 길게 3. GC콜이 Concurrent 방식으로 이루어지게 함. 


2. Paging in/out에 의한 GC 지연

해결책 : 1. Java Max heap size 를 줄임. (OS자체의 메모리 부족으로 페이징이 자주 일어나는거 같다) 


3. Heap 여유 공간이 충분한데도 OutOfMemoryException이  일어난다.

자바의 메모리공간에 대한 이해가 필요하다. (Parmanent Space 와 힙이 2가지임. 객체용, 네이티브용) 

C++ 과 다르게 자바는 실행시간에 리플렉션을 통해 코드를 끌어오므로 Parmanent Space 가 막 늘어날수가있음. 

또 쓰레드에 대한 정보는 네이티브힙에서 관리하는데 쓰레드가 너무 많아지면 이게 네이티브힙이 버티질 못한다.

해결책 : 1. Parmanent Space 크기를 늘린다.(로그에 이게 문제라면)  2. 쓰레드풀을 사요하여 네이티브힙의 크기를 안정화시킨다.


4. 큰  뉴 제네레이션에 의한 FULL DC 발생.

New Generation 이 크면 minor DC 에는 유리하지만 FULL DC에는 불리할 확률이 높다는것을 알고 적당한 크기를 찾자


------------------------------------------------------------------------------------

가비지 컬렉션에서의 메모리 누수 (http://minjang.egloos.com/2372567)

.

메모리 누수는 GC라 해도 완벽히 잡아낼 수 없다. 메모리 누수를 다시 두 개로 나눠 생각하면:

  • 접근 불가능한 객체에 대한 누수(lost objects): 더 이상 해당 객체에 접근을 할 수 없지만 반환되지 않은 것들
  • 더 이상 사용하지 않는 객체에 대한 누수(useless objects): 도달은 가능한데 더 이상 사용하지 않는 것들

핵심은 두 번째 해당하는 녀석인데, 먼저 여기서 아주 간략히 GC의 작동 원리부터 이야기 하면, 사실 별 것 없다. 근본 원리는 할당 받은 메모리에 접근할 수 있는 경로가 더 이상 존재하지 않는다면 이 메모리는 사용되지 않는 것으로 간주하고 GC가 자동적으로 반환하는 것이다. 코드까지 쓰기는 귀찮고(…) 대표적인 예로 메모리를 할당 받은 변수가 지역 변수라서 해당 스코프를 벗어나면 더 이상 접근 불가능한 경우가 있다.

그러나 문제는 두 번째에 해당하는 문제, 즉 도달은 가능한데 더 이상 사용하지 않는 객체에 대한 메모리 누수이다. 조금 극단적인 경우지만 이런 서버 프로그램의 예를 생각해보자.


  1. 클라이언트가 접속할 때 마다 Client라는 자료 구조를 하나 할당하고 전역 리스트에 이 Client 객체를 넣는다.
  2. 클라이언트가 접속을 끊으면 해당 Client 자료 구조를 리스트에서 삭제한다.

만약 이렇게만 작동한다면 GC는 똑똑히 작동한다. 2번 과정에서 더 이상 해당 Client 자료 구조에 대한 접근이 사라지므로 GC는 이 객체를 해제할 수 있다. C/C++ 처럼 명시적으로 Client 객체를 free/delete 할 필요는 없다.

그러나 문제는 프로그램이 복잡해지면 2번 과정에 실수가 있을 수 있다. 분명 더 이상 객체가 사용되지 않는데 프로그램 어딘가에 이 객체로 접근할 수 있는 길이 남아 있는 것이다. 극단적으로 이야기 하면 프로그래머가 2번 과정을 실수로 빼먹는 경우다. 아무리 이 Client 객체가 사용되지 않지만 전역 리스트에 자리를 차지하고 있으니 GC는 이것이 언젠가는 사용될 것이라고 보수적으로 판단해서 삭제를 할 수 없다.

어떤 메모리가 오랫동안 사용되지 않는다 하더라도 GC는 이 메모리 영역을 해제할 수 없다. 극단적으로 어떤 객체가 백 만년 뒤에 쓰인다면? 이건 구현이 어려워서가 아니라 아예 불가능한 영역이 있다. 미래를 예측할 수 없기 때문이다. GC 구현 기법 중 Generational GC 등이 있는데 이건 접근 불가능한 객체들을 효율적으로 찾기 위한 것이지, 이렇게 더 이상 사용되지 않는, 그런데 도달은 가능한 객체에 대한 누수를 해결하는 것이 아니다.

따라서 어떤 객체로 접근할 수 있는 방법이 있다면 GC는 아무것도 할 수 없고 실제 큰 자바 프로젝트(예를 들어 느려터진 이클립스)에서는 메모리 누수가 많이 일어난다. Managed 환경에서도 메모리 사용을 프로파일링 하는 도구는 많이 있고 메모리 누수 검출 도구도 많이 있다. .NET Memory ProfilerJRockit 같은 것이 일단 있고 C/C++ 쪽은 익히 아는 DevPartner나 GNU 환경에서는 Valgrind 같은 것들이 있다.

위키에도 이런 내용이 잘 있다. 물리적 메모리 누수(lost objects)와 논리적 메모리 누수(useless objects)로 분류하고 GC는 후자에 대해서는 아무것도 할 수 없다.




Garbage Collection과 Statement Pool

NHN 게임서비스기술지원팀 최동순 


잘 설정한 Statement Pool 개수는 GC(Garbage Collection) 전문가의 튜닝이 부럽지 않습니다. 이 글에서는 Statement Pool의 개수가 GC 과정에 미치는 영향을 살펴보고 어떻게 Statement Pool 개수를 설정하는지 설명합니다.


Statement Pool 개수를 살펴야 하는 이유

JDBC Statement Pool의 크기를 기본값으로 설정해 사용하는 경우가 많다. 물론 기본값을 사용해도 특별한 문제가 없는 경우가 많다. 그러나 제대로 설정한 Statement Pool 개수는 GC 튜닝을 한만큼의 효과가 있을 수 있다. Statement Pool 값을 기본값으로 설정해 사용하고 있다면, 메모리 사용을 좀 더 최적화하고 싶을 때 GC 튜닝을 시도하기에 앞서 올바른 Statement Pool 값이 무엇인지 고민해 보자.

"Java Garbage Collection"에서 다룬 것처럼 Java에서는 Garbage Collector를 설계할 때 weak generational hypothesis 를 전제로 한다. NHN 웹 서비스는 특별한 경우가 아니면 대부분 늦어도 300ms 이내에 응답을 줄 수 있어야 한다. 그렇기 때문에 일반적인 스탠드얼론 형태의 애플케이션보다 NHN의 웹 서비스가 위 전제에 더 부합하고 있음을 알 수 있다.


HTTP 요청에서 응답까지 GC가 일어나는 과정

Tomcat 같은 웹 컨테이너와 다양한 프레임워크를 이용하여 웹 서비스를 개발할 때에 개발자가 직접 생성한 객체의 수명은 보통 아주 짧거나 아주 길거나 둘 중에 하나이다.

웹 개발자가 주로 작성하는 코드는 Interceptor, Action, BO, DAO 인데, 이런 코드에서 생성되는 객체는 HTTP 요청이 왔을 때 응답을 주기까지의 매우 짧은 시간만 살아 있다. 그래서 이런 객체는 대부분 Young GC 때 수거된다.

물론 singleton 객체같이 Tomcat 라이프사이클과 같을 정도로 아주 길게 살아 있는 객체도 있을 것이다. 이런 객체는 Tomcat이 가동된 후 얼마 지나지 않아 모두 Old 영역으로 Promotion되어 있을 것이다.

그런데 jstat 등으로 웹 애플리케이션을 지속적으로 모니터링해 보면 Young GC 때 Old 영역으로 Promotion되는 객체가 항상 있다.

이런 객체는 대부분 컨테이너와 프로젝트에서 사용하는 프레임워크에서 성능 향상을 위해 사용하는 캐시에 저장하여 사용하는 객체이다. 이렇게 캐싱되는 객체는 시간적인 문제가 아닌 cache hit ratio에 따라서 GC 대상 여부가 결정되기 때문에 hit ratio가 100%가 아닌 이상은 아무리 Young GC 주기를 길게 설정해도 Old로 Promotion되는 것을 막을 수 없다.

이런 캐시 중에서 메모리 사용량에 가장 큰 영향을 주는 것은 Statement Pool이다. 만약 iBatis를 사용하고 있다면 모든 SQL을 preparedStatement로 처리하는 iBatis의 특성상 Statement Pool을 사용한다.

만약 사용하는 SQL의 개수에 비하여 Statement Pool의 크기가 작다면, cache hit ratio가 낮게 될 것이고 캐시 유지 비용이 발생하게 될 것이다. Old 영역에서 계속 있어도 되는 (reachable 또는 있는 게 좋은) 객체가 GC의 대상이 되어 회수되고, 이후 HTTP 요청 처리 과정에서 다시 생성된 다음 캐싱되어 Old 영역까지 Promotion되는 것이다. 이런 과정으로 Full GC 주기에 영향을 주게 된다.


Statement 객체의 크기

하나의 Statement 객체의 크기는 그 Statement가 처리하는 SQL 코드 길이에 비례한다고 보아도 무리가 없다. 길고 복잡한 SQL을 예로 든다고 해도 약 500바이트 내외가 될 것이다. 객체 크기가 작아 Full GC 주기에 별 영향을 줄 것 같지 않지만, 실제로는 그렇지 않다.

JDBC 스펙을 살펴 보면 다음 그림처럼 각 커넥션이 자신의 Statement Pool을 각각 가지는 구조이다. 즉 하나의 Statement 객체 크기가 500바이트 정도로 작더라도 커넥션 수가 많으면 그에 비례하여 heap을 점유하게 된다.

sp

그림 1 Connection과 Statement Pool과의 관계

Statement Pool의 cache hit ratio가 Full GC에 미치는 영향

cache hit ratio가 Full GC에 미치는 영향을 알아보기 위해 간단한 테스트 프로그램을 제작하였다. cache hit ratio가 하나는 100%가 되도록 하고 다른 하나는 50%가 되도록 했다. 그리고 동일한 부하를 주었을 때 다음 표와 같은 결과가 나왔다.

Young GC가 발생한 횟수는 둘 다 비슷하지만, Full GC의 경우는 cache hit ratio가 100%이면 Young GC 때 Old로 Promotion되는 객체의 양이 적어 한 번만 Full GC가 발생한다. 반면 cache hit ratio가 50%이면, Statement Pool에 캐싱되었다가 다시 LRU 방식으로 풀(pool)에서 제거되고 다음 요청에서 다시 캐싱되는 방식이기 때문에 Young GC 때 Old로 Promotion되는 Statement 객체의 개수가 많아져 총 4번의 Full GC가 발생하였다.

표 1 cache hit ratio = 100%

...

OC

OU

YGC

FGC

FGCT

GCT

10688.0

6940.9

532

1

0.190

1.274

...

10688.0

6940.9

532

1

0.190

1.274

 

표 2 cache hit ratio = 50%

...

OC

OU

YGC

FGC

FGCT

GCT

...

10240.0

7092.7

554

4

0.862

2.253

...

10240.0

7412.0

555

4

0.862

2.255

 

또 한 가지 언급하고 싶은 내용은, cache hit ratio가 50%인 상황은 앞서 소개한 weak generational hypothesis의 2번째 항목에 위배되는 상황이라는 것이다. cache hit ratio가 낮아서 빈번히 풀에 등록되었다가 제거되는 현상이 반복된다는 것은 이미 Old 영역에 있는 풀에서 Young 영역에 생성된 Statement 객체에 대한 참조를 가지게 되는 것으로 card table marking 기법 으로 별도로 reference를 관리하게 되어서 GC 시에 추가적인 부담이 발생하게 된다.


마치며

Oracle과 MySQL에 대한 Statement Pool 기본값은 500으로 설정해도 충분할 것이다. 그 이상의 SQL이 사용된다면 충분한 크기로 늘리는 것이 시스템의 효율을 높이는 방법이다 .

하지만 필요보다 높은 수를 설정하는 것도 문제가 있다. 그만큼 많은 메모리를 사용하게 되고, OOME(Out Of Memory) 발생 가능성도 높아지기 때문이다. SQL 개수가 1만 개, 커넥션 개수가 50개인 상황을 가정하고 계산해 보면 메모리 사용량이 250MB 정도가 된다(500 byte * 50 * 10,000 = 250 MB).

운영중인 서비스의 Xmx 설정을 확인하여 OOME 발생 가능성 여부는 쉽게 판단할 수 있을 것으로 생각한다.


http://helloworld.naver.com/helloworld/textyle/4717)


'Java' 카테고리의 다른 글

자바 스케쥴링 & 타이머 방법들  (0) 2015.05.14
Java Time/ Date / Calendar example  (0) 2015.05.13
Java Time,Data 클래스의 문제점과 JAVA 8  (0) 2015.05.13
자바 List 순회  (0) 2015.05.12
자바에서 Map 순회  (0) 2015.05.12



Scala 와 Python 의 비교를 통해서 이 언어들의  기초적인 언어 스타일링에 대해서 빠르게 훑어보도록 하겠습니다.



                                             Scala vs  Python  



Scala 


스칼라(Scala)는 객체 지향 프로그래밍 언어와 함수형 프로그래밍의 요소가 결합된 다중패러다임 프로그래밍 언어이다. C++ 가 C의 요소를 가져오긴 했지만 객체지향을 지향하듯이 스칼라도 함수형으로 개발하는것을 지향한다. 스칼라의 이름은 <skah-lah>라고 발음하며 'Scalable Language'에서 따왔다. 적은 양의 코드로도 방대한 규모의 시스템을 작성할 수 있다는 특징이 있다.[2] 자바의 자바 가상 머신 에서 실행 할 수 있으며 자바의 API도 그대로, 혹은 스칼라의 형식에 맞춰 약간 수정한 형태로 사용 할 수 있다. 대중화를 위한 굉장히 큰 장점이다.스칼라의 주요 특징은 함수도 객체라는것과 숫자도 객체 즉 모든 것이 객체인것이 자바와는 가장 크게 다른 점이다.



Python :

파이썬도 절차지향,객체 지향, 함수형 프로그래밍의 요소가 결합된 다중패러다임 프로그래밍 언어이다

파이썬[1](Python)은 1991년[2] 프로그래머인 귀도 반 로섬(Guido van Rossum)[3] 이 발표한 고급 프로그래밍 언어로, 플랫폼 독립적이며 인터프리터식, 객체지향적, 동적 타이핑(dynamically typed) 대화형 언어이다. 파이썬이라는 이름은 귀도가 좋아하는 코미디 〈Monty Python's Flying Circus〉에서 따온 것이다.


파이썬은 비영리의 파이썬 소프트웨어 재단이 관리하는 개방형, 공동체 기반 개발 모델을 가지고 있다. C언어로 구현된 C파이썬 구현이 사실상의 표준이다. 표준 파이썬은 아무래도 느린감이 있지만 분산,병렬로 어느정도 카바 가능하다. 엄청나게 속도 크리티컬한 프로그램에선 쓸 수 없지만 pypy 등 속도를 향상시킨 파이썬 버전을 이용하여 속도라는 토끼도 잡을 수 있다. 개인적으로 그렇게 쓸 바엔 C++ 이나 RUST 쓰겠다. 




                                      이 표는 좀 오래된것 같군요. 몇가지가 빠진듯..Flask 같은..



   

1. 기본 구조 및 HelloWorld 


Scala) 


object HelloWorld {

  def main(args: Array[String]) {

    println("Hello, world!")

  }

}


HelloWorld.scala 로 저장



Python) 


class HelloWorld:

        def f(self):

                print "Hello, world!"

hw = HelloWorld()

hw.f()


print "HelloWorld"  라고 그냥 해도 됨. 클래스 안 만들고 말이죠.


Helloworld.py 로 저장.



Scala 의 main 함수는 static이 아니다. Scala에는 정적 멤버(함수,필드 모두) 라는 개념이 존재하지 않는다.
object선언은 싱글턴 객체를 생성하는데, 정적개념이 필요하면 싱글턴객체 안에 넣으면 된다. 


Python 은 구조상 특이점은 { }가 없다는것과 들여쓰기를 통한 단락의 구분과 : (콜론) 의 사용이다. 하위 레벨의 코드는 들여쓰기가 한 단계 깊어지고 상위레벨은 콜론으로 끝나야한다. 자바나 c++ 에서의 This 를 명시적으로 self 로 사용하는게 이채롭다. 



2. 주석 스타일 


Scala) 


// 한 줄 주석


/* 

여러 줄 주석  

*/




Python) 


# 한 줄 주석


‘’’ 

여러 줄 주석

‘’’


Scala 는 자바와 같고  

Python 은 여러 줄 일 경우 따옴표 3개 혹은 쌍따옴표 3개를 배치하면 된다.  



3. 기본 자료형  


Scala) 


Byte   8 bit . Range from -128 to 127

Short 16 bit . Range -32768 to 32767

Int        32 bit . Range -2147483648 to 2147483647

Long   64 bit

Float 32 bit IEEE 754 single-precision float

Double 64 bit IEEE 754 double-precision float

Char         16 bit unsigned Unicode character.

String A sequence of Chars

Boolean Either the literal true or the literal false

Unit         Corresponds to no value

Null         null or empty reference

Nothing The subtype of every other type;

Any          The supertype of any type; 

AnyRef  The supertype of any reference type




Python) 


Numbers (int, float etc.)

String

Dictionary

Tuple

List


a=’76.3’

b=float(a)     # 숫자로 형변환 

c = str(b)     # 문자로 형변환 


strLesson = ‘This is Python strings lesson’

StrLessonTriple = """This

is

string

Lesson"""   




4. 기본 자료구조  


Scala) 


val 변경불가
var 변경가능

 

val scor    =  Map(“Alice” → 10, “Bob” → 3)

val scores = scala.collection.mutable.Map()

scoes(“Bob”) = 10

scores += (“Bob”->1, “Fred”->7)




Python) 


Dictionary

Tuple

List

   


dict = {‘Mike’: 1233456,’John’: 567890}


list_mixed = [1, ‘this’, 2, ‘is’, 3, ‘list’, 4, ‘chapter’]

list_list      = [1, ‘this’, [ 5, ‘hell’], 9’]


tuple_strings = (‘this’, ‘is’, ‘tuple’, ‘lesson’)

tuple_mixed = (1, ‘this’, 2, ‘is’, 3, ‘tuple’, 4, ‘chapter’)


print “=” * 50





5. 연산자 


Scala) 


인식자 

변수,함수, 클래스등의 이름을 총체적으로 인식자라고부른다. 추가적으로 일련의 연산자문자도 사용가능 

val √ = scala.math.sqrt _

√ (2)

val ‘val’ = 42


삽입 연산자 (a identifier b)

a to 10

a.to(10)

1->10

1.->(10)


단항 연산자 (a identifier)

1 toString

1.toString() 


할당연산자  (a operator=b)

a+=b

a= a+b


apply/  update / unapply 

함수호출문법을 함수아닌값에도사용가능 

f(arg1,arg2, …)

f.apply(arg1,arg2,...)

f(arg1,arg2, …) = value

f.update(arg1,arg2,... value) 


unapply는 어떤 객체에서 값을 추출한다고 보면된다.

Case Fraction(a,b) =>   하면 Fraction 객체안의 값이 옵션으로 리턴됨. Option[(Int,Int)]




Python) 


1) 산술 연산자 : +, -, *, **, /, //, %


2) 관계 연산자 : <, >, <=, >=, ==, !=, <>


3) 논리 연산자 : not, and, or

    - 0, 0.0, 0L, 0.0+0.0j, (), {}, [], None, ""은 모두 거짓으로 간주

    - 나머지는 모두 참

    - 'and'와 'or'이 포함된 논리식은 식의 결과 값을 판정하는데 최종적으로 기여한 객체의 값을 식의 값으로 리턴


4) 비트 단위 연산자 : <<, >>, &, |, ^, ~


3. 수치 연산 함수

  1) 내장 수치 연산 함수

    - abs(x) : x의 절대값

    - int(x) : x를 int(정수)형으로 변환

    - long(x) :  x를 long형으로 변환

    - float(x) : x를 float형(실수형)으로 변환

    - complex(re, im) : 실수부 re와 허수부 im를 가지는 복소수

    - c.conjugate() : 복소수 c의 켤레 복소수

    - divmod(x, y) : (x//y, x%y) 쌍

    - pow(x, y) : x의 y승

  2) math 모듈 : math(실수 연산), cmath(복소수 연산)



5. 조건문 


Scala) 


var result = ""

if(marks >= 50)

  result = "passed"

else

  result = "failed"


------------------------------

result match {

  case "A" | "B" => println("Congratulations!")

  case "C" => println("There is room ..")

  case _ => println("We are ...")

}




Python) 



var = 100

if var == 200:

   print "1 - Got a true expression value"

   print var

elif var == 150:

   print "2 - Got a true expression value"

   print var

elif var == 100:

   print "3 - Got a true expression value"

   print var

else:

   print "4 - Got a false expression value"

   print var


Python 에는 switch 문이 없다. 



6. 반복문 


Scala) 


object Test {

   def main(args: Array[String]) {

      var a = 0;

      // for loop execution with a range

      for( a <- 1 to 10){

         println( "Value of a: " + a );

      }

   }

}

----------------------------------   

  var a = 0;

  val numList = List(1,2,3,4,5,6);


  for( a <- numList ){

     println( "Value of a: " + a );

  }

   



Python) 



for i in a:

  print I


# 0~3까지 범위

for i in range(4):

  print I


# 지정한 하한/상한의 범위에서만. (1씩 증가)

for i in range(1000, 1010):

  print I


# 지정한 하한/상한의 범위에서만. (2씩 증가)

# range()의 3번째 파라이터가, 건너뛸 숫자

for i in range(1, 14, 2):

  print I


# 지정한 숫자에서만

for i in 123, 341, 0, 666:

  print I


myList = ['cat', 'dog', 'rabbit']

 

for a in myList:

    print(a)




7. 배열 


Scala) 



var z = new Array[String](3)


z(0) = "Zara"

z(1) = "Nuha"

z(2) = "Ayan"


var z = Array("Zara", "Nuha", "Ayan")

var myList = Array(1.9, 2.9, 3.4, 3.5)

var myMatrix = ofDim[Int](3,3)


import Array._

object Test {

   def main(args: Array[String]) {

      var myMatrix = ofDim[Int](3,3)

      

      // build a matrix

      for (i <- 0 to 2) {

         for ( j <- 0 to 2) {

            myMatrix(i)(j) = j;

         }

      }


      var myList1 = Array(1.9, 2.9, 3.4, 3.5)

      var myList2 = Array(8.9, 7.9, 0.4, 1.5)


      var myList3 =  concat( myList1, myList2)


      var myList1 = range(10, 20, 2)

      var myList2 = range(10,20)

      




Python) 



MyList=[1,2,3,4,5,6]


MyList[2]=100


MyList[2:5]  #  from myList[2] to myList[4]. 


MyList[5:]     #  List[5] to the end of the list 


MyList[:5]     #  not including myList[5]


mi=MyList.index(m) 


myList=[i for i in range(10)]


myList=[]

for i in range(10):

 myList.append(1)  or myList[i]=1


b=[[0]*3 for i in range(3)]


B[0][0]=1


myArray=[[0 for j in range(3)] for i in range(3)]



8. 문자열 


Scala) 


var greeting = "Hello world!";

or

var greeting:String = "Hello world!";


var palindrome = "Dot saw I was Tod";

var len = palindrome.length();


"My name is ".concat("Zara");


object Test {

   def main(args: Array[String]) {

      var str1 = "Dot saw I was ";

      var str2 =  "Tod";

      println("Dot " + str1 + str2);

   }

}


object Test {

   def main(args: Array[String]) {

      var floatVar = 12.456

      var intVar = 2000

      var stringVar = "Hello, Scala!"

      var fs = printf("The value of the float variable is " +

                   "%f, while the value of the integer " +

                   "variable is %d, and the string " +

                   "is %s", floatVar, intVar, stringVar)

      println(fs)

   }

}




Python) 



food = "Python's favorite food is perl"

say = '"Python is very easy." he says.'

food = 'Python\'s favorite food is perl'


>>> head = "Python"

>>> tail = " is fun!"

>>> print(head + tail)

Python is fun!


>>> a = "python"

>>> print(a * 2)

Pythonpython


>>> a = "Life is too short, You need Python"

>>> a[3]

'e'


>>> a[0:4]

'Life'


>>> a[5:7]

'is'


>>> a[19:]

'You need Python'


>>> print("I eat %d apples." % 3)

I eat 3 apples.


>>> number = 10

>>> day = "three"

>>> print("I eat %d apples. so I was sick for %s days." % (number, day))

I eat 10 apples. so I was sick for three days.



9. 예외 


Scala) 


import java.io.FileReader

import java.io.FileNotFoundException

import java.io.IOException


object Test {

   def main(args: Array[String]) {

      try {

         val f = new FileReader("input.txt")

      } catch {

         case ex: FileNotFoundException => {

            println("Missing file exception")

         }

         case ex: IOException => {

            println("IO Exception")

         }

      } finally {

         println("Exiting finally...")

      }

   }

}



Python) 


        try:

...         result = x / y

...     except ZeroDivisionError:

...         print "division by zero!"

...     else:      ( 예외가 실행되지 않으면 실행)

...         print "result is", result

...     finally:     (예외가발생하던 말던 실행)

...         print "executing finally clause"



    try:

...     raise NameError, 'HiThere'

... except NameError:

...     print 'An exception flew by!'

...     raise



10. 클래스 


Scala) 


import java.io._


class Point(val xc: Int, val yc: Int) {

   var x: Int = xc

   var y: Int = yc

   def move(dx: Int, dy: Int) {

      ...

   }

}



class Time {

  private[this] var h = 12

  private[this] var m = 0

      

  def hour: Int = h

  def hour_=(x: Int){ h = x }

      

  def minute: Int = m

  def minute_=(x: Int){ m = x }

}




Python) 




class Service:

...     secret = "영구는 배꼽이 두 개다"

...     def __init__(self, name):

...         self.name = name

...     def sum(self, a, b):

...         result = a + b

...         print("%s님 %s + %s = %s입니다." % (self.name, a, b, result))


>>> pey = Service("홍길동”)

>>> pey.sum(1, 1)



11. 함수 


Scala) 



object Test {

   def main(args: Array[String]) {

        println( "Returned Value : " + addInt(5,7) );

   }

   def addInt( a:Int, b:Int ) : Int = {

      var sum:Int = 0

      sum = a + b


      sum

   }

}



Python) 



>>> def fib(n):    # write Fibonacci series up to n

...     "Print a Fibonacci series up to n"

...     a, b = 0, 1

...     while b < n:

...         print b,

...         a, b = b, a+b

... 

>>> # Now call the function we just defined:

... fib(2000)

1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597


>>> def fib2(n): # return Fibonacci series up to n

...     "Return a list containing the Fibonacci series up to n"

...     result = []

...     a, b = 0, 1

...     while b < n:

...         result.append(b)    # see below

...         a, b = b, a+b

...     return result

... 

>>> f100 = fib2(100)    # call it

>>> f100                # write the result

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]



12. Import  방법 


Scala) 


Import clauses


An import clause has the form import p.I where the import expression I determines a set of names of members of p which are made available without qualification. For example:

The clause makes available without qualification..

import p._ all members of p (this is analogous to import p.* in Java).

import p.x the member x of p.

import p.{x => a} the member x of p renamed as a.

import p.{x, y} the members x and y of p.

import p1.p2.z the member z of p2, itself member of p1.



Python) 


import 모듈이름


>>> import mod1 

>>> print(mod1.sum(3,4))

7


mod1.py에 다음의 함수를 추가 시켜 보자.

def safe_sum(a, b): 

    if type(a) != type(b): 

        print("더할수 있는 것이 아닙니다.")

        return 

    else: 

        result = sum(a, b) 

    return result


>>> import mod1 

>>> print(mod1.safe_sum(3, 4))

7


>>> from mod1 import sum         // 모듈내의 기능을 바로 사용할수있게한다. 

>>> sum(3, 4) 

7


from mod1 import *



13. 콘솔 입출력   


Scala) 



object ScannerTest {

  def main(args: Array[String]) {

    var ok = true

    while (ok) {

      val ln = readLine()

      ok = ln != null

      if (ok) println(ln)

    }

  }

}


object ScannerTest {

  def main(args: Array[String]) {

    for (ln <- io.Source.stdin.getLines) println(ln)

  }

}


object Test {

   def main(args: Array[String]) {

      print("Please enter your input : " )

      val line = Console.readLine

      

      println("Thanks, you just typed: " + line)

   }

}



Python) 



>>> a = input() 

Life is too short, you need python 

>>> a 

Life is too short, you need python 

>>>


>>> number = input("숫자를 입력하세요: ") 

숫자를 입력하세요:


>>> a = 123 

>>> print(a)

123 

>>> a = "Python" 

>>> print(a) 

Python 

>>> a = [1, 2, 3] 

>>> print(a) 

[1, 2, 3]



14. 파일  입출력   


Scala) 


import java.io._


object Test {

   def main(args: Array[String]) {

      val writer = new PrintWriter(new File("test.txt" ))


      writer.write("Hello Scala")

      writer.close()

   }

}


import scala.io._


object ReadFile extends Application {

  val s = Source.fromFile("some_file.txt")

  s.getLines.foreach( (line) => {

    println(line.trim.toUpperCase)

  })

}



Python) 



f = open("C:/Python/새파일.txt", 'w')


f = open("새파일.txt", 'w')


for i in range(1, 11): 

    data = "%d 번째 줄입니다.\n" % i 

    f.write(data) 


f.close()


f = open("새파일.txt", 'r') 

line = f.readline() 

print(line)

f.close()


f = open("새파일.txt", 'r')


while 1: 

    line = f.readline()

    if not line: break 

    print(line)


f.close()


f = open("새파일.txt", 'r') 

lines = f.readlines()


for line in lines: 

    print(line)


f.close()




15. 컨테이너 (컬렉션) 


Scala) 


// Define List of integers.

val x = List(1,2,3,4)


// Define a set.

var x = Set(1,3,5,7)


// Define a map.

val x = Map("one" -> 1, "two" -> 2, "three" -> 3)


// Create a tuple of two elements.

val x = (10, "Scala")


// Define an option

val x:Option[Int] = Some(5)


object Test {

   def main(args: Array[String]) {

      val capitals = Map("France" -> "Paris", "Japan" -> "Tokyo")

      

      println("show(capitals.get( \"Japan\")) : " +  

                                          show(capitals.get( "Japan")) )

      println("show(capitals.get( \"India\")) : " +  

                                          show(capitals.get( "India")) )

   }

   

   def show(x: Option[String]) = x match {

      case Some(s) => s

      case None => "?"

   }

}



 var s = Some("abc")          // Scala can infer the type

    var t: Option[String] = None // Type must be explicit


def chooseFile(): Option[File] = { ... }



The most general way to use an Option is with a match:


    chooseFile() match {

      case Some(f) => // Do something with file f

      case None =>    // Do something when there is no file

    } 


You can check if an Option value is defined by comparing it with None, or by using the isDefined or isEmpty method:


    if (t isDefined) println(t)

    if (t isEmpty) println("Nothing here!")

You can extract the value of an Option, but only if you provide some default value for the None case:


    val w = t.getOrElse("Nothing here!")



Python) 


dict = {‘Mike’: 1233456,’John’: 567890}


list_mixed = [1, ‘this’, 2, ‘is’, 3, ‘list’, 4, ‘chapter’]

list_list      = [1, ‘this’, [ 5, ‘hell’], 9’]


tuple_strings = (‘this’, ‘is’, ‘tuple’, ‘lesson’)

tuple_mixed = (1, ‘this’, 2, ‘is’, 3, ‘tuple’, 4, ‘chapter’)







16. 상속 


Scala) 


class ScientificCalculator(brand: String) extends Calculator(brand) {
  def log(m: Double, base: Double) = math.log(m) / math.log(base)
}

Python) 


class Person:

    def __init__(self, name, age, gender):

        self.Name = name

        self.Age = age

        self.Gender = gender

    def aboutMe(self):

        print("저의 이름은 " + self.Name + "이구요, 제 나이는 " + self.Age + "살 입니다.")

         

class Employee(Person):

    def __init__(self, name, age, gender, salary, hiredate):

        Person.__init__(self, name, age, gender)

        self.Salary = salary

        self.Hiredate = hiredate

    def doWork(self):

        print("열심히 일을 합니다.")

    def aboutMe(self):

        Person.aboutMe(self)

        print("제 급여는 " + self.Salary + "원 이구요, 제 입사일은 " + self.Hiredate + " 입니다.")

 




17. 쓰레드 

Scala) 


scala> val hello = new Thread(new Runnable {

  def run() {

    println("hello world")

  }

})

hello: java.lang.Thread = Thread[Thread-3,5,main]


scala> hello.start

hello world 

------------------------------


import java.net.{Socket, ServerSocket}

import java.util.concurrent.{Executors, ExecutorService}

import java.util.Date


class NetworkService(port: Int, poolSize: Int) extends Runnable {

  val serverSocket = new ServerSocket(port)


  def run() {

    while (true) {

      // This will block until a connection comes in.

      val socket = serverSocket.accept()

      (new Handler(socket)).run()

    }

  }

}


class Handler(socket: Socket) extends Runnable {

  def message = (Thread.currentThread.getName() + "\n").getBytes


  def run() {

    socket.getOutputStream.write(message)

    socket.getOutputStream.close()

  }

}


(new NetworkService(2020, 2)).run


---------------------------------------


import java.net.{Socket, ServerSocket}

import java.util.concurrent.{Executors, ExecutorService}

import java.util.Date


class NetworkService(port: Int, poolSize: Int) extends Runnable {

  val serverSocket = new ServerSocket(port)

  val pool: ExecutorService = Executors.newFixedThreadPool(poolSize)


  def run() {

    try {

      while (true) {

        // This will block until a connection comes in.

        val socket = serverSocket.accept()

        pool.execute(new Handler(socket))

      }

    } finally {

      pool.shutdown()

    }

  }

}


class Handler(socket: Socket) extends Runnable {

  def message = (Thread.currentThread.getName() + "\n").getBytes


  def run() {

    socket.getOutputStream.write(message)

    socket.getOutputStream.close()

  }

}


(new NetworkService(2020, 2)).run



Python) 


from thread import start_new_thread


def heron(a):

    """Calculates the square root of a"""

    eps = 0.0000001

    old = 1

    new = 1

    while True:

        old,new = new, (new + a/new) / 2.0

        print old, new

        if abs(new - old) < eps:

            break

    return new


start_new_thread(heron,(99,))

start_new_thread(heron,(999,))

start_new_thread(heron,(1733,))


c = raw_input("Type something to quit.")


--------------------

import time

from threading import Thread


def sleeper(i):

    print "thread %d sleeps for 5 seconds" % i

    time.sleep(5)

    print "thread %d woke up" % i


for i in range(10):

    t = Thread(target=sleeper, args=(i,))

    t.start()


--------------------


class PrimeNumber(threading.Thread):

    prime_numbers = {} 

    lock = threading.Lock()

    

    def __init__(self, number): 

        threading.Thread.__init__(self) 

        self.Number = number

        PrimeNumber.lock.acquire() 

        PrimeNumber.prime_numbers[number] = "None" 

        PrimeNumber.lock.release() 

 

    def run(self): 

        counter = 2

        res = True

        while counter*counter < self.Number and res: 

            if self.Number % counter == 0: 

               res = False 

            counter += 1 

        PrimeNumber.lock.acquire() 

        PrimeNumber.prime_numbers[self.Number] = res 

        PrimeNumber.lock.release() 

threads = [] 

while True: 

    input = long(raw_input("number: ")) 

    if input < 1: 

        break 

 

    thread = PrimeNumber(input) 

    threads += [thread] 

    thread.start() 

 

for x in threads: 

    x.join()


18.  클로저  (외부의 변수에 자유로이 접근하라~) 


Scala) 


object Test {

   def main(args: Array[String]) {

      println( "muliplier(1) value = " +  multiplier(1) )

      println( "muliplier(2) value = " +  multiplier(2) )

   }

   var factor = 3

   val multiplier = (i:Int) => i * factor

}


C:/>scalac Test.scala

C:/>scala Test

muliplier(1) value = 3

muliplier(2) value = 6




19.  패턴 매칭  ( 스마트한 Switch 문 ) 


Scala) 

object Test {

   def main(args: Array[String]) {

      println(matchTest(3))


   }

   def matchTest(x: Int): String = x match {

      case 1 => "one"

      case 2 => "two"

      case _ => "many"

   }

}


C:/>scalac Test.scala

C:/>scala Test

many




20.  트레잇   ( 다중상속 가능 객체 , 공통적으로 사용되는 것들이 현실에 존재함..) 


Scala) 


 trait Equal {

  def isEqual(x: Any): Boolean

  def isNotEqual(x: Any): Boolean = !isEqual(x)

}


class Point(xc: Int, yc: Int) extends Equal {

  var x: Int = xc

  var y: Int = yc

  def isEqual(obj: Any) =

    obj.isInstanceOf[Point] &&

    obj.asInstanceOf[Point].x == x

}


object Test {

   def main(args: Array[String]) {

      val p1 = new Point(2, 3)

      val p2 = new Point(2, 4)

      val p3 = new Point(3, 3)


      println(p1.isNotEqual(p2))

      println(p1.isNotEqual(p3))

      println(p1.isNotEqual(2))

   }

}




21.  정규표현식 


Scala) 



import scala.util.matching.Regex


object Test {

   def main(args: Array[String]) {

      val pattern = new Regex("(S|s)cala")

      val str = "Scala is scalable and cool"

      

      println((pattern findAllIn str).mkString(","))

   }

}





Python) 


import re

programming = ["Python", "Perl", "PHP", "C++"]


pat = "^B|^P|i$|H$"


for lang in programming:

    

    if re.search(pat,lang,re.IGNORECASE):

        print lang , "FOUND"

    

    else:

        print lang, "NOT FOUND"



The output of above script will be:


Python FOUND

Perl FOUND

PHP FOUND

C++ NOT FOUND



22.  함수형 고계함수들


 Scala) 

val nums = List(1, 2, 3, 4, 5)

//map
val nums_map = nums.map(x => x * 10)
println(nums_map)//List(10, 20, 30, 40, 50)

//reduce
val nums_reduce = nums.reduce( _ + _ )
println(nums_reduce)//15

//filter
val nums_filter = nums.filter( x => x < 3)
println(nums_filter)//List(1, 2)

//fold
val nums_fold = nums.fold(1){ (sum , x) => sum + x}
println(nums_fold)//16

//group
val nums_trans = nums.groupBy(x => x < 3)
println(nums_trans) //Map(false -> List(3, 4, 5), true -> List(1, 2))


Python) 

nums = [1, 2, 3, 4, 5]


#map
nums_map = map(lambda x : x * 10, nums)
print(nums_map)

#reduce
nums_reduce = reduce(lambda x , y : x + y , nums)
print(nums_reduce)

#filter
nums_filter = filter(lambda x : x < 3 , nums)
print(nums_filter)

#fold
nums_fold = reduce(lambda x , y : x + y, nums, 1)
print(nums_fold)

#group
from itertools import groupby
for key, igroup in groupby(nums, lambda x: x < 3):
print key, list(igroup)

# True [1, 2]
# False [3, 4, 5]








'Zookeeper' 카테고리의 다른 글

Zookeeper 에서 쿼럼을 과반수로 하는 이유  (0) 2015.05.04

주키퍼에서 과반수 쿼럼을 사용하는 이유에 대해 설명해보겠습니다.


설명을 위해 과반수 쿼럼이 아닌 경우에 발생하는 문제를 예를 들어 설명합니다.


서버 5대로 앙상블을 구성하고, 쿼럼을 2대로 구성한다고 가정해 보겠습니다.


1. 사용자가 주키퍼에게 쓰기 작업 요청을 합니다.

2. 주키퍼는 쓰기 작업 요청을 쿼럼(2대)으로 복제합니다.

3. 5대 중 쿼럼(2대)에 쓰기 작업 요청이 복제되면 주키퍼는 쓰기 작업이 성공했다고 반환 합니다.

4. 이 때 (쓰기 작업 내용을 가지고있는)쿼럼(2대)에 장애가 발생하게 되면 쓰기 작업 요청이 유실됩니다.

5. 주키퍼는 쿼럼에 장애가 발생했으므로, 새로운 쿼럼을 구성합니다. 

6. 쿼럼은 2대로 구성 가능하기 때문에 남아있는 3대의 서버 중 2대로 새로운 쿼럼을 구성하고 주키퍼 서비스를 합니다.

7. 사용자가 쓰기 요청한 내용을 조회하는 경우 쓰기 작업 내용을 확인할 수 없습니다.


이렇게 주키퍼 서비스는 정상적인데, 작업 내용이 유실되므로 주키퍼는 유실을 방지하기 위해 과반수 쿼럼을 사용합니다.


그러면 쿼럼을 3대로 구성하는 경우는 어떻게 될까요?


1. 사용자가 주키퍼에게 쓰기 작업을 요청합니다.

2. 주키퍼는 쓰기 작업 요청을 쿼럼(3대)으로 복제합니다.

3. 5대 중 쿼럼(3대)에 쓰기 작업 요청이 복제되면 주키퍼는 쓰기 작업이 성공했다고 반환 합니다.

4. 이 때, 쿼럼 중 2대의 서버에 장애가 발생합니다.

5. 주키퍼는 쿼럼으로 구성되었던 서버 1대와 쿼럼에 포함되지 않았던 서버 2대로 새로운 쿼럼을 구성합니다.

6. 쿼럼으로 구성되었던 서버 1대는 사용자가 요청한 쓰기 작업을 알고 있습니다.

7. 사용자가 쓰기 작업 요청 내용을 새롭게 구성된 쿼럼에 포함된 다른 서버들에게 복제합니다.

8. 주키퍼는 장애상황에도 사용자가 요청한 쓰기 작업을 유실하지 않습니다.


쿼럼(3대)이 모두 장애가 발생하는 경우는 어떻게 될까요?


1. 5대 중 쿼럼(3대)에 장애가 발생합니다.

2. 남은 2대의 서버는 과반수(3대)를 만족하지 못하기 때문에 쿼럼을 구성하지 못합니다.

3. 주키퍼 서비스는 이용 불가 상태가 됩니다.


주키퍼 서비스 이용 불가 상태이기 사용자는 주키퍼를 이용할 수 없습니다. 복구하려면 장애가 발생한 서버 중 하나를 복구해서 쿼럼을 구성해야합니다.


















최근, 세계 주요 선진국 및 전력회사에서는 스마트 그리드 사업을 연구 또는 추진 중이거나 계획단계에 있다. 스마트 그리드 시스템은 시대적 변화에 대응하여 에너지 절감과 대고객 서비스 수준 향상 그리고 전력설비의 효율적 운영을 위한 필수 인프라이다.


스마트 그리드의 구현을 위한 필수 영역인 AMI 및 스마트 미터링 기술은 광대역 고속 통신망을 이용하여 지능화된 스마트 미터로부터 수집된 다양한 데이터와 부하 측의 각종설비를 제어하기 위한 제어신호를 신뢰성 있게 전송 가능하도록 한다. 스마트 그리드 시스템의 전 분야에서 핵심적인 역할을 수행하게 될 스마트 미터와 스마트 미터링 및 AMI 시스템의 개념적 정의를 명확히 하고, 향후의 스마트 미터링 관련 산업의 시장동향과 전망에 대해 고찰해 보고자 한다.

글: 김석곤 선임연구원
한국전력공사 전력연구원 송배전연구소(blade@kepco.co.kr)

용어 소개 : 스마트 미터 & 미터링 

스마트 미터란 AMI를 구축하는데 있어서 이종의 네트워크시스템과 인터페이스를 지원하는 통신시스템과 함께 AMI를 구성하는 핵심 설비이다. 
Meter란 통상적으로 미터링(계량)을 위한 계량설비를 나타내는 말로 스마트그리드 시스템 기술에서 설명되고 있는 Meter는 주로 전력량계(Electricity Meter or Watt Hour Meter)을 의미한다.  


미터링 기술이 산업현장에 본격적으로 활용되기 시작한 시점에서의 용어적 정의를 살펴보면 미터링이란 물리적인 값에 대한 분량을 계산하는 것을 말하며, 측정이라고도 표현된다. 계측이 가장 적합한 측정방법을 연구하여 그것을 실현할 장치와 설비를 설계 및 제작, 측정하고 측정결과에 따른 연산과 필요한 정보를 얻는 광의적 개념이라면, 계량은 특정시간 동안 축적된 측정 데이터의 양을 나타낸다. 따라서 계량기는 계량의 기능을 수행하는 기기를 말하며, 계량기의 종류에는 전기요금의 산정을 위한 전력량계 외에도 가스 계량기, 수도 계량기 등이 있다.


근래에는 일반적인 계량의 수준보다는 더욱 지능화된 의미의 스마트 계량이라는 용어가 자주 나타난다. 스마트 미터링 기술은 앞서 설명한 바와 같이 전력량은 물론, 가스와 수도 계량 등이 포함되는 범위이나 최근 전 세계적으로 추진되고 있는 스마트 그리드 사업의 예에서는 전력망 지능화에 초점을 맞추고 있으므로 데이터의 측정 및 수집 능력이 향상된 지능형 전력계량 시스템 기술이라 불러도 무방하다 하겠다.


 즉, 스마트 미터링 기술은 현재의 집중화된 전력공급망의 한계를 극복하고 공해배출의 억제 및 전력 효율의 향상을 도모할 수 있는 미래 혁신형 기술이라 할 수 있으며, 또한 전력사용자의 전력에너지의 효율적인 이용과 재생 에너지와 같은 다양한 전력공급방식 환경변화에 대해 사용자 입장에서 전력수용변화에 대체하는 기술로서 인텔리그리드와 스마트그리드 등의 기술과 연계하여 발전되고 있는 전력 IT 기술의 일부분으로 단위 시스템으로 구분한다면 지능형 전력량계(Smart Meter)와 지능형 통신 시스템(Intelligent Communication System)으로 구분할 수 있다.


한편, 스마트 미터란 유/무효 전력 사용량과 역율 등을 주로 측정하는 현재 일반적으로 사용 중에 있는 전자식 전력량계보다 더욱더 지능적인 부가 기능을 보유한 전력량계를 일컫는다. 미래 지향적인 미터란 의미의 진보된 미터(Advanced Meter) 또는 스마트 미터란 용어가 혼용되어 사용되고 있으나, 근래에는 스마트 미터라는 용어로 표준화 되어가고 있다고 볼 수 있다. 


국외의 스마트 전력량계 정의 사례를 살펴보면, 미국의 NETL은 2008년 'Advanced Metering Infrastructure'에서 스마트 미터는 '전자식 프로그램이 가능한 장치로 매우 많은 기능을 수행할 수 있는 미터'라고 간략히 규정하고 있고 DoE에서는 'Guidance for Electric Metering in Federal Building'을 통하여 '스마트 미터는 시간 간격 간 데이터(전력의 경우 최소 시간별 데이터)를 측정하고 기록하는 능력을 가지고 진보된 미터링 시스템(Advanced Metering System)에 쉽게 통합될 수 있는 형식으로 데이터를 원격지점으로 통신할 수 있어야 한다'라고 정의하고 있으며 스마트 미터링 관련 글로벌 연구기관인 글로벌 스마트 에너지에서는 스마트 전력량계란 '소비되는 에너지의 양과 발생시간을 측정하고 내부적으로 측정할 수 있는 능력을 가져야 하고 측정결과를 자동 전송할 수 있어야 하며, 양방향 통신이 가능하여야 한다'라고 정의하고 있다.

시스템 기술 소개

스마트 미터링 시스템
미국의 FERC는 'Advanced Metering'란 "고객이 소비하는 량(가능한 한 다른 항목들도 포함)을 시간 간격 또는 더 짧은 시간간격으로 주기적으로 기록하고 측정된 값을 통신망을 통해 중앙 수집지점으로 매일 또는 더 짧은 시간간격으로 전송할 수 있어야 한다"라고 정의하고 있으며, DoE에서는 'Guidance for Electric Metering in Federal Building'에서 "진보된 미터링 시스템(Advanced Metering system)은 즉석 요구에 의하거나 사전에 예약된 스케줄에 의해 통신망 시스템을 통하여 진보된 미터로부터 차별화된 에너지 사용 데이터를 수집하는 시스템이다"라고 정의하고 있다.


즉, 전력 사용자의 전력 에너지의 효율적인 이용과 신재생 에너지와 같은 다양한 전력공급방식에 맞춰, 전력 에너지 분산화, 전력 시스템의 고 효율화를 요구하고 있고, 이것은 최종적으로 전력 사용에 대한 방안을 제시하는 기술이 필요하게 되었다. 이런 필요성에 따라 스마트 미터링 기술이 발전하게 되었으며. 스마트 미터링 기술의 도입으로 인한 결과론적 기대효과는 상기와 같은 다양한 전력공급 방식을 갖는 환경에서 사용자 입장에서 전력수용 변화에 적극 대처 가능하다. 


AMI 시스템 및 활용 기술
AMI 기술은 AMR 및 AMM 기술수준보다 더 진보 된 기술로 단순히 한 가지 기술이 아닌, 현존하거나 새로운 프로세스 및 애플리케이션 등이 통합되어 구성된 기반 시스템이다.
 

AMR 기술이 검침원이 직접 검침하던 것을 자동화한 시스템으로서 잔력량계의 지침을 자동검침하고 전기사용량의 산정과 과금 기능을 갖는 시스템이라면 AMI는 그 구축목표가 검침자료를 이용한 전력생산 및 유통기반을 정보화하고 활용적 측면에서는 현재의 다양한 수요정보 활용 뿐 아니라, 미래에 발생 가능한 수요정보 활용 서비스를 담당하여 특정 목적에 따라 시스템을 구현하기보다, 수요정보의 획득/수집/가공이 가능한 수단을 마련하고, 사용자에게 다양한 서비스를 제공하는 미래 지향적인 시스템이다.


한편 AMM 기술은 AMR에서 AMI로 진행되는 중간단계의 활용기술로 전력량계 데이터의 효율적 처리, 유용한 정보의 추출 그리고 다양한 기술분야와 연계활용이 시스템으로서 초기 단계의 스마트 미터링 시스템 활용과 응용을 위한 시스템이다.

스마트 미터링 시스템 시장 동향

스마트 미터링 시스템 표준화
국내에서는 전자식 전력량계의 국제표준 도입은 이미 완료한 상태이며 최근 스마트그리드 사업 추진의 일환으로 스마트 미터링 분야를 포함한 스마트 그리드 기술 전반에 대한 표준관련 기술개발과 국제 표준화에 대한 관심을 높이고 있다.


스마트 미터의 표준화 추진은 상위 개념인 스마트 미터링 시스템의 아키텍처의 일부로서 추진되어야 하며, 표준화의 중요성에 대한 공감대 형성이 중요하다.


한편 미국, 유럽을 중심으로 각 국의 정부 및 기업에서는 표준화에 대한 특허 선점과 이를 바탕으로 표준화 활동을 적극적으로 펼쳐가고 있다.
그리고 국내에서도 스마트 그리드 사업의 일환으로 'AMI 시스템 구축' 연구과제를 추진 중에 있으며 스마트 미터링 시스템 관련분야에 대한 특허와 표준에 대응할 수 있는 정보의 수집과 및 정책 수립을 위한 노력을 더욱 더 확대해 나갈 시점이라 판단되며 스마트 미터링 기술의 표준화는 개방형의 유연한 구조를 갖도록 하고, 제조업체간, 응용분야간 상호 운용성을 확보하며 규모의 경제 달성, 기술혁신과 경쟁환경 조성, 국가 내 또는 국가간 그리드 상호작용면에서 공통 경험을 확보하도록 추진되는 것이 바람직하다.


유럽에서는 이탈리아 ENEL사의 Telegestore 사업이 성공적으로 진행되는 과정에서 일부 참여업체들이 세계시장으로 확대하려는 노력의 일환으로 표준화 활동을 시작하였으며, 미국에서는 EPACT 2005, EISA 2007을 제정하면서 정부차원에서 표준화를 주도하려는 노력을 하고 있으며 구체적으로는 DoE의 Gridwise, EPRI의 Intelligrid를 시스템 아키텍처로 하여 세계 AMI 표준화를 주도하려 하고 있다.

이탈리아의 스마트 미터링 시스템 사업추진 사례
스마트 미터링 시스템은 이탈리아 최대 배전 전력회사 ENEL사가 2001년도부터 2006년 까지 5년간에 약 3,000만 세대에 1세대 스마트형 미터를 대량 보급 설치한 Telegestore 사업으로 시작되었으며, 이것이 스마트 미터링 시스템의 시초라고 할 수 있다.
그림 2 및 그림 3은 ENEL사의 Telegestore 사업을 위한 시스템 구성도 및 활용효과를 설명한 것이다. 


ENEL사의 미터링 시스템은 Telegestore 미터, 데이터 집중장치(Concentrator), 중앙 시스템 등으로 구성된다. 데이터는 AMM에서 처리되며, ENEL사가 IBM과 협력하여 개발하였다. ENEL사의 Telegestore 사업이 추진될 당시에는 표준화에 대한 개념보다는 새로운 비즈니스 모델을 성공시켜야 된다는 생각이 우선시 되었다.


스마트 미터를 활용한 Telegestore 사업의 주요효과로는 고객의 입장에서는 실질적인 에너지 소비량의 인지기능 외에 원격계약관리를 가능하게 하고 과금비용을 절감케 하며, 전력회사 및 전력시스템의 운용 측면에서는 Peak 저감을 통한 부하율 향상과 에너지 사용효율 향상 및 이산화탄소 저감효과와 고객만족과 운용비용 절감 등을 기대 할 수 있다.


호주의 스마트 미터 설치 사업
호주에서는 약 16억 유로의 예산을 들여 2016년까지 전국적으로 약 500만대의 기존 전력량계를 스마트 미터로 교체할 예정이다. 이미 2007년까지 Energie AG & SIMENS사를 통해 스마트 미터 설치 시험 프로젝트를 완성하였으며, 호주 내에서는 Victoria 주에서 양방향 통신과 원격투입 기능이 포함된 스마트 미터의 설치를 주도하고 있다. 그림 5는 스마트 그리드 산업분야별 글로벌 기업 및 산업구조 연계 체계를 나타낸 것이다.


AMI 시스템 시장 현황
그림 5는 스마트 그리드 산업분야별 글로벌 기업 및 산업구조 연계 체계도를 나타낸 것이다. 이중 일부 기업은 각 해당분야외의 연계분야에서도 주도적 역할을 하고 있다.


연계도에서 나타나는 바와 같이 스마트 미터링 시스템과 연계된 스마트 그리드 관련 산업분야는 스마트 미터, 스마트 홈, 커넥티비티, 애플리케이션 & 소프트웨어 그리고 AMI 분야로 구분 가능하며, 애플리케이션 & 소프트웨어 분야는 Demand Response, Data Management, Clean Energies Integration & Storage 분야 등으로 구분된다. 이중에서 AMI는 스마트 그리드 및 스마트 미터링 시스템에서 양방향 통신을 가능케 하는 핵심설비로서 각 개체간 또는 분야 간 통신 또는 접속은 AMI와 Demand Response 등의 스마트 그리드 애플리케이션을 통하여 이루어진다.  

스마트 미터 및 AMI 시스템 시장 전망
스마트 미터 시장은 향후 지속적으로 성장할 것으로 예상되며, 스마트 미터 시장은 스마트 그리드 환경구축을 위한 관련 산업분야의 시장규모와 그 맥을 같이한다. 2020년까지 미국이 스마트 미터 시장 성장을 주도하게 될 것으로 예상되며, 뒤를 이어 유럽지역에서는 많은 국가들이 100% 설치 완료를 목표로 사업을 진행하고 있다. 한편 아시아 지역에서는 중국의 관련산업 활성화 정책을 시행함에 따라 급격한 성장을 할 것으로 예상되며 한국과 호주, 일본에서 주도적인 역할을 할 것으로 예상된다.


그림 6은 2010년∼2015년 간의 세계 전 지역에서의 AMI 시스템 구축을 위한 무선통신 기술 분야의 수익 예상치를 보여준다. 전체 시장규모에서 유럽지역 및 북미지역에서의 산업규모가 80% 이상을 차지하고 있으며, 무선통신설비의 시장규모는 곧 스마트 미터 산업의 성장률과 비례적 관계를 갖는다. 


아래 그림 7은 전력량계 설치 수량 기준 전 세계 전력량계 시장규모를 나타낸 것이다. 2008년부터 2015년까지 전력량계 총 설치수량은 약 1,300만 대이며 기존의 전력량계는 지속적으로 감소하는 대신 스마트 미터는 지속적인 성장세를 보일 것으로 예상되며 2015년에는 240만의 전력량계가 설치될 전망이다. 

표 1은 유럽지역 국가별 스마트 미터 생산 예상수량을 나타낸 것이다. 2007년부터 2012년 까지는 이탈리아와 북유럽국가들이 주도하고 있으며 프랑스와 영국 등의 국가에서는 2012년 이후로 집중적인 활용이 예상된다. 한편 유럽지역에서의 출하량 대비 스마트 미터의 점유율은 2007년의 19%에서 2013년에는 95까지 성장할 것으로 예상되며 설비 보급수량 측면에서는 2007년 14%에서 2013년에는 32%까지 도달하여 6년간의 보급율은 200% 이상 성장할 것으로 예상된다.  

국내 스마트 미터링 시스템 연계 에너지정보표시장치(IHD) 보급사업
국내에서 추진 중인 'AMI 시스템 구축' 사업은 홈 내 기기간의 인터페이스를 위한 홈 네트워크 시스템, 온도조절장치를 비롯한 댁내 제어기기와의 통신, 지능형 전력량계, 미터로부터 지역 데이터 수집 장치에 이르는 통신 네트워크, 데이터 센터와 연계하기 위한 백홀 통신망, 미터 데이터 관리 시스템(MDMS), 그리고 현존하거나 새로운 소프트웨어 애플리케이션 플랫폼으로의 데이터 통합 등을 모두 포함한다.


한편 스마트 그리드 및 스마트 미터링 사업의 활용성 증대를 위해서는 각 가정내 에너지정보를 표시하는 디스플레이 장치(IHD)의 개발 및 보급 사업이 중요한 역할을 수행한다.

IHD의 개념
IHD는 소비자와 공급자간의 실시간 정보소통을 통해 소비자의 자발적인 수요반응(DR)을 유도함으로써 소비자와 공급자 모두에게 편익을 제공하기 위한 AMI 시스템의 소비자 측 정보채널 역할을 하는 기기이며 주요 운용방식은 PLC 타입과 지그비(ZigBee) 타입 등이 있다.


그림 8는 국내 전력사에서 시행한 제주 구좌지역 스마트 그리드 실증단지의 IHD 보급사업 대상지역을 나타낸 것이다. 지역내 스마트 그리드 실증단지에 포함된 6,000호중 실증대상에서 제외된 3,000호를 주요 대상으로 하였으며 사업의 주요 기대효과로는 주민 형평성 확보와 민원예방효과 그리고 스마트 그리드 붐 조성을 위한 목적도 병행하고 있다.


그림 10은 강릉지역 녹색 시범도시 지역에 IHD 보급사업을 시행한 주요지역을 나타낸 것으로 지자체(강릉시)의 제의에 의해 전력회사 시행한 사례로 시범도시내 약 4,000호를 대상으로 하고 있다. 본 사업은 지방자치 단체와의 협력모델을 구축하고 타 사업과의 연계를 통한 효과를 극대화하는 목적이 있다.

IHD 활용 시스템 구성도
그림 11 및 그림 12는 국내 전력사에서 시행한 IHD 활용 연계시스템의 구성도와 PLC 방식의 댁내 IHD 설치 구조도를 나타낸 것이다. IHD는 PLC와 ZigBee 등의 통신망을 통해 데이터수집장치(DCU)를 통해 지사 또는 지점의 AMI 서버에 데이터를 송수신하고 지사 AMI 서버는 본사의 영업서버와 IHD 서버와 연계된다.  


또한 IHD를 이용하여 전기, 가스, 수도의 통합검침도 가능하며, 각 누적 사용 계량값 및 요금 정보와, 현재 및 예측 사용량 및 요금 표시, 각 검침데이터의 상세정보 지원 그리고 검침계기 별 시간정보, 일간정보, 월간정보 등의 정보제공 기능 등을 포함한다.


결론

스마트 미터링 기술을 종합해 보면 스마트 미터가 기존의 전자식 전력량계와 크게 다른 점은 다양한 데이터의 실시간 측정 및 저장능력과 함께 전력계통 운용서버와 홈 내 다른 지능형 기기들과의 실시간 양방향 통신능력 측면에서 찾을 수 있다. 즉, 스마트미터를 가장 기본적이고 일반적으로 정의하면 '사용한 전력량을 다양한 시간 구간 또는 간격으로 계량하고 이를 전력량계에 프로그램할 수 있으며, 계기 정보를 다른 기기들과 통신할 수 있는 전자식 계기'라 할 수 있으며 또한 '계기와 전력회사 간에 양방향 통신을 지원하는 새로운 통신 기술이 동반된다'라고 할 수 있다.

한편, 스마트 미터와 홈 내 지능형 디바이스와의 연결은 인터페이스를 통하여 이루어지기 때문에 인터페이스에 대한 표준은 중요하며, 결국 통신수단 결정과 게이트웨이 기능을 어느 위치(장치)에 두는가를 결정하는 문제로 귀결된다고 할 수 있다.


스마트 미터의 단기적 발전전망은 스마트 미터링 사업을 추진하는 주요 국가들을 중심으로 진행될 것이며, 일반적인 주요 기능적 변화추이는 반전자식에서 디지털형 전력량계로 그리고 개별 분산 관리에서 통합 관리로, 양방향 통신과 함께 이동통신망의 활용에서 유무선망의 활용 등으로 변화할 것으로 예상되며, 이외에도 기능적인 발전전망으로는 측정값의 내부저장기능 강화, 다중 요금제 지원기능 추가, 전력품질 감시 및 기록, 원격 차단/투입, 도전 감시, 부하제어, 개방형 표준 채택 그리고 시간에 따른 요금 부과, 소비자와 공급자(전력회사)에 필요한 소비(사용)량 데이터 취득 및 실시간 활용, 순수 전력 사용량 측정 및 전력품질 감시 기능, 원격 차단/투입 및 전력손실(정전)/복구 통보, 부하제어 및 수요관리 시스템(DR) 적용, 도전 예방 및 감시 등이 있다.


그리고, 스마트 미터링 시스템으로의 진화 방향에서 가장 중요한 키워드는 AMI이다. AMR 시스템은 검침 및 과금이라는 특정 용도에 목적을 둔 시스템으로서 검침업무 자동화의 측면에서 이해되었으나, 이후 AMM 시스템이 출현하였고 이러한 시스템은 곧 특정 애플리케이션에 목적을 두지 않는 수요정보 인프라 개념으로의 발전을 의미한다. EU 및 미국의 적용사례에서 보는 바와 같이 스마트 미터링 시스템은 전 세계적으로도 스마트 미터의 개발 및 보급을 통하여 최초 적용이 이루어지고 있다.


스마트 미터의 활용성 측면의 중장기적 고찰은 미국 스마트 미터링 사업 추진사례를 주목해 볼 필요가 있다. 계획 단계에서의 미국 스마트 미터링 시스템 적용사유는 비용 절감(42%), 고객 서비스 향상(14%) 등이다. 그러나 초기 설치목적과 비교하여 실질적인 활용성과 측면에서는 '도전 감지' 목적으로 설치 및 활용중인 것이 52%, 계획 중 19% 그리고 적용고려 13% 등 총 84%의 높은 비율을 보이고 있고 계속해서 '정전감시' 분야와 '원격 On/Off 제어', '산업용과 상업용에 대한 TOU 적용', '주택용에 대한 TOU 적용', '고객 에너지 관리' 등의 순으로 나타나고 있다.


그리고, 스마트 미터와 연계한 IHD를 활용하여 운용하는 시스템에서의 기대효과로는 소비자의 입장에서는 수요반응을 통한 합리적 전력소비를 유도하여 에너지비용을 절감하며, 전력회사 등 공급자의 입장에서는 설비운영 효율을 향상시키고, 투자비를 절감함으로서 경영효율성을 제고할 수 있다. 또한 글로벌 환경보호 측면으로는 온실가스 배출량 감축을 통한 기후변화에 대응하고 경제적 관점에서는 관련산업의 해외수출 활성화를 통해 녹색성장의 기반을 마련할 수 있다. 


스마트 미터링 관련산업 및 시장의 발전전망을 고찰해 보면, 2005년부터 약 5년간 미국의 스마트 미터링 관련산업 시장전망을 소요예산을 기준으로 참조해 보면 스마트 미터와 소프트웨어, 통신 하드웨어 분야가 지속적으로 일관성 있게 성장하는 반면, 유지보수 측면의 소프트웨어 지원분야와 네트워크 분야의 성장세가 급격히 증가 할 것으로 예상된다.


또한, 고객별 보급률 전망은 교통, 항공 등 운송 분야의 보급률이 가장 높게 예상되며 그 뒤를 이어 산업용과 상업용 고객의 순으로 예상된다. 단, 국가별 환경적 특성 또는 고객구성의 차이 측면에서 주거용 고객에 대한 스마트 미터링 시장의 적용전망 차이가 발생할 것으로 예상된다. 미국시장의 특징은 전력회사의 수가 상대적으로 매우 많고 주택용 고객의 전력사용량과 전기요금이 대체로 높아 주택용 고객에 대한 스마트 미터링 사업이 초기부터 비교적 활발히 진행된 편이나, 국내의 경우는 국내 주거문화의 특징으로 인해 아파트 등 상당수의 주택용 고객이 고압고객으로 관리되고 있으며, 단독 주택용 고객의 경우에도 고객 당 전력사용량과 전기요금 수준에서 미국 등 주요 선진 국가들에 비해 값이 저렴한 편으로 우리나라에서의 AMI 수준 스마트 미터링 사업의 추진은 300∼400kWh급 이하의 주택용 등 저용량 고객보다는 대규모 주택용과 상업용, 산업용 등 대용량 고객을 중점적으로 적용해 나가는 것이 효율적인 방법이 될 것으로 예상된다.



출처: http://www.epnc.co.kr/atl/view.asp?a_id=9026

'IoT' 카테고리의 다른 글

Power Patch - 스마트 미터  (0) 2015.06.15
ICT란 무엇인가  (0) 2015.06.11
스마트홈 빌딩 & BEMS 시스템  (0) 2015.06.11
무선통신 프로토콜 비교 모음  (0) 2015.06.10
Z-Wave 장단점  (0) 2015.05.21
순서

1) 하둡 RPC
2) 하둡 스트리밍
3) 하둡 & 보안
4) 하둡 HDFS 읽기
5) 하둡 HDFS 쓰기
6) 하둡 IO  (Writable / Avro)
7) 하둡 & 가용성  (Zookeeper) 
8) 하둡 쉘 스크립트 및 환경


하둡은 HDFS 라는 분산파일시스템과 맵리듀스라는 그것을 이용하여 계산을 하는 도구를 가지고있습니다.  (YARN 이전) 계산에는 간단한 배치성 작업이 주를 이루며 다양한 머신러닝 알고리즘 (머하웃 라이브러리) 을 실행할수도 있습니다.계산복잡도,알고리즘형태에 따라서  지라프,하마같은 다른 도구를 사용할수도 있으며 , 하둡 YARN 과 함께 다양한 빅데이터 솔루션들이 하모니를 이루고 있습니다. 
Storm-yarn 같은 도구를 사용하여 실시간 분석을 용이하게 할수도있으며, 메모리를 적극적으로 활용한 Spark 라는 제품도 각광을 받고 있습니다.


... 작성중 ...

+ Recent posts