8 자 전용 UUID 생성


82

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

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


확실한. 그러나 그것은 당연히 간단하지 않고 짧을수록 실제로 독특 할 가능성이 적습니다. 왜?

@delnan, 임베디드 환경에서 사용할 수 있습니까?
Allen Zhang

1
결과 문자열을 UTF-8로 저장할 수있는 경우 잠재적으로 문자 당 4 바이트가 있습니다. 전체 범위를 사용할 수있는 경우 동일한 정보를 나타내는 데 4 개의 UTF-8 문자 만 필요합니다.
EECOLOR

답변:


72

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

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


방법 타임 스탬프에 대한
안나 poorani

60

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 충돌 가능성이 중요 할 수 있습니다. 생일 문제 가 귀하의 경우에 어떻게 적용 되는지 확인하십시오 . 이 답변 에서 근사치를 계산하는 방법에 대한 좋은 설명을 찾을 수 있습니다 .


4
이후 org.apache.commons.lang3.RandomStringUtils사용되지 않습니다, 당신은 사용 좋을 것 org.apache.commons.text.RandomStringGeneratorcommons.apache.org/proper/commons-text
BrunoJCM

RandomStringGenerator코드가 완전히 다르기 때문에에 대한 새로운 답변을 추가했습니다 .
BrunoJCM

2
미래의 시청자를위한 참고로 Randomness는 고유성을 보장하지 않습니다. 임의 생성기는 임의성을 보장합니다. 반복 값이있는 유효한 난수 집합을 생성 할 수 있습니다.
Vishnu Prasad V

RandomStringUtils더 이상 사용되지 않습니다. 간단한 사용을위한 것입니다. RandomStringUtils더 이상 사용되지 않는 정보의 소스를 제공 할 수 있습니까 ? RandomStringUtils더 이상 사용되지 않는다는 증거로 의 최신 버전 문서를 제공 할 수 있습니다 . commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…
krm

이미 사용 된 uuid가있는 맵이나 해시 셋을 확인해야만 충돌 가능성이 큽니다.
안톤

18

첫째 : 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)하면 결과는 항상 가장 임의의 값만큼 무작위로 나타납니다. 둘 다 무작위).


7

@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()), '=');
}

2

이것은 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);
    }

}

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


RandomStringUtils더 이상 사용되지 않습니다. 간단한 사용을위한 것입니다. RandomStringUtils더 이상 사용되지 않는 정보의 소스를 제공 할 수 있습니까 ? RandomStringUtils더 이상 사용되지 않는다는 증거로 의 최신 버전 문서를 제공 할 수 있습니다 . commons.apache.org/proper/commons-lang/javadocs/api-3.9/org/…
krm

글쎄, 조금 더 자세히 살펴보면이 답변을 쓰는 ​​시점에서 최신 릴리스가 실제로이 클래스를 더 이상 사용하지 않았 음을 알 수 있습니다 . github.com/apache/commons-lang/commits/master/src / main / java / org /… 아마도 일부 피드백 ( user.commons.apache.narkive.com/GVBG2Ar0/… )이 돌아 왔습니다. commons.lang어차피 언어 자체와 엄격하게 관련되지 않고 commons.text목적을 가지고 만들어진 어떤 것도 사용해서는 안됩니다 .
BrunoJCM

BrunoJCM에 대한 설명에 감사드립니다. 현재는 RandomStringUtils더 이상 사용되지 않으며 귀하가 제공 한 참조에 따르면 RandomStringGenerator간단한 사용 사례 보다 사용하기가 훨씬 간단하기 때문에 더 이상 사용하지 않는 것이 좋습니다 . 답변을 업데이트 할 수 있습니까? RandomStringUtils간단한 사용 사례에 대한 / 언제 또는 기능이로 이동되면 commons.text답변을 다시 업데이트 할 수 있지만 현재는 오해의 소지가 있습니다.
krm

참고 사항을 추가했지만 Apache Commons 프로젝트가 텍스트 유틸리티를에서 commons.lang으로 이동하고 있음이 분명합니다 commons.text. 이미 다른 곳에서 사용하는 것 외에는 전자를 사용할 이유가 없습니다. 여기에서의 단순성은 다소 주관적입니다. 제 대답은 여전히 ​​매우 간단하며 Commons Lang을 가져 오기 위해 필요한 것으로 변경하지 않았습니다.
BrunoJCM

1

이건 어때? 실제로이 코드는 최대 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);
}

4
getLong()버퍼의 처음 8 바이트 만 읽는다는 것을 알고 있습니다. UUID에는 최소 36 바이트가 있습니다. 나에게 이것이 결코 작동하지 않기 때문에 나는 무언가를 놓치고 있는가?
Edwin Dalorzo 2014 년

2
처음 8 바이트는 UUID의 최상위 비트입니다. 에 따라 이 응답 덜 중요한 비트 것이 더 랜덤이다. 그래서 Long.toString(uuid.getLessSignificantBits(), Character.MAX_RADIX)더 좋습니다.
DouO 2014

0

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

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 자)

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



-11

나는 그것이 가능하다고 생각하지 않지만 좋은 해결 방법이 있습니다.

  1. substring ()을 사용하여 UUID 끝을 자릅니다.
  2. 코드를 사용 new Random(System.currentTimeMillis()).nextInt(99999999); 하면 최대 8 자 길이의 임의 ID가 생성됩니다.
  3. 영숫자 ID 생성 :

    char[] chars = "abcdefghijklmnopqrstuvwxyzABSDEFGHIJKLMNOPQRSTUVWXYZ1234567890".toCharArray();
    Random r = new Random(System.currentTimeMillis());
    char[] id = new char[8];
    for (int i = 0;  i < 8;  i++) {
        id[i] = chars[r.nextInt(chars.length)];
    }
    return new String(id);
    

14
불행히도 이러한 모든 접근 방식은 원하는 것보다 빨리 반복 (즉, 고유하지 않은 ID)을 제공 할 수 있습니다.
Stephen C

1
현재 날짜로 시드하는 것이 빈 생성자를 사용하는 것보다 덜 무작위하지 ​​않습니까?
Patrick Favre
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.