[java] 8 자 전용 UUID 생성

UUID 라이브러리는 32 자 UUID를 생성합니다.

8 자 전용 UUID를 생성하고 싶습니다. 가능합니까?



답변

UUID는 정의당 16 바이트 숫자이므로 불가능합니다. 물론 8 자 길이의 고유 한 문자열을 생성 할 수 있습니다 (다른 답변 참조).

또한 ID의 일부가 고정 된 바이트를 포함 할 수 있으므로 더 긴 UUID를 생성하고 하위 문자열을 지정할 때주의해야합니다 (예 : MAC, DCE 및 MD5 UUID의 경우).


답변

RandomStringUtils apache.commons에서 수업을 시도 할 수 있습니다 .

import org.apache.commons.lang3.RandomStringUtils;

final int SHORT_ID_LENGTH = 8;

// all possible unicode characters
String shortId = RandomStringUtils.random(SHORT_ID_LENGTH);

URL이나 사람에게 친숙하지 않은 모든 가능한 문자가 포함된다는 점을 명심하십시오.

따라서 다른 방법도 확인하십시오.

// HEX: 0-9, a-f. For example: 6587fddb, c0f182c1
shortId = RandomStringUtils.random(8, "0123456789abcdef");

// a-z, A-Z. For example: eRkgbzeF, MFcWSksx
shortId = RandomStringUtils.randomAlphabetic(8);

// 0-9. For example: 76091014, 03771122
shortId = RandomStringUtils.randomNumeric(8);

// a-z, A-Z, 0-9. For example: WRMcpIk7, s57JwCVA
shortId = RandomStringUtils.randomAlphanumeric(8);

다른 사람들이 말했듯이 더 작은 id와 id 충돌 가능성이 중요 할 수 있습니다. 생일 문제 가 귀하의 경우에 어떻게 적용 되는지 확인하십시오 . 이 답변 에서 근사치를 계산하는 방법에 대한 좋은 설명을 찾을 수 있습니다 .


답변

첫째 : java UUID.randomUUID 또는 .net GUID에서 생성 된 고유 ID조차도 100 % 고유하지 않습니다. 특히 UUID.randomUUID는 128 비트 (보안) 임의 값 “전용”입니다. 따라서 64 비트, 32 비트, 16 비트 (또는 1 비트)로 줄이면 단순히 덜 고유 해집니다.

따라서 최소한 위험 기반 결정, uuid가 얼마나 길어야 하는가입니다.

둘째 : “8 자만”이라는 말은 일반 인쇄 가능한 8 자 문자열을 의미한다고 가정합니다.

길이가 8 개의 인쇄 가능한 문자로 된 고유 한 문자열을 원한다면 base64 인코딩을 사용할 수 있습니다. 이것은 문자 당 6 비트를 의미하므로 총 48 비트를 얻게됩니다 (매우 고유하지는 않지만 응용 프로그램에 적합 할 수도 있음).

따라서 방법은 간단합니다. 6 바이트 임의 배열 생성

 SecureRandom rand;
 // ...
 byte[] randomBytes = new byte[16];
 rand.nextBytes(randomBytes);

그런 다음 예를 들어 다음과 같이 Base64 문자열로 변환합니다. org.apache.commons.codec.binary.Base64

BTW : 무작위로 “uuid”를 만드는 더 좋은 방법이 있는지 응용 프로그램에 따라 다릅니다. (초당 한 번만 UUID를 생성하는 경우 타임 스탬프를 추가하는 것이 좋습니다.) (참고 : 두 개의 임의 값을 결합 (xor)하면 결과는 항상 가장 임의의 값만큼 무작위로 나타납니다. 둘 다 무작위).


답변

@Cephalopod가 말했듯이 불가능하지만 UUID를 22 자로 줄일 수 있습니다.

public static String encodeUUIDBase64(UUID uuid) {
        ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
        bb.putLong(uuid.getMostSignificantBits());
        bb.putLong(uuid.getLeastSignificantBits());
        return StringUtils.trimTrailingCharacter(BaseEncoding.base64Url().encode(bb.array()), '=');
}


답변

이것은 Anton Purin 답변을 기반으로 고유 한 오류 코드를 생성하기 위해 여기에서 사용하는 것과 비슷한 방법이지만 org.apache.commons.text.RandomStringGenerator더 이상 사용되지 않는 (더 이상이 아닌) 대신 더 적절한 것에 의존합니다 org.apache.commons.lang3.RandomStringUtils.

@Singleton
@Component
public class ErrorCodeGenerator implements Supplier<String> {

    private RandomStringGenerator errorCodeGenerator;

    public ErrorCodeGenerator() {
        errorCodeGenerator = new RandomStringGenerator.Builder()
                .withinRange('0', 'z')
                .filteredBy(t -> t >= '0' && t <= '9', t -> t >= 'A' && t <= 'Z', t -> t >= 'a' && t <= 'z')
                .build();
    }

    @Override
    public String get() {
        return errorCodeGenerator.generate(8);
    }

}

충돌에 대한 모든 조언은 여전히 ​​적용됩니다.


답변

이건 어때? 실제로이 코드는 최대 13자를 반환하지만 UUID보다 짧습니다.

import java.nio.ByteBuffer;
import java.util.UUID;

/**
 * Generate short UUID (13 characters)
 *
 * @return short UUID
 */
public static String shortUUID() {
  UUID uuid = UUID.randomUUID();
  long l = ByteBuffer.wrap(uuid.toString().getBytes()).getLong();
  return Long.toString(l, Character.MAX_RADIX);
}


답변

실제로 타임 스탬프 기반의 더 짧은 고유 식별자를 원하므로 아래 프로그램을 시도했습니다.

nanosecond + ( endians.length * endians.length )조합 으로 추측 할 수 있습니다.

public class TimStampShorterUUID {

    private static final Character [] endians =
           {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
            'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
            'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
            'U', 'V', 'W', 'X', 'Y', 'Z',
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
            };

   private static ThreadLocal<Character> threadLocal =  new ThreadLocal<Character>();

   private static AtomicLong iterator = new AtomicLong(-1);


    public static String generateShorterTxnId() {
        // Keep this as secure random when we want more secure, in distributed systems
        int firstLetter = ThreadLocalRandom.current().nextInt(0, (endians.length));

        //Sometimes your randomness and timestamp will be same value,
        //when multiple threads are trying at the same nano second
        //time hence to differentiate it, utilize the threads requesting
        //for this value, the possible unique thread numbers == endians.length
        Character secondLetter = threadLocal.get();
        if (secondLetter == null) {
            synchronized (threadLocal) {
                if (secondLetter == null) {
                    threadLocal.set(endians[(int) (iterator.incrementAndGet() % endians.length)]);
                }
            }
            secondLetter = threadLocal.get();
        }
        return "" + endians[firstLetter] + secondLetter + System.nanoTime();
    }


    public static void main(String[] args) {

        Map<String, String> uniqueKeysTestMap = new ConcurrentHashMap<>();

        Thread t1 = new Thread() {
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }
        };

        Thread t2 = new Thread() {
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }
        };

        Thread t3 = new Thread() {
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }
        };

        Thread t4 = new Thread() {
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }
        };

        Thread t5 = new Thread() {
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }
        };

        Thread t6 = new Thread() {
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }
        };

        Thread t7 = new Thread() {
            @Override
            public void run() {
                while(true) {
                    String time = generateShorterTxnId();
                    String result = uniqueKeysTestMap.put(time, "");
                    if(result != null) {
                        System.out.println("failed! - " + time);
                    }
                }
            }
        };

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
        t7.start();
    }
}

최신 정보 :이 코드는 단일 JVM에서 작동하지만 분산 JVM에 대해 생각해야하므로 DB가있는 솔루션과 DB가없는 솔루션을 생각하고 있습니다.

DB로

회사 이름 (짧은 이름 3 자) —- Random_Number —- 키 특정 redis COUNTER
(3 자) ————————– ———————- (2 자) —————- (11 자)

DB없이

IPADDRESS —- THREAD_NUMBER —- INCR_NUMBER —- epoch milliseconds
(5 chars) —————– (2char) ——— ————– (2 자) —————– (6 자)

코딩이 완료되면 업데이트됩니다.