하이브에서 랜덤 샘플링 하는 방법 (셔플링)

하이브에서 수백만 수천만의 행이 있다고 생각을 해보자,
효율적으로 고정된 숫자를 임의로 샘플링을 하고 싶다면, 어떻게 효율적으로 할 수 있을까?

샘플링(Sampling)

sql select * from my_table limit 1000;

아래와 같이 샘플링을 하면, 고정된 1000개의 rows를 위에서 부터 순서대로 데이터를 가져올 수 있다.
하지만, 동일한 방법으로 1000개를 가져오는 쿼리를 수행하면 같은 데이터가 샘플링이 된다.

order by

sql select * from my_table order by rand() limit 1000;

그렇다면, 임의로 정렬을 한뒤에 샘플링을 하면 어떨까?
아래와 같이 샘플링을 하면, 임의로 데이터를 정렬을 한 뒤에 1000개를 추출하기 때문에 매번 다른 결과를 준다.

sort by

sql select * from my_table sort by rand() limit 1000;

하지만 하이브에서는 데이터의 양이 많기 때문에 성능면에서 좋지 않다.
order by 는 하나의 reducer를 사용해서 전체 데이터를 정렬하기 때문에 성능이 상당히 좋지 않다.
order by 대신에 sort by 를 사용하면, 각각의 reducer에서 정렬을 하기 때문에
전체 데이터를 순서는 보장하지 않지만, 각각의 reducer내의 데이터의 순서는 보장하게 된다.

하지만, 하이브에서 여러개의 reducers로 데이터를 splitting하는게 정의가 되어 있지 않기 때문에
파일의 순서에 따라서 랜덤하게 파일이 나뉘어 진다.
limit 절을 사용할 경우에는 reducres에 대해서 정의되어 있지 않기 때문에, round-robins을 통해서
각각의 reducers에 있는 데이터를 mixes해서 가져오게 될 것이다.

데이터의 colummn기반으로 reduce key를 사용하면 limit 절은 reducers의 순서가 된다.
그렇게 되면 샘플들은 extremely skewed 될것이다.

distribute by

sql select * from my_table distribute by rand() sort by rand() limit 10000;

위 문제를 해결하기 위해 "distribute by"를 사용하면 된다.
query의 구조에 따라 reduce key가 결정이 되지 않기 때문에, 정확하게 reduce key를 내가 원하는 값으로 specify 가능하다.
더이상 이제 limit을 생각할 필요가없이 reducer내에 정렬이 가능하다.

filtering map-side

sql select * from my_table where rand() <= 0.0001 distribute by rand() sort by rand() limit 10000;

만약 테이블의 전체 사이즈를 안다면, 쉽게 data에서 랜덤하게 proportion을 가져올 수 있다.
total size가 10,000,000,000이라고 하고, sample을 1,000개 한다고 할때,
전체 데이터에서 0.000001을 가져오면 된다. 만약 where에서 "rand() < 0.000001"을 하면,
10000개의 rows를 ouput으로 출력하게 된다.
결국 병목 현상이 작업을 시작하기위한 간단한 전체 테이블 스캔이되기 때문에 중요하지 않으며,
reducer로 보내지는 볼륨을 기반으로하는 것이 아니기 때문에 더 효율적으로 sampling이 가능하다.

  • [참고]
    • http://www.joefkelley.com/736/

+ Recent posts