บริบท
ฉันออกแบบฐานข้อมูล (บน 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:7334
0db8:85a3:0000:0000:8a2e:0370:7334
คำถามหลักที่นี่คือ: เมื่อเทียบกับbytea
มีข้อได้เปรียบหลักหรือข้อเสียในการใช้cidr
ชนิดข้อมูลใด ๆ
varchar
ในปัญหาอื่น ๆ อีกมากมาย ฉันไม่รู้เกี่ยวกับโดเมนของ pg ซึ่งเป็นการเรียนรู้ที่ดี ฉันเห็นโดเมนที่ใช้ในการตรวจสอบความถูกต้องหากแบบสอบถามที่ระบุกำลังใช้วัตถุที่ถูกต้อง แต่ก็ยังคงต้องอาศัยการมีดัชนีที่ไม่ใช่จำนวนเต็ม ไม่แน่ใจว่ามีวิธีการ "ปลอดภัย" ที่serial
นี่หรือไม่ (ไม่มีขั้นตอนการล็อคหนึ่งครั้ง)
varchar
โดเมนที่ไม่จำเป็นต้องเป็น ลองสร้างเป็นFK
integer
ประเภทและเพิ่มตารางการค้นหา ด้วยวิธีนี้คุณสามารถมีทั้งมนุษย์ที่สามารถอ่านได้และคุณจะปกป้องคอมโพสิตของคุณPK
จากการแทรก / อัปเดตความผิดปกติ (ใส่โดเมนที่ไม่มีอยู่)
text
varchar
มีมากกว่าที่นิยม ดูdepesz.com/2010/03/02/charx-vs-varcharx-vs-varchar-vs-textและpostgresql.org/docs/current/static/datatype-character.html