[HBase] Client 개발 참고 사항[HBase] Client 개발 참고 사항

Posted at 2014. 3. 12. 14:50 | Posted in Server
HTable
- 인스턴스는 한번만 생성하고, 프로그램이 끝날때까지 재사용하라.
- Thread 별로 만들어라.(HTablePool 사용 하라.)
- HTablePool 사용 예(HTableInterfaceFactory 인터페이스로 한번 감싸서 사용하면 편하다)
public class BaseHTableInterfaceFactory implements HTableInterfaceFactory {
	
	private HTablePool hTablePool;
	
	public BaseHTableInterfaceFactory(Configuration config, int maxSize) {
		hTablePool = new HTablePool(config, maxSize);
	}
	
	@Override
	public HTableInterface createHTableInterface(Configuration config, byte[] tableName) {
		return hTablePool.getTable(tableName);
	}

	@Override
	public void releaseHTableInterface(HTableInterface table) throws IOException {
		table.close();
	}
}
- 내부적으로 병렬처리 thread를 가진다. 
  public HTable(Configuration conf, final byte [] tableName)
  throws IOException {
    ...
    int maxThreads = conf.getInt("hbase.htable.threads.max", Integer.MAX_VALUE);
    if (maxThreads == 0) {
      maxThreads = 1; // is there a better default?
    }
    long keepAliveTime = conf.getLong("hbase.htable.threads.keepalivetime", 60);

    // Using the "direct handoff" approach, new threads will only be created
    // if it is necessary and will grow unbounded. This could be bad but in HCM
    // we only create as many Runnables as there are region servers. It means
    // it also scales when new region servers are added.
    this.pool = new ThreadPoolExecutor(1, maxThreads,
        keepAliveTime, TimeUnit.SECONDS,
        new SynchronousQueue(),
        Threads.newDaemonThreadFactory("hbase-table"));
    ((ThreadPoolExecutor)this.pool).allowCoreThreadTimeOut(true);

    /* 설명:
     기본 생성자의 설정이다.
     coreThread=1, maxThreads=Integer.MAX_VALUE 이고, SynchronousQueue를 사용한다.
     따라서 region 서버 수만큼 Thread를 생성하고 keepAliveTime 동안 유지했다가 Thread를 죽인다.
     뜨문 뜨문 호출되는 경우 Thread가 만들어졌다 없어졌다를 반복할 것이므로, 환경에 맞게 조정해서 사용하라.
    */
  ...
  }

- HTable은 HConnection을 사용해서 원격서버와 통신하고, HConnectionManager가 이 커넥션을 관리한다.
- HBase 내부에서는 연결들이 맵 안에 저장되는데 현재 사용중인 Configuration 인스턴스가 키가된다. 동일한 Configuration을 참조하는 HTable 인스턴스를 여러개 만들었다면, 이들 모두는 HConnection 객체를 공유한다.

Put
- 쓰기 버퍼 크기를 늘리면, 클라이언트 뿐 아니라, 서버에서도 많은 메모리를 소비한다. 서버에서도 전송받은 데이터를 인스턴스화 하기 때문이다.(P.148)
- 크기가 큰 셀만을 저장한다면 로컬 버퍼는 덜 유용하다. 실제 데이터 전송 시간이 대부분일 것이고, 이럴 경우는 버퍼 크기를 늘리지 않는 쪽이 권장된다. (p.148)

batch()
- 일괄처리 연산은 Put처럼 쓰기버퍼를 사용하지 않는다. 동기적이며 바로 바로 처리한다.
- 각 작업에 대한 결과를 따로 보고 받을 수 있다. (Object results[])
- 일괄처리 명령은 NotServingRegionException(리전 이동을 알리는 예외)와 같은 일시적인 에러에는 여러 번 시도해본다. hbase.client.retries.number 속성이며 기본값은 10이다.

RowLock/Scanner
- hbase.regionserver.lease.period = 60000 (1분) 락 만료 시간 또는 스캐너 임대 만료 시간에 모두 사용됨.
  - 이 설정은 클라이언트측 Configure에 해봤자 무용지물이다. 리전서버 hbase-site.xml에 설정되어야 한다. (p200)

Scan
- Scan.next() 호출 시 RPC가 일어난다.
- setCaching() / hbase.client.scanner.caching (default=1) 를 조정하여 한번의 RPC에서 여러개의 row를 가져오도록 할 수 있다.
  - 데이터 큰 경우 클라이언트에 전송되는 데이터량이 많아져 수행시간이 길어지고, 메모리 문제로 이어질 수 있다. (p.200)
- setBatch()를 조절하여 한번에 읽어올 컬럼개수를 정할 수 있다. setBatch(5)일 때 컬럼이 17개면 Result인스턴스를 총 4번 받게되고, 세번은 5개, 한번은 2개를 반환할 것이다.(p201) --> RPC 횟수를 조절할 수 있다(p.202)

Filter
- 필터는 클라이언트측에서 생성된 후 RPC를 통해 서버로 전송돼 실행된다. (p.210)
- 사용자 정의 Filter는 jar로 묶어 리전서버 classpath에 넣어줘야 한다(hbase-env.sh). 재시작 필요 (p.244)
- 필터 요약(p.246)

보조처리기(Coprocessor)
- preCreateTable(), postCreateTable(), preGet(), postGet() ... 등
- 정적, 동적 로드 가능하나 0.92 버전 현재 동적 로드 API는 아직 없음. (p.262)

HTablePool
- maxSize는 HTableInterface 인스턴스 개수의 상한선의 의미하지 않는다. 5로 지정하고 getTable()을 10번 실행하면 HTable이 10개 생성된다. 이들을 반환하면 5개까지만 남고 나머지는 버려진다. 즉, Pool에 남겨둘 인스턴스의 상한선이다. (p.289)

HConnectionManager (p.292)
- 동일한 설정의 HTable은 모두 같은 HConnection객체를 공유한다. 
  - 이 공유는 같은 Configure객체를 사용할 때 적용된다. (p.293)
- hbase.client.pause = 1000
- hbase.client.retries.number = 10
- hbase.client.rpc.maxattempts = 1
- hbase.rpc.timeout = 60000
- hbase.client.prefetch.limit = 10


참고: 소스분석 & O'Reilly - HBase 완벽 가이드

//

[MySQL] 멀티 컬럼 select[MySQL] 멀티 컬럼 select

Posted at 2014. 3. 12. 11:03 | Posted in Server


CREATE TABLE `mytable` (
  `userid` bigint(20) NOT NULL,
  `groupid` bigint(20) NOT NULL,
  `state` tinyint(1) NOT NULL,
  `col1` bigint(20) DEFAULT NULL,
  `cal2` varchar(200) NOT NULL,
  `cal3` varchar(100) NOT NULL,
  `create_time` datetime NOT NULL,
  `update_time` datetime NOT NULL,
  PRIMARY KEY (`userid`,`groupid`),
  KEY `idx_userid_createtime` (`userid`,`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8


(1) 컬럼 조합 IN 쿼리

EXPLAIN
SELECT *
FROM mytable
WHERE (userId, groupid) IN ((1526291770280964803,2679212559264916326),(1526292017760867305, 2679249801360511502),(1526421185616035771,2679342905646882845));

id|select_type|table  |type|possible_keys|key |key_len|ref |rows|Extra
1 |SIMPLE     |mytable|ALL |NULL         |NULL|NULL   |NULL|172 |Using where

index를 타지 않는다.


(2) FROM절 subquery

EXPLAIN
SELECT a.* FROM (SELECT * FROM mytable WHERE userid IN (1526291770280964803,1526292017760867305,1526421185616035771)) a
WHERE a.groupid IN (2679212559264916326, 2679249801360511502, 2679342905646882845);

id|select_type|table     |type |possible_keys|key|key_len|ref|rows|Extra
1 |PRIMARY    |<derived2>|ALL  |NULL|NULL|NULL|NULL|15|Using where
2 |DERIVED    |mytable   |range|PRIMARY,idx_userid_createtime|PRIMARY|8|NULL|15|Using where

userid가 index를 탄다. groupid도 인덱스를 걸어줘야 할 듯.


(3) 각 컬럼에 대해서 IN query

EXPLAIN
SELECT *
FROM mytable WHERE userid IN (1526291770280964803,1526292017760867305,1526421185616035771) AND groupid IN (2679212559264916326, 2679249801360511502, 2679342905646882845);

id|select_type|table  |type |possible_keys|key|key_len|ref|rows|Extra
1 |SIMPLE     |mytable|range|PRIMARY,idx_userid_createtime|PRIMARY|16|NULL|9|Using where


결론) 3번이 가장 나아보임.


//

[MySQL] 실행계획(EXPLAIN)[MySQL] 실행계획(EXPLAIN)

Posted at 2014. 3. 12. 10:39 | Posted in Server


type (나쁜것부터 좋은것 순)
- ALL: full scan (extra에 "Using ditict"/"not exists" 가 있거나 LIMIT있는 쿼리는 예외)
- index: full scan but index order. 장점:정렬할 필요 없다. 단점: 전체 테이블을 인덱스 순서로 읽어야해서 random access가 일어남(비용 큼). (extra에 "Using index"가 나오면 커버링 인덱스를 사용하는 것임.(인덱스의 데이터만을 스캔한다는 것임))
- range: 제한된 형태의 index 스캔. index보다는 나은 성능을 보인다. 범위에 따라 성능차가 있다.
- ref: 어떤 조건 하나에 매치되는 행들을 반환해주는 인덱스 접근 방식. 여러개의 행을 찾게 될 수도 있으므로 탐색과 스캔이 함계 사용된다. unique하지 않은 인덱스 검색이 걸릴때 사용된다.
- eq_ref: MySQL이 기껏해야 값 하나만을 반환한다는 것을 알때 사용됨. 기본 키 혹은 unique 인덱스에 걸릴때 사용.
- const, system: 쿼리의 일부를 상수로 대체해서 최적화 할 수 있는 경우.
- NULL: 인덱스나 테이블에 접근조차 하지 않는 경우.

key: MySQL이 최적화를 위해 어떤 키를 사용하기로 했는지를 나타냄.

key_len: MySQL이 인덱스에 얼마나 많은 바이트를 사용하는지를 보여줌.

ref: key 에 나와 있는 인덱스에서 값을 찾기 위해 선행 테이블의 어떤 컬럼(또는 상수(const))이 사용되었는지를 나타냄.

rows: 원하는 행을 찾기위해 얼마나 많은 행을 읽어야 할지에 대한 예측값. 예측값은 인덱스의 선택도와 테이블 통계 정보에 의존적이므로 정확도가 상당히 떨어질 수도 있다.
      MySQL이 조사해야 할 것이라고 생각하는 행 수를 의미할 뿐 결과에 있는 행 수를 의미하지 않는다. 여기서 보여지는 행 수가 그리 중요하지 않을 수도 있다. 예측한 모든 행을 읽지는 않을 것이며, 대체로도 그렇다.

extra: 앞 선 컬럼에 적합하지 않은 나머지 정보를 표시. (중요하고 빈번한 몇가지만 나열)
- Using index: 커버링 인덱스를 사용한다는 것을 알려줌.
- Using where: MySQL서버가 스토리지 엔진에서 값을 가져온 뒤 행을 필터링한다는 것을 의미.
- Using temporary: MySQL이 쿼리 결과를 정렬하기 위해 임시 테이블을 사용한다는 것을 의미.
- Using filesort: 이는 MySQL이 결과의 순서를 맞추기 위해 인덱스 순서로 테이블을 읽는 것이 아니라 외부 정렬을 사용해야 한다는 것을 의미. 메모리나 디스크에서 수행될 수 있으며, EXPLAIN으로는 어떤 방식을 사용하지는, 디스크에서 하는지 메모리에서 하는지 등은 알 수 없다.
- Range checked for each record(index map:N): 적합한 인덱스가 없으므로 각 레코드의 조인에서 각 인덱스들을 재평가한다는 것을 의미한다. N은 possible_keys에 나타나 있는 인덱스들의 비트맵 값이다.


//

[NoSQL] CAP 이론[NoSQL] CAP 이론

Posted at 2013. 11. 14. 11:27 | Posted in Server




//

DHT(Distributed Hash Table 분산 해시 테이블)DHT(Distributed Hash Table 분산 해시 테이블)

Posted at 2013. 4. 10. 12:33 | Posted in Server

데이터와 서버를 동일한 주소 공간에 배치.

데이터의 키 값과 분산 서버 ID는 동일한 해시 함수로 동일한 주소 공간에 데이터와 노드를 배치.


중앙 서버 없이도 데이터를 관리하는 서버를 찾는 룩업에 성능이 좋다.

클러스터에 참여하는 서버의 추가/제거가 자동으로 이뤄지게 구성할 수 있다.


부하가 집중되지 않고 분산된다는 큰 장점이 있어, 극단적으로 큰 규모의 노드들도 관리할 수 있다.

종래의 순수 P2P에서 채용되었던 방식에서는 수십만 노드 정도가 한계였으나, DHT의 사용으로 수십억개의 노드를 검색범위로 할 수 있게 되었다. 그러나 DHT는 실제 구현이 어렵다.


키 공간 분할(keyspace partition)과 오버레이 네트워크(overlay network)로 구성된다.


- keyspace partition:

  * 분산된 서버에 키를 어떻게 배치시킬 것인가를 결정하는 것

  * 데이터의 hash(key) 값을 이용해 키 영역을 파티셔닝 시킨다.(range, interval)

- overlay network:

  * 물리적인 서버의 연결과 상관없이 논리적인 서버 간의 연결 관리와 키를 담당하는 노드를 찾아가는 메커니즘을 제공.

  * 특정 키를 서비스하는 노드를 찾아가는 라우팅 알로리즘을 제공한다.


DHT를 활용한 대표적인 시스템으로 비트토렌트(DHT를 확장하여 사용), eDonkey 등이 있다.


참고: 클라우드 컴퓨팅 구현 기술(에이콘), 위키백과


//

TCP RST 보내기TCP RST 보내기

Posted at 2012. 12. 28. 10:02 | Posted in Server

tcp 소켓을 닫을때 일반적으로는 normal tcp termination(4way closing handshake) 과정을 거치게 된다.

이 때, 소켓을 먼저 close한 쪽에 TIME_WAIT 상태로 소켓이 남아 있게 된다.

만약 서버가 먼저 close한 경우 TIME_WAIT 소켓이 가득차서 더 이상의 소켓을 accept 할 수 없는 상황이 발생할 수 있다.

이럴 때의 대안으로 normal tcp termination이 아닌 abortive close로 TIME_WAIT이 발생하지 않도록 할 수 있다.

(TIME_WAIT 상태가 왜 있는지 정확히 알고, 특정 상황에서만 사용하자.)


SO_LINGER with timeout 0

로 설정하고 close()를 호출하면 TCP FIN 대신 RST 이 나간다.

peer는 그 유명한 "Connection reset by peer" 에러가 발생하게 된다.


상세는 다음 글을 참조하자.

according to "UNIX Network Programming" third edition page 202-203, setting SO_LINGER with timeout 0 prior to calling close() will cause the normal termination sequence not to be initiated.


http://stackoverflow.com/a/13088864


//

대용량 데이터 분산, scale-out 에 대한 메모대용량 데이터 분산, scale-out 에 대한 메모

Posted at 2012. 10. 8. 14:14 | Posted in Server

gywn.net 의 텀블러와 Gizzard 분석 내용.

- http://gywn.net/2012/05/how_to_shard_big_data_in_tumblr/

http://gywn.net/2012/03/new-tweet-store/

- http://gywn.net/2012/03/gizzard-a-library-for-creating-distributed-datastores/


대용량 시스템을 위한 데이타베이스 아키텍쳐-Sharding & Query Off Loading

http://bcho.tistory.com/670


id sharding (인스타그램)

-http://charsyam.wordpress.com/2011/12/04/instagram-%EC%97%90%EC%84%9C-id-%EC%83%A4%EB%94%A9%ED%95%98%EA%B8%B0/



<TBD>...





//

Forward Proxy & Reverse ProxyForward Proxy & Reverse Proxy

Posted at 2012. 9. 19. 17:35 | Posted in Server

Forward Proxy

클라이언트가 타겟서버의 주소를 프록시에 전달하여, 프록시가 요청된 내용을 가져오는 방식

  예) 클라이언트 Proxy 설정에 proxy.com을 설정하고, 주소창에 target.com을 입력하여 브라우징.


Reverse Proxy

프록시 서버로 요청을 보내면 프록시 서버가 '배후'서버로 요청하여 내용을 가져오는 방식. 이 때 Reverse는 "거꾸로, 역전"이 아닌 "배후, 뒷쪽"의 의미임. 클라이언트는 '배후' 서버를 알 수 없다.

  예) 클라이언트가 proxy.com에 요청하면 프록시 서버가 target.com 서버로 요청하여 결과를 반환.

  예) 많이 쓰는 nginx의 proxy_pass 설정은 reverse proxy 이다.

//

http long polling 구현시 참고할 사항http long polling 구현시 참고할 사항

Posted at 2012. 9. 16. 14:01 | Posted in Server

LongPolling 구현이 문제된 경우

 - 서버를 직접 만들고 경험한 내용이다.

 - Parent창이 longpoll 세션을 하나 열고, Child창을 띄워 별도의 longpoll 세션을 또 하나 열었다.

 - 각 세션에 논리 세션ID를 부여하고 서버측에서 데이터를 관찰.

 - 몇번 데이터가 왔다갔다 하면, Parent창과 Child창이 서로 다른 소켓으로 데이터를 보냄.

 - 브라우저가 물리 소켓을 pool 처럼 사용하는 것으로 추측. ?> pool처럼 사용한다고 함.


브라우저 동작 방식

 - 브라우저별 OS별 설정이 다르긴 하지만, 브라우저 프로세스 내에서 같은 도메인에 대해 열 수 있는 소켓 수는 제한적이다.

   * http 1.1 버전에서는 default=2 개라고 함. (사실 여부는 표준 문서 등 확인 필요)

   * 도메인별로 기본적으로 하나는 브라우저 메인이 사용하고, 나머지는 추가로 열어서 쓸 수 있다. 제한수가 2라는 의미는 추가로 열 수 있는 소켓수가 1이란 뜻이다.

   * 제한수를 넘어서 소켓을 열면 이전 소켓중 하나가 닫힌다고 함(역시 표준 문서 등 확인 필요)

 - 요즘 브라우저는 창을 여러개 띄우더라도 프로세스는 하나이고 쓰레드를 여러개 띄우는 형태.

 - 따라서, 창을 여러개 열어도 도메인이 같으면 제한된 소켓만 오픈할 수 있다.

 - 구글은 이런 브라우저 특성 때문에 빠른 로딩을 위해서 이미지 서버 도메인을 여러개 두고 한 화면 안의 img 태그에 여러개의 이미지 서버 도메인을 넣어 사용한다고 함. 병렬로 동시에 로딩 가능하다.

※ 위의 LongPolling구현의 문제는 Parent창이 추가로 longpoll 세션을 열었는데(2개), Child창이 뜨면서 세션을 하나더 연 경우(3개)가 됨.


해결

 - Child창을 띄우지 말고, Parent창 하나만 longpoll을 사용하거나,

 - Child창은 다른 도메인으로 longpoll 세션을 연다. (세션서버에 도메인을 여러개 등록) --> 잘 됨!

※ 더 정확한 내용 아시는 분은 댓글 부탁드립니다.

//

ssl 인증서 만들기ssl 인증서 만들기

Posted at 2012. 9. 9. 12:58 | Posted in Server

Generate a private key

key 파일 생성

$ openssl genrsa [-des3] -out test.key 1024


Generate Root CA Certificate

루트 인증기관(Root CA) 인증서 생성

$ openssl req -new -x509 -keyout cakey.pem -out cacert.pem -days 3650


Generate Self Signed Certificate

자체 서명 인증서 생성

$ openssl req -new -x509 -keyout cacert.pem -out cacert.pem -days 3650


keyout과 out에 같은 파일명을 주면되며, 분리하고 싶은 경우는

RSA PRIVATE KEY부분과 CERTIFICATE부분을 다른 파일로 분리하면 된다.


Generate Certificate signing request

인증 요청서(Certificate Signing Request) 생성

$ openssl req -new -key test.key -x509 -out test.csr


Sign certificate with private key

키로 인증서에 서명하기

$ openssl x509 -req -days 3650 -in test.csr -signkey test.key -out test.crt


[Optional] key에서 Password phase 삭제

Remove Password Requirement

$ cp test.key test.key.secure

$ openssl rsa -in test.key.secure -out test.key


Generate dhparam file(Diffie-Hellman)

$ openssl dhparam -out dh512.pem 512

※ if an error, "digest too big for rsa key", occures, simply use 1024 bits. (happens on openssl-1.0.x later)

//