บริบท
ฉันออกแบบฐานข้อมูล (บน PostgreSQL 9.6) ซึ่งจะเก็บข้อมูลจากแอปพลิเคชันแบบกระจาย เนื่องจากลักษณะการกระจายของแอปพลิเคชันฉันไม่สามารถใช้จำนวนเต็มเพิ่มโดยอัตโนมัติ ( SERIAL) เป็นคีย์หลักของฉันเนื่องจากสภาพการแข่งขันที่เป็นไปได้
วิธีแก้ปัญหาตามธรรมชาติคือการใช้ UUID หรือตัวระบุที่ไม่ซ้ำกันทั่วโลก Postgres มาพร้อมกับประเภทในตัวUUIDซึ่งเป็นขนาดที่พอดี
ปัญหาที่ฉันมีกับ UUID เกี่ยวข้องกับการดีบัก: มันเป็นสตริงที่ไม่เป็นมิตรกับมนุษย์ ตัวระบุไม่ff53e96d-5fd7-4450-bc99-111b91875ec5บอกอะไรฉันในขณะACC-f8kJd9xKCdที่ไม่รับประกันว่าจะไม่ซ้ำใคร แต่บอกว่าฉันกำลังจัดการกับACCวัตถุ
จากมุมมองการเขียนโปรแกรมเป็นเรื่องปกติที่จะดีบักเคียวรีแอปพลิเคชันที่เกี่ยวข้องกับวัตถุต่าง ๆ สมมติว่าโปรแกรมเมอร์ค้นหาACCวัตถุ (บัญชี) ที่ORDตาราง (ลำดับ) อย่างไม่ถูกต้อง ด้วยตัวระบุที่มนุษย์สามารถอ่านได้โปรแกรมเมอร์จะระบุปัญหาได้ทันทีในขณะที่ใช้ UUID เขาจะใช้เวลาในการพิจารณาว่ามีอะไรผิดปกติ
ฉันไม่ต้องการเอกลักษณ์ "รับประกัน" ของ UUIDs; ฉันไม่ต้องการห้องพักบางส่วนสำหรับการสร้างปุ่มโดยไม่ให้เกิดความขัดแย้ง แต่ UUID เป็น overkill นอกจากนี้สถานการณ์กรณีที่เลวร้ายที่สุดมันจะไม่ใช่จุดจบของโลกหากเกิดการชน (ฐานข้อมูลปฏิเสธและแอปพลิเคชันสามารถกู้คืนได้) ดังนั้นการพิจารณาถึงความไม่เหมาะสมตัวระบุขนาดเล็ก แต่เป็นมิตรกับมนุษย์จะเป็นทางออกที่ดีสำหรับกรณีการใช้งานของฉัน
การระบุวัตถุแอปพลิเคชัน
ตัวระบุที่ฉันใช้มีรูปแบบต่อไปนี้: {domain}-{string}ซึ่ง{domain}จะถูกแทนที่ด้วยโดเมนวัตถุ (บัญชีคำสั่งผลิตภัณฑ์) และ{string}เป็นสตริงที่สร้างแบบสุ่ม ในบางกรณีอาจทำให้การแทรก a {sub-domain}ก่อนสตริงสุ่ม ลองมองข้ามความยาวของ{domain}และ{string}เพื่อจุดประสงค์ในการรับประกันเอกลักษณ์
รูปแบบอาจมีขนาดคงที่หากช่วยในการจัดทำดัชนี / สอบถามประสิทธิภาพการทำงาน
ปัญหา
รู้ว่า:
- ฉันต้องการมีคีย์หลักที่มีรูปแบบ
ACC-f8kJd9xKCdดังนี้ - คีย์หลักเหล่านี้จะเป็นส่วนหนึ่งของหลายตาราง
- คีย์เหล่านี้ทั้งหมดจะใช้กับการเชื่อมต่อหลายครั้ง / ความสัมพันธ์บนฐานข้อมูล 6NF
- ตารางส่วนใหญ่จะมีขนาดกลางถึงขนาดใหญ่ (เฉลี่ยประมาณ 1M แถวที่ใหญ่ที่สุดที่มีแถว ~ 100M)
เกี่ยวกับประสิทธิภาพวิธีใดดีที่สุดในการจัดเก็บคีย์นี้
ด้านล่างเป็นวิธีแก้ปัญหาที่เป็นไปได้สี่ข้อ แต่เนื่องจากฉันมีประสบการณ์น้อยในฐานข้อมูลฉันจึงไม่แน่ใจว่าจะเลือกแบบใดดีที่สุด (ถ้ามี)
ถือว่าเป็นโซลูชั่น
1. เก็บเป็นสตริง ( VARCHAR)
(Postgres ไม่สร้างความแตกต่างระหว่างCHAR(n)และVARCHAR(n)ดังนั้นฉันจึงไม่สนใจCHAR)
หลังจากการวิจัยบางอย่างที่ผมเคยพบว่ามีการเปรียบเทียบสตริงพิเศษในการเข้าร่วมการดำเนินงานจะช้ากว่าการใช้VARCHAR INTEGERมันสมเหตุสมผลแล้ว แต่เป็นสิ่งที่ฉันควรกังวลในระดับนี้หรือไม่?
2. จัดเก็บเป็นไบนารี ( bytea)
แตกต่างจาก Postgres, MySQL ไม่มีUUIDประเภทพื้นเมือง มีหลายโพสต์อธิบายวิธีการจัดเก็บ UUID ใช้ 16 ไบต์มีBINARYสนามแทนที่จะเป็น 36 ไบต์VARCHARหนึ่ง โพสต์เหล่านี้ทำให้ฉันมีความคิดในการจัดเก็บคีย์เป็นไบนารี ( byteaบน Postgres)
สิ่งนี้ช่วยประหยัดขนาด แต่ฉันเกี่ยวข้องกับประสิทธิภาพมากกว่า ฉันมีโชคเล็กน้อยในการหาคำอธิบายว่าการเปรียบเทียบแบบใดที่เร็วกว่า: ไบนารีหรือสตริง ฉันเชื่อว่าการเปรียบเทียบแบบไบนารีนั้นเร็วกว่า ถ้าเป็นเช่นนั้นbyteaก็อาจจะดีกว่าVARCHARแม้ว่าตอนนี้โปรแกรมเมอร์จะต้องเข้ารหัส / ถอดรหัสข้อมูลทุกครั้ง
ฉันอาจจะผิด แต่ฉันคิดว่าทั้งสองbyteaและVARCHARจะเปรียบเทียบ (ความเสมอภาค) byte byte (หรือตัวละครโดยตัวละคร) มีวิธี "ข้าม" การเปรียบเทียบแบบทีละขั้นตอนนี้และเพียงแค่เปรียบเทียบ "สิ่งทั้งหมด"? (ฉันไม่คิดอย่างนั้น แต่ไม่มีค่าใช้จ่ายในการตรวจสอบ)
ฉันคิดว่าการจัดเก็บตามที่byteaเป็นทางออกที่ดีที่สุด แต่ฉันสงสัยว่ามีทางเลือกอื่น ๆ ที่ฉันไม่สนใจ นอกจากนี้ข้อกังวลเดียวกับที่ฉันแสดงในโซลูชันที่ 1 ถือเป็นจริง: ค่าใช้จ่ายในการเปรียบเทียบเพียงพอที่ฉันควรกังวลหรือไม่
โซลูชัน "สร้างสรรค์"
ฉันคิดวิธีแก้ปัญหาที่ "สร้างสรรค์" สองอย่างที่สามารถใช้งานได้ฉันไม่แน่ใจในระดับที่ (เช่นถ้าฉันมีปัญหาในการปรับขนาดให้มากกว่าสองพันแถวในตาราง)
3. เก็บเป็นUUIDแต่มี "ฉลาก" ติดอยู่
เหตุผลหลักที่จะไม่ใช้ UUID คือเพื่อให้โปรแกรมเมอร์สามารถดีบักแอปพลิเคชันได้ดีขึ้น แต่จะเกิดอะไรขึ้นถ้าเราสามารถใช้ทั้งคู่: ฐานข้อมูลจะเก็บคีย์ทั้งหมดเป็นUUIDเพียง s เท่านั้น แต่มันจะล้อมวัตถุก่อน / หลังแบบสอบถาม
ยกตัวอย่างเช่นโปรแกรมเมอร์ขอACC-{UUID}ฐานข้อมูลละเว้นส่วนเรียกผลและกลับมาทั้งหมดของพวกเขาเป็นACC-{domain}-{UUID}
อาจจะเป็นไปได้ด้วยการแฮกเกอร์ที่มีขั้นตอนหรือฟังก์ชั่นการจัดเก็บไว้ แต่คำถามบางข้อมาถึง:
- นี่คือ (ลบ / เพิ่มโดเมนที่แต่ละแบบสอบถาม) เป็นค่าใช้จ่ายมากหรือไม่
- เป็นไปได้ไหม
ฉันไม่เคยใช้ขั้นตอนหรือฟังก์ชั่นที่เก็บไว้มาก่อนดังนั้นฉันไม่แน่ใจว่าสิ่งนี้จะเป็นไปได้หรือไม่ ใครบางคนสามารถส่องแสงบ้างไหม? ถ้าฉันสามารถเพิ่มเลเยอร์โปร่งใสระหว่างโปรแกรมเมอร์และข้อมูลที่เก็บไว้ดูเหมือนว่าเป็นโซลูชั่นที่สมบูรณ์แบบ
4. (My Favorite) Store เป็น IPv6 cidr
ใช่คุณอ่านถูกต้อง แต่กลับกลายเป็นว่ารูปแบบที่อยู่ IPv6 แก้ปัญหาของฉันได้อย่างสมบูรณ์แบบ
- ฉันสามารถเพิ่มโดเมนและโดเมนย่อยที่สองสาม octets แรกและใช้ที่เหลือเป็นสตริงแบบสุ่ม
- อัตราต่อรองการปะทะกันก็โอเค (ฉันจะไม่ใช้ 2 ^ 128 แต่ก็ยังใช้ได้)
- การเปรียบเทียบความเท่าเทียมกันได้รับการปรับให้เหมาะสม (หวังว่า) ดังนั้นฉันจึงอาจได้รับประสิทธิภาพที่ดีกว่าการใช้เพียงอย่าง
byteaเดียว - ฉันสามารถทำการเปรียบเทียบที่น่าสนใจเช่น
containsขึ้นอยู่กับว่าโดเมนและลำดับชั้นถูกแสดงอย่างไร
ตัวอย่างเช่นสมมติว่าฉันใช้รหัส0000เพื่อแทนโดเมน "ผลิตภัณฑ์" ที่สำคัญจะเป็นตัวแทนของสินค้า0000:0db8:85a3:0000:0000:8a2e:0370:73340db8:85a3:0000:0000:8a2e:0370:7334
คำถามหลักที่นี่คือ: เมื่อเทียบกับbyteaมีข้อได้เปรียบหลักหรือข้อเสียในการใช้cidrชนิดข้อมูลใด ๆ
varcharในปัญหาอื่น ๆ อีกมากมาย ฉันไม่รู้เกี่ยวกับโดเมนของ pg ซึ่งเป็นการเรียนรู้ที่ดี ฉันเห็นโดเมนที่ใช้ในการตรวจสอบความถูกต้องหากแบบสอบถามที่ระบุกำลังใช้วัตถุที่ถูกต้อง แต่ก็ยังคงต้องอาศัยการมีดัชนีที่ไม่ใช่จำนวนเต็ม ไม่แน่ใจว่ามีวิธีการ "ปลอดภัย" ที่serialนี่หรือไม่ (ไม่มีขั้นตอนการล็อคหนึ่งครั้ง)
varcharโดเมนที่ไม่จำเป็นต้องเป็น ลองสร้างเป็นFK integerประเภทและเพิ่มตารางการค้นหา ด้วยวิธีนี้คุณสามารถมีทั้งมนุษย์ที่สามารถอ่านได้และคุณจะปกป้องคอมโพสิตของคุณPKจากการแทรก / อัปเดตความผิดปกติ (ใส่โดเมนที่ไม่มีอยู่)
textvarcharมีมากกว่าที่นิยม ดูdepesz.com/2010/03/02/charx-vs-varcharx-vs-varchar-vs-textและpostgresql.org/docs/current/static/datatype-character.html