การสร้าง ID ที่มนุษย์อ่านได้ / ใช้งานได้สั้น แต่ไม่ซ้ำใคร


89
  • ต้องจัดการ> 1,000 แต่ <10,000 บันทึกใหม่ต่อวัน

  • ไม่สามารถใช้ GUID / UUID หมายเลขเพิ่มอัตโนมัติเป็นต้น

  • ตามหลักการแล้วควรมีความยาว 5 หรือ 6 ตัวอักษรสามารถเป็นอัลฟ่าได้แน่นอน

  • ต้องการนำอัลกอสที่มีอยู่ซึ่งเป็นที่รู้จักกันดีมาใช้ซ้ำหากมี

มีอะไรอีกไหม?


ทำไมไม่ใช้ INT หรือ BIGINT ที่เติมอัตโนมัติ? มันอาจจะอ่านง่ายที่สุดและสามารถจัดการกับระดับเสียงได้อย่างง่ายดาย
Malk

ตาม Q ด้านบนพยายามรักษาให้สูงสุด 5/6 ตัวอักษรและรองรับบันทึกใหม่ไม่เกิน 9999 ครั้งต่อวัน
Kumar

@Kumar - จะเกิดอะไรขึ้นถ้าคุณต้องการบันทึกมากกว่า 9999 รายการในหนึ่งวัน? วิธีการแก้ปัญหาที่คุณเสนอไม่ได้รับผลกระทบ
ChaosPandion

@ChaosPandion: ฉันคิดว่าสิ่งเหล่านี้อาจเป็นการคาดเดาอย่างคร่าวๆเกี่ยวกับการบรรทุก / การจราจรมากกว่าขอบเขตที่ยาก ฉันไม่แน่ใจว่าเหตุใดคุณจึงต้องการกำหนดขีด จำกัด ของจำนวนธุรกรรมรายวันโดยพลการ
Paul Sasik

คุณสามารถเข้ารหัสเป็นฐาน 64 และใช้สิ่งนั้น ฉันไม่แน่ใจว่าคุณสามารถลดให้เล็กกว่านั้นได้และยังคงใช้อักขระที่อ่านได้ แต่ฉันจะยืนยันว่าฐาน 64 นั้นอ่านได้น้อยกว่าฐาน 32 มากเนื่องจากต้องเพิ่มคุณสมบัติพิเศษให้กับอักขระส่วนใหญ่ (ตัวพิมพ์ใหญ่ f, o ต่ำกว่า, o ต่ำกว่าเมื่อเทียบกับ f, oo)
Malk

คำตอบ:


122

ฐาน 62 ใช้โดย tinyurl และ bit.ly สำหรับ URL แบบย่อ เป็นวิธีการที่เข้าใจกันดีในการสร้าง ID ที่ "ไม่ซ้ำใคร" ซึ่งมนุษย์สามารถอ่านได้ แน่นอนคุณจะต้องจัดเก็บ ID ที่สร้างขึ้นและตรวจสอบรายการที่ซ้ำกันในการสร้างเพื่อให้แน่ใจว่าไม่ซ้ำกัน (ดูรหัสที่ด้านล่างของคำตอบ)

เมตริกฐาน 62 ความเป็นเอกลักษณ์

5 ตัวอักษรในฐาน 62 จะให้ ID เฉพาะ 62 ^ 5 = 916,132,832 (~ 1 พันล้าน) ที่ 10k ID ต่อวันคุณจะใช้ได้ 91k + วัน

6 ตัวอักษรในฐาน 62 จะให้ 62 ^ 6 ID ที่ไม่ซ้ำกัน = 56,800,235,584 (56+ พันล้าน) ที่ 10k ID ต่อวันคุณจะใช้ได้เป็นเวลา 5+ ล้านวัน

ตัวชี้วัดความเป็นเอกลักษณ์ฐาน 36

6 ตัวอักษรจะให้ ID เฉพาะ 36 ^ 6 = 2,176,782,336 (2+ พันล้าน)

7 ตัวอักษรจะให้ ID เฉพาะ 36 ^ 7 = 78,364,164,096 (78+ พันล้าน)

รหัส:

public void TestRandomIdGenerator()
{
    // create five IDs of six, base 62 characters
    for (int i=0; i<5; i++) Console.WriteLine(RandomIdGenerator.GetBase62(6));

    // create five IDs of eight base 36 characters
    for (int i=0; i<5; i++) Console.WriteLine(RandomIdGenerator.GetBase36(8));
}

public static class RandomIdGenerator 
{
    private static char[] _base62chars = 
        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
        .ToCharArray();

    private static Random _random = new Random();

    public static string GetBase62(int length) 
    {
        var sb = new StringBuilder(length);

        for (int i=0; i<length; i++) 
            sb.Append(_base62chars[_random.Next(62)]);

        return sb.ToString();
    }       

    public static string GetBase36(int length) 
    {
        var sb = new StringBuilder(length);

        for (int i=0; i<length; i++) 
            sb.Append(_base62chars[_random.Next(36)]);

        return sb.ToString();
    }
}

เอาท์พุต:

z5KyMg
wd4SUp
uSzQtH
UPrGAT
UIf2IS

QCF9GNM5
0UV3TFSS
3MG91VKP
7NTRF10T
AJK3AJU7

3
ดูดีมีอะไรที่ไม่พิจารณาตัวพิมพ์เล็กและใหญ่?
Kumar

2
หากคุณต้องการหลีกเลี่ยงกรณีที่สำคัญคุณสามารถใช้ฐาน 36: codeproject.com/Articles/10619/Base-36-type-for-NET-Cแต่เพื่อให้ได้การเรียงสับเปลี่ยนจำนวนมากเป็นฐาน 62 คุณจะต้องใช้อักขระเพิ่มเติมในของคุณ ID. มันเป็นการแลกเปลี่ยน หรือคุณอาจลองใช้อักขระอื่น ๆ นอกเหนือจากอัลฟ่า แต่มันน่าเกลียดสำหรับผู้ใช้
Paul Sasik

2
ที่นี่stackoverflow.com/questions/9543892/… & ขอบคุณมาก
Kumar

12
หนึ่งความคิด บางทีอาจใช้สระออกเพื่อป้องกันไม่ให้เกิดคำสบถโดยไม่ได้ตั้งใจ โดยเฉพาะอย่างยิ่งถ้าเป็นที่สาธารณะ
Damien Sawyer

4
ขึ้นอยู่กับตำแหน่งที่คุณใช้สิ่งนี้ (โดยเฉพาะอย่างยิ่งหากคาดว่ามนุษย์จะอ่านและป้อนรหัสซ้ำ) คุณอาจต้องการพิจารณาลบอักขระที่สับสนออกจากการพิจารณา: 0 / O และ I / l / 1 สิ่งนี้สามารถบรรเทาได้ในบางกรณีด้วยการเลือกแบบอักษรที่ดี แต่ฉันไม่สามารถบอกได้จากคำถามว่า OP จะควบคุมสิ่งนั้นได้หรือไม่
GrandOpener

18

ฉันแนะนำhttp://hashids.org/ซึ่งแปลงตัวเลขใด ๆ (เช่น DB ID) เป็นสตริง (โดยใช้เกลือ)

อนุญาตให้ถอดรหัสสตริงนี้กลับไปเป็นตัวเลข คุณจึงไม่จำเป็นต้องเก็บไว้ในฐานข้อมูล

มี libs สำหรับ JavaScript, Ruby, Python, Java, Scala, PHP, Perl, Swift, Clojure, Objective-C, C, C ++ 11, Go, Erlang, Lua, Elixir, ColdFusion, Groovy, Kotlin, Nim, VBA, CoffeeScript และสำหรับ Node.js & .NET


1
คุณสามารถระบุตัวเลือกอื่น ๆ ที่คล้ายคลึงกับข้อเสนอของคุณได้หรือไม่ - - มันน่าสนใจอย่างมาก. ฉันต้องการทราบว่ามีตัวเลือกเริ่มต้นเช่นนั้นใน PostgreSQL หรือไม่
LéoLéopold Hertz 준영

1
นี่คือเวอร์ชัน. NETแต่คุณสามารถอธิบายวิธีการทำงานโดยไม่จำเป็นต้องเก็บไว้ในฐานข้อมูลได้หรือไม่? ฉันสามารถสร้างแรนดอมเฉพาะโดยไม่ต้องใส่ตัวเลขเป็นอินพุตและไม่ใส่เกลือได้หรือไม่?
Shaiju T

@ Slawa ฉันต้องการบางอย่างเช่นแฮชสำหรับ. NET แต่แฮชสุดท้ายจะถูกเก็บไว้ในฐานข้อมูลในคอลัมน์ที่มีความยาวคงที่เป็นไปได้หรือไม่ที่จะบอกว่าสร้างแฮชที่มีความยาวสูงสุด N เสมอ
Anon Dev

6

ฉันมีข้อกำหนดที่คล้ายกันกับ OP ฉันดูห้องสมุดที่มีอยู่ แต่ส่วนใหญ่จะขึ้นอยู่กับการสุ่มและฉันไม่ต้องการสิ่งนั้น ฉันไม่พบสิ่งที่ไม่ได้ขึ้นอยู่กับการสุ่มและยังสั้นมาก ... ดังนั้นฉันจึงลงเอยด้วยการหมุนของตัวเองโดยใช้เทคนิคที่ Flickr ใช้แต่แก้ไขให้ต้องการการประสานงานน้อยลงและอนุญาตให้ออฟไลน์นานขึ้น

ในระยะสั้น:

  • เซิร์ฟเวอร์ส่วนกลางออกบล็อก ID ซึ่งประกอบด้วย 32 ID แต่ละรายการ
  • ตัวสร้าง ID โลคัลจะรักษาพูลของบล็อก ID เพื่อสร้าง ID ทุกครั้งที่มีการร้องขอ เมื่อพูลทำงานต่ำจะดึง ID บล็อกเพิ่มเติมจากเซิร์ฟเวอร์เพื่อเติมอีกครั้ง

ข้อเสีย:

  • ต้องมีการประสานงานจากส่วนกลาง
  • รหัสสามารถคาดเดาได้มากหรือน้อย (น้อยกว่ารหัส DB ทั่วไป แต่ไม่ได้สุ่ม)

ข้อดี

  • อยู่ภายใน 53 บิต (ขนาดสูงสุดของ Javascript / PHP สำหรับตัวเลขจำนวนเต็ม)
  • รหัสสั้นมาก
  • ฐาน 36 เข้ารหัสเพื่อให้มนุษย์อ่านเขียนและออกเสียงได้ง่ายมาก
  • ID สามารถสร้างในเครื่องได้เป็นเวลานานก่อนที่จะต้องติดต่อกับเซิร์ฟเวอร์อีกครั้ง (ขึ้นอยู่กับการตั้งค่าพูล)
  • ในทางทฤษฎีไม่มีโอกาสเกิดการชนกัน

ฉันได้เผยแพร่ทั้งไลบรารี Javascript สำหรับฝั่งไคลเอ็นต์ตลอดจนการใช้งานเซิร์ฟเวอร์ Java EE การติดตั้งเซิร์ฟเวอร์ในภาษาอื่นควรทำได้ง่ายเช่นกัน

นี่คือโครงการ:

suid - รหัสเฉพาะบริการแบบกระจายที่สั้นและไพเราะ

suid-server-java - การติดตั้ง Suid-server สำหรับสแตกเทคโนโลยี Java EE

ไลบรารีทั้งสองมีให้บริการภายใต้สัญญาอนุญาตครีเอทีฟคอมมอนส์แบบเสรีนิยม หวังว่านี่อาจช่วยให้คนอื่นมองหารหัสเฉพาะสั้น ๆ


คุณช่วยเปรียบเทียบstackoverflow.com/a/29372036/54964กับข้อเสนอของคุณได้suidไหม
LéoLéopold Hertz 준영

1
มันขึ้นอยู่กับตัวเลขสุ่ม มันค่อนข้างดีจริง แต่ ID ของคุณจะไม่สั้นเท่าที่ควร ฉันเขียน SUID เพื่อเริ่มเลขที่ 1 ดังนั้นคุณจะเริ่มต้นด้วยID ที่สั้นมาก คิด 3 หรือ 4 ตัวอักษร นอกจากนี้ยังมีข้อดีอีกอย่างที่จะมี ID ที่สั่งซื้อเพิ่มขึ้น (โดยประมาณ) นอกเหนือจากการเริ่มต้นด้วยรหัสที่สั้นจริงๆ
Stijn de Witt

3

ฉันใช้ฐาน 36เมื่อฉันแก้ไขปัญหานี้สำหรับแอปพลิเคชันที่ฉันกำลังพัฒนาเมื่อสองสามปีก่อน ฉันต้องการสร้างหมายเลขที่ไม่ซ้ำกันที่มนุษย์สามารถอ่านได้อย่างสมเหตุสมผล (ภายในปีปฏิทินปัจจุบันอยู่ดี) ฉันเลือกใช้เวลาเป็นมิลลิวินาทีตั้งแต่เที่ยงคืนของวันที่ 1 มกราคมของปีปัจจุบัน (ดังนั้นในแต่ละปีการประทับเวลาอาจซ้ำกันได้) และแปลงเป็นเลขฐาน 36 หากระบบที่กำลังพัฒนาประสบปัญหาร้ายแรงระบบจะสร้างหมายเลขฐาน 36 (7 ตัวอักษร) ที่แสดงต่อผู้ใช้ปลายทางผ่านทางเว็บอินเทอร์เฟซซึ่งสามารถถ่ายทอดปัญหาที่พบ (และหมายเลข) ไปยังผู้สนับสนุนด้านเทคนิค (who จากนั้นสามารถใช้เพื่อค้นหาจุดในบันทึกที่ stacktrace เริ่มต้น) ตัวเลขเช่น56af42g7เป็นอนันต์ง่ายขึ้นสำหรับผู้ใช้ในการอ่านและการถ่ายทอดกว่าการประทับเวลาเช่น2016-01-21T15: 34: 29.933-08: 00หรือ UUID สุ่มเช่น5f0d3e0c-da96-11e5-b5d2-0a1d41d68578


4
คุณช่วยระบุรหัสเทียมในรูปแบบโครงสร้างเกี่ยวกับข้อเสนอของคุณได้ไหม ฟังดูน่าสนใจ
LéoLéopold Hertz 준영

0

ฉันชอบความเรียบง่ายของการเข้ารหัส GUID โดยใช้รูปแบบ Base64 และตัดทอนท้าย == เพื่อให้ได้สตริง 22 อักขระ (ใช้โค้ดหนึ่งบรรทัดและคุณสามารถแปลงกลับเป็น GUID ได้ตลอดเวลา) น่าเศร้าที่บางครั้งอาจมีอักขระ + และ / ใช้ได้สำหรับฐานข้อมูลไม่ดีสำหรับ URL แต่ช่วยให้ฉันขอบคุณคำตอบอื่น ๆ :-)

จากhttps://www.codeproject.com/Tips/1236704/Reducing-the-string-Length-of-a-Guidโดย Christiaan van Bergen

เราพบว่าการแปลง Guid (16 ไบต์) เป็นการแสดง ASCII โดยใช้ Base64 ทำให้มีmessageID ที่ใช้งานได้และยังคงไม่ซ้ำกันเพียง 22 อักขระ

var newGuid = Guid.NewGuid();
var messageID = Convert.ToBase64String(newGuid.ToByteArray());

var message22chars = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0,22);

ตัวอย่างเช่น Guid 'e6248889-2a12-405a-b06d-9695b82c0a9c' (ความยาวสตริง: 36) จะได้รับการแทนค่า Base64: 'iYgk5hIqWkCwbZaVuCwKnA ==' (ความยาวสตริง: 24)

การแทนค่า Base64 ลงท้ายด้วยอักขระ '==' คุณสามารถตัดทอนสิ่งเหล่านี้โดยไม่ส่งผลกระทบต่อความเป็นเอกลักษณ์ ปล่อยให้คุณมีตัวระบุที่มีความยาวเพียง 22 อักขระ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.