Ich habe mit der Verwendung von UUIDs als Datenbankschlüssel experimentiert. Ich möchte so wenig Bytes wie möglich belegen und gleichzeitig die UUID-Darstellung für den Menschen lesbar halten.
Ich denke, dass ich es mit base64 auf 22 Bytes reduziert und einige nachfolgende "==" entfernt habe, die für meine Zwecke unnötig zu speichern scheinen. Gibt es irgendwelche Mängel bei diesem Ansatz?
Grundsätzlich führt mein Testcode eine Reihe von Konvertierungen durch, um die UUID auf eine 22-Byte-Zeichenfolge zu reduzieren, und konvertiert sie dann wieder in eine UUID.
import java.io.IOException;
import java.util.UUID;
public class UUIDTest {
public static void main(String[] args){
UUID uuid = UUID.randomUUID();
System.out.println("UUID String: " + uuid.toString());
System.out.println("Number of Bytes: " + uuid.toString().getBytes().length);
System.out.println();
byte[] uuidArr = asByteArray(uuid);
System.out.print("UUID Byte Array: ");
for(byte b: uuidArr){
System.out.print(b +" ");
}
System.out.println();
System.out.println("Number of Bytes: " + uuidArr.length);
System.out.println();
try {
// Convert a byte array to base64 string
String s = new sun.misc.BASE64Encoder().encode(uuidArr);
System.out.println("UUID Base64 String: " +s);
System.out.println("Number of Bytes: " + s.getBytes().length);
System.out.println();
String trimmed = s.split("=")[0];
System.out.println("UUID Base64 String Trimmed: " +trimmed);
System.out.println("Number of Bytes: " + trimmed.getBytes().length);
System.out.println();
// Convert base64 string to a byte array
byte[] backArr = new sun.misc.BASE64Decoder().decodeBuffer(trimmed);
System.out.print("Back to UUID Byte Array: ");
for(byte b: backArr){
System.out.print(b +" ");
}
System.out.println();
System.out.println("Number of Bytes: " + backArr.length);
byte[] fixedArr = new byte[16];
for(int i= 0; i<16; i++){
fixedArr[i] = backArr[i];
}
System.out.println();
System.out.print("Fixed UUID Byte Array: ");
for(byte b: fixedArr){
System.out.print(b +" ");
}
System.out.println();
System.out.println("Number of Bytes: " + fixedArr.length);
System.out.println();
UUID newUUID = toUUID(fixedArr);
System.out.println("UUID String: " + newUUID.toString());
System.out.println("Number of Bytes: " + newUUID.toString().getBytes().length);
System.out.println();
System.out.println("Equal to Start UUID? "+newUUID.equals(uuid));
if(!newUUID.equals(uuid)){
System.exit(0);
}
} catch (IOException e) {
}
}
public static byte[] asByteArray(UUID uuid) {
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
byte[] buffer = new byte[16];
for (int i = 0; i < 8; i++) {
buffer[i] = (byte) (msb >>> 8 * (7 - i));
}
for (int i = 8; i < 16; i++) {
buffer[i] = (byte) (lsb >>> 8 * (7 - i));
}
return buffer;
}
public static UUID toUUID(byte[] byteArray) {
long msb = 0;
long lsb = 0;
for (int i = 0; i < 8; i++)
msb = (msb << 8) | (byteArray[i] & 0xff);
for (int i = 8; i < 16; i++)
lsb = (lsb << 8) | (byteArray[i] & 0xff);
UUID result = new UUID(msb, lsb);
return result;
}
}
Ausgabe:
UUID String: cdaed56d-8712-414d-b346-01905d0026fe
Number of Bytes: 36
UUID Byte Array: -51 -82 -43 109 -121 18 65 77 -77 70 1 -112 93 0 38 -2
Number of Bytes: 16
UUID Base64 String: za7VbYcSQU2zRgGQXQAm/g==
Number of Bytes: 24
UUID Base64 String Trimmed: za7VbYcSQU2zRgGQXQAm/g
Number of Bytes: 22
Back to UUID Byte Array: -51 -82 -43 109 -121 18 65 77 -77 70 1 -112 93 0 38 -2 0 38
Number of Bytes: 18
Fixed UUID Byte Array: -51 -82 -43 109 -121 18 65 77 -77 70 1 -112 93 0 38 -2
Number of Bytes: 16
UUID String: cdaed56d-8712-414d-b346-01905d0026fe
Number of Bytes: 36
Equal to Start UUID? true
Antworten:
Sie können die Polsterung "==" in dieser Anwendung sicher ablegen. Wenn Sie den Base-64-Text wieder in Bytes dekodieren würden, würden einige Bibliotheken erwarten, dass er vorhanden ist. Da Sie jedoch nur die resultierende Zeichenfolge als Schlüssel verwenden, ist dies kein Problem.
Ich würde Base-64 verwenden, da seine Codierungszeichen URL-sicher sein können und es weniger nach Kauderwelsch aussieht. Es gibt aber auch Base-85 . Es werden mehr Symbole und Codes mit 4 Bytes als 5 Zeichen verwendet, sodass Sie Ihren Text auf 20 Zeichen reduzieren können.
quelle
%
) oder Backslash (`\`)? Müssen Sie den Bezeichner codieren und decodieren? (Das heißt, möchten Sie in der Lage sein, wieder in eine herkömmliche UUID zu konvertieren oder diese einfach zu verkürzen?)Ich habe auch versucht, etwas Ähnliches zu tun. Ich arbeite mit einer Java-Anwendung, die UUIDs des Formulars verwendet
6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8
(die mit der Standard-UUID lib in Java generiert werden). In meinem Fall musste ich in der Lage sein, diese UUID auf 30 Zeichen oder weniger zu reduzieren. Ich habe Base64 verwendet und dies sind meine praktischen Funktionen. Hoffentlich sind sie für jemanden hilfreich, da mir die Lösung nicht sofort klar war.Verwendung:
String uuid_str = "6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8"; String uuid_as_64 = uuidToBase64(uuid_str); System.out.println("as base64: "+uuid_as_64); System.out.println("as uuid: "+uuidFromBase64(uuid_as_64));
Ausgabe:
as base64: b8tRS7h4TJ2Vt43Dp85v2A as uuid : 6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8
Funktionen:
import org.apache.commons.codec.binary.Base64; private static String uuidToBase64(String str) { Base64 base64 = new Base64(); UUID uuid = UUID.fromString(str); ByteBuffer bb = ByteBuffer.wrap(new byte[16]); bb.putLong(uuid.getMostSignificantBits()); bb.putLong(uuid.getLeastSignificantBits()); return base64.encodeBase64URLSafeString(bb.array()); } private static String uuidFromBase64(String str) { Base64 base64 = new Base64(); byte[] bytes = base64.decodeBase64(str); ByteBuffer bb = ByteBuffer.wrap(bytes); UUID uuid = new UUID(bb.getLong(), bb.getLong()); return uuid.toString(); }
quelle
import org.apache.commons.codec.binary.Base64;
Base64.getUrlEncoder().encodeToString(bb.array())
undBase64.getUrlDecoder().decode(id)
Hier ist mein Code, der org.apache.commons.codec.binary.Base64 verwendet, um url-sichere eindeutige Zeichenfolgen mit einer Länge von 22 Zeichen (und derselben Eindeutigkeit wie UUID) zu erstellen.
private static Base64 BASE64 = new Base64(true); public static String generateKey(){ UUID uuid = UUID.randomUUID(); byte[] uuidArray = KeyGenerator.toByteArray(uuid); byte[] encodedArray = BASE64.encode(uuidArray); String returnValue = new String(encodedArray); returnValue = StringUtils.removeEnd(returnValue, "\r\n"); return returnValue; } public static UUID convertKey(String key){ UUID returnValue = null; if(StringUtils.isNotBlank(key)){ // Convert base64 string to a byte array byte[] decodedArray = BASE64.decode(key); returnValue = KeyGenerator.fromByteArray(decodedArray); } return returnValue; } private static byte[] toByteArray(UUID uuid) { byte[] byteArray = new byte[(Long.SIZE / Byte.SIZE) * 2]; ByteBuffer buffer = ByteBuffer.wrap(byteArray); LongBuffer longBuffer = buffer.asLongBuffer(); longBuffer.put(new long[] { uuid.getMostSignificantBits(), uuid.getLeastSignificantBits() }); return byteArray; } private static UUID fromByteArray(byte[] bytes) { ByteBuffer buffer = ByteBuffer.wrap(bytes); LongBuffer longBuffer = buffer.asLongBuffer(); return new UUID(longBuffer.get(0), longBuffer.get(1)); }
quelle
Ich habe eine Anwendung, in der ich fast genau das mache. 22 Zeichen codierte UUID. Es funktioniert gut. Der Hauptgrund, warum ich das so mache, ist, dass die IDs in den URIs der Web-App verfügbar sind und 36 Zeichen für etwas, das in einem URI angezeigt wird, wirklich ziemlich groß sind. 22 Zeichen sind noch ein bisschen lang, aber wir schaffen es.
Hier ist der Ruby-Code dafür:
# Make an array of 64 URL-safe characters CHARS64 = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + ["-", "_"] # Return a 22 byte URL-safe string, encoded six bits at a time using 64 characters def to_s22 integer = self.to_i # UUID as a raw integer rval = "" 22.times do c = (integer & 0x3F) rval += CHARS64[c] integer = integer >> 6 end return rval.reverse end
Es ist nicht genau dasselbe wie die Base64-Codierung, da Base64 Zeichen verwendet, die maskiert werden müssten, wenn sie in einer URI-Pfadkomponente erscheinen würden. Die Java-Implementierung ist wahrscheinlich ganz anders, da Sie eher ein Array von Rohbytes als eine wirklich große Ganzzahl haben.
quelle
Sie sagen nicht, welches DBMS Sie verwenden, aber es scheint, dass RAW der beste Ansatz ist, wenn Sie Platz sparen möchten. Sie müssen nur daran denken, für alle Abfragen zu konvertieren, sonst riskieren Sie einen enormen Leistungsabfall.
Aber ich muss fragen: Sind Bytes bei Ihnen wirklich so teuer?
quelle
Hier ist ein Beispiel mit
java.util.Base64
in JDK8 eingeführt:import java.nio.ByteBuffer; import java.util.Base64; import java.util.Base64.Encoder; import java.util.UUID; public class Uuid64 { private static final Encoder BASE64_URL_ENCODER = Base64.getUrlEncoder().withoutPadding(); public static void main(String[] args) { // String uuidStr = UUID.randomUUID().toString(); String uuidStr = "eb55c9cc-1fc1-43da-9adb-d9c66bb259ad"; String uuid64 = uuidHexToUuid64(uuidStr); System.out.println(uuid64); //=> 61XJzB_BQ9qa29nGa7JZrQ System.out.println(uuid64.length()); //=> 22 String uuidHex = uuid64ToUuidHex(uuid64); System.out.println(uuidHex); //=> eb55c9cc-1fc1-43da-9adb-d9c66bb259ad } public static String uuidHexToUuid64(String uuidStr) { UUID uuid = UUID.fromString(uuidStr); byte[] bytes = uuidToBytes(uuid); return BASE64_URL_ENCODER.encodeToString(bytes); } public static String uuid64ToUuidHex(String uuid64) { byte[] decoded = Base64.getUrlDecoder().decode(uuid64); UUID uuid = uuidFromBytes(decoded); return uuid.toString(); } public static byte[] uuidToBytes(UUID uuid) { ByteBuffer bb = ByteBuffer.wrap(new byte[16]); bb.putLong(uuid.getMostSignificantBits()); bb.putLong(uuid.getLeastSignificantBits()); return bb.array(); } public static UUID uuidFromBytes(byte[] decoded) { ByteBuffer bb = ByteBuffer.wrap(decoded); long mostSigBits = bb.getLong(); long leastSigBits = bb.getLong(); return new UUID(mostSigBits, leastSigBits); } }
Die in Base64 codierte UUID ist URL-sicher und ohne Auffüllen.
quelle
Dies ist nicht genau das, wonach Sie gefragt haben (es ist nicht Base64), aber aufgrund der zusätzlichen Flexibilität einen Blick wert: Es gibt eine Clojure-Bibliothek, die eine kompakte URL-sichere Darstellung von UUIDs mit 26 Zeichen implementiert ( https: // github) .com / Tonsky / Compact-Uuids ).
Einige Highlights:
Das sind ziemlich schöne Eigenschaften. Ich habe diese Codierung in meinen Anwendungen sowohl für Datenbankschlüssel als auch für vom Benutzer sichtbare Bezeichner verwendet und sie funktioniert sehr gut.
quelle
Unten ist, was ich für eine UUID (Comb Style) verwende. Es enthält Code zum Konvertieren einer UUID-Zeichenfolge oder eines UUID-Typs in base64. Ich mache es pro 64 Bit, also beschäftige ich mich nicht mit Gleichheitszeichen:
JAVA
import java.util.Calendar; import java.util.UUID; import org.apache.commons.codec.binary.Base64; public class UUIDUtil{ public static UUID combUUID(){ private UUID srcUUID = UUID.randomUUID(); private java.sql.Timestamp ts = new java.sql.Timestamp(Calendar.getInstance().getTime().getTime()); long upper16OfLowerUUID = this.zeroLower48BitsOfLong( srcUUID.getLeastSignificantBits() ); long lower48Time = UUIDUtil.zeroUpper16BitsOfLong( ts ); long lowerLongForNewUUID = upper16OfLowerUUID | lower48Time; return new UUID( srcUUID.getMostSignificantBits(), lowerLongForNewUUID ); } public static base64URLSafeOfUUIDObject( UUID uuid ){ byte[] bytes = ByteBuffer.allocate(16).putLong(0, uuid.getLeastSignificantBits()).putLong(8, uuid.getMostSignificantBits()).array(); return Base64.encodeBase64URLSafeString( bytes ); } public static base64URLSafeOfUUIDString( String uuidString ){ UUID uuid = UUID.fromString( uuidString ); return UUIDUtil.base64URLSafeOfUUIDObject( uuid ); } private static long zeroLower48BitsOfLong( long longVar ){ long upper16BitMask = -281474976710656L; return longVar & upper16BitMask; } private static void zeroUpper16BitsOfLong( long longVar ){ long lower48BitMask = 281474976710656L-1L; return longVar & lower48BitMask; } }
quelle