RDBMS에서 랜덤 샘플링을 위해 ORDER BY RAND() 이런 것은 금지!!!

RDBMS에서 랜덤하게 데이터를 추출하기 위해
ORDER BY RAND()를 한다면…
바로 망하는 길입니다.
물론 데이터의 수가 적을 때에는 문제가 되지 않을 수도 있습니다.
하지만 레코드가 100만개라면?


1000만개의 데이터에 각각 RAND() 값을 정하게 되어
RAND() 1000만번을 호출하게 되고 이를 임시 공간에 들고 있어야 되며,
1000만개의 값을 정렬해야 되니… 쿼리 하나 때문에 장시간 DB가 멈추겠죠?


그럼 어떻게 랜덤하게 데이터를 추출해야 하는가?


두가지 정도가 될 거 같습니다. (MySQL 기준)


우선은 랜덤한 데이터를 1개씩 n회 추출하는 방법은


1. 전체 데이터 개수를 가져온다. (cnt)
2. 프로그램에서 [0, cnt)의 정수를 랜덤으로 정한다. (skip_cnt)
3. SELECT some_fields FROM some_tabe LIMIT skip_cnt, 1
4. 2, 3번을 n회 반복한다. (필요에 따라 skip_count가 중복되지 않게 처리)

조금 번거롭게 느껴질 수도 있습니다만 제일 확실한 방법이 아닐까요?
물론 some_table은 PK가 있어야 성능이 나오겠죠?
인덱스가 없으면 skip_cnt를 구하기나 LIMIT를 사용할 때에 좀 애매해 보입니다.


두번째 방법 범위를 이용해서 한번에 n개를 뽑는다


이 방법을 사용하려면 범위를 정하기 위한 인덱싱이 되어 있는 정수 타입의 필드가 있어야 합니다.


1. SELECT MIN(soem_indexed_field), MAX(soem_indexed_field) FROM some_table (min_val, max_val)
2. 프로그램에서 min_val, max_val 사이에서 n x m개의 랜덤 리스트를 만든다. (random_list)
3. SELECT some_fields FROM some_table WHERE soem_indexed_field IN random_list LIMIT n

m이 등장하는데 이유는 some_indexed_field의 값들에서 비어있는 값이 있을 경우를 대비해서 입니다.
데이터가 1, 2, 3, 4, 5, 6, … 과 같이 빈 값이 없다면 m == 1일 겁니다.
하지만 데이터가 중간 중간 값이 비어 있어서
1, 5, 7, 123, … 이런 식이라면? ㅠ.ㅠ m이 적절한 값이 되어야 합니다.
문제는 m 값을 정하는 것이 쉽지 않다는 것.
게다가 some_indexed_field 값이 단순 증가 ID 필드가 아닐 경우에는 상당히 곤란해 집니다.


적절히 작지도 크지도 않은 m 값을 가지고 두번째 방법을 통해 데이터를 추출하고
그렇게 나온 데이터가 n개가 되도록 루프…. 이게 가장 현실적이 아닐까요?

글쓴이

BS

BS == Programmer