non-blocking UUID generator

글쓴이 Engineer Myoa 날짜

UUID

  • UUID(Universally unique identifier) 는 이름 그대로 범용 고유 식별자 이다.
  • random 한 문자열로 오해할 수 있으나, 특정 조건으로 생성한 난수열을 바탕으로 생성하는 128bit 길이의 수이다.
  • 흔히 아는 88888888-4444-4444-4444-bbbbbbbbbbbb 같은 형태의 UUID string 은 128bit 수를 humanify 한 문자열의 불과하다.

UUID 레코드 레이아웃

이름길이 (byte)길이 (hex)길이 (bits)내용
time_low4832시간의 low 32비트를 부여하는 정수
time_mid2416시간의 middle 16비트를 부여하는 정수
time_hi_and_version2416최상위 비트에서 4비트 “version”, 그리고 시간의 high 12비트
clock_seq_hi_and_res clock_seq_low2416최상위 비트에서 1-3비트는 UUID의 레이아웃형식, 그리고 13-15비트 클럭 시퀀스
node6124848비트 노드 id

UUID.randomUUID vs ?

  • Java 에서 UUID 값을 생성할 때에는 UUID.randomUUId() 메서드를 사용한다.
  • WAS 형태의 application 은 multithread 환경에서 동작하는데, 이 때 contention 이 발생하면서 UUID 생성에 병목이 발생한다.
  • 병목의 원인은
    • UUID 내부에서 SecureRandom 클래스를 사용한다.
    • 이 코드내에서 synchronized 블록이 있음.
    • 동시에 여러 스레드에서 접근 시 blocking 이 되고 병목이 발생함.
    • synchronized (this) { secureRandomSpi.engineNextBytes(bytes); }
    • JUG(Java UUID Generator) 같은 라이브러리 내에서도
      • Generators → UUIDTimer → public synchronized long getTimestamp()
      • 결국 현재의 timestamp 값을 가져오기 위해 blocking 이 발생함.
      • BLOCKED 66.9% 70.3% com.fasterxml.uuid.UUIDTimer.getTimestamp
  • 이를 해결하기 위한 방법으로 /dev/random -> /dev/urandom , /dev/.urandom 등이 존재하나
    • 별도의 VM 옵션이 필요
    • 시스템 별로 의존성이 달라짐
  • 따라서 non-blocking 의 UUID generation 을 위해 ThreadLocalRandom 을 이용한다.

UUID Generator

public final class RandomGenerator {
    private RandomGenerator() {
    }

    private static final ThreadLocalRandom random = ThreadLocalRandom.current();

    public static UUID getUUID() {
        // https://github.com/apache/openwhisk/issues/2747
        return new UUID(random.nextLong(), random.nextLong());
    }

    public static String getUUIDString() {
        return getUUID().toString();
    }
    public static long getLong() {
        return random.nextLong();
    }
}

Benchmark 결과

@Benchmark
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void test1() {
    String uuid = UUID.randomUUID().toString();
}

@Benchmark
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public void test3() {
    String uuid = RandomGenerator.getUUIDString();
}
BenchmarkModeScoreUnitsComparison
RandomGeneratorBenchmarkTest.test1thrpt2143ops/ms1x
RandomGeneratorBenchmarkTest.test3thrpt464856ops/ms198x
Benchmark                                  Mode  Cnt       Score   Error   Units
RandomGeneratorBenchmarkTest.test1        thrpt         2143.575          ops/ms
RandomGeneratorBenchmarkTest.test1:stack  thrpt              NaN             ---
RandomGeneratorBenchmarkTest.test3        thrpt       464856.130          ops/ms
RandomGeneratorBenchmarkTest.test3:stack  thrpt              NaN             ---

References

카테고리: 개발노트

0개의 댓글

답글 남기기

Avatar placeholder

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다