Mysql int vs varchar เป็นคีย์หลัก (InnoDB Storage Engine?


13

ฉันสร้างเว็บแอปพลิเคชัน (ระบบการจัดการโครงการ) และฉันสงสัยเกี่ยวกับสิ่งนี้เมื่อพูดถึงประสิทธิภาพ

ฉันมีตารางปัญหาอยู่ข้างในนั้นมี 12 ปุ่มต่างประเทศที่เชื่อมโยงกับตารางอื่น ๆ ในบรรดา 8 คนนั้นฉันต้องเข้าร่วมเพื่อรับฟิลด์ชื่อเรื่องจากตารางอื่น ๆ เพื่อให้ระเบียนมีเหตุผลในแอปพลิเคชันเว็บ 1 ฟิลด์สำหรับการรวมแต่ละรายการ

ตอนนี้ฉันยังได้รับคำสั่งให้ใช้คีย์หลักที่เพิ่มขึ้นอัตโนมัติ (ยกเว้นการใช้เศษเป็นข้อกังวลในกรณีที่ฉันควรใช้ GUID) สำหรับเหตุผลด้านความคงทน แต่มันแย่ขนาดไหนในการใช้ varchar (ความยาวสูงสุด 32) ฉันหมายถึงส่วนใหญ่ของตารางเหล่านี้อาจไม่ได้มีการบันทึกจำนวนมาก (ส่วนใหญ่ควรต่ำกว่า 20) นอกจากนี้ถ้าฉันใช้ชื่อเป็นคีย์หลักฉันจะไม่ต้องรวม 95% ของเวลาดังนั้นสำหรับ 95% ของ sql ฉันจะเกิดผลการทำงานที่ยอดเยี่ยม (ฉันคิดว่า) ข้อเสียอย่างเดียวที่ฉันคิดได้ก็คือฉันมีคือฉันจะมีการใช้พื้นที่ดิสก์ที่สูงขึ้น

เหตุผลที่ฉันใช้ตารางการค้นหาสำหรับสิ่งนี้มากมายแทนที่จะเป็น enums ก็เพราะฉันต้องการค่าทั้งหมดเหล่านี้เพื่อกำหนดค่าโดยผู้ใช้ผ่านแอปพลิเคชันเอง

อะไรคือข้อเสียของการใช้ varchar เป็นคีย์หลักสำหรับตารางที่ไม่ได้ยกเว้นที่จะมีหลายระเบียน?

อัพเดท - การทดสอบบางอย่าง

ดังนั้นฉันจึงตัดสินใจทำการทดสอบเบื้องต้นเกี่ยวกับสิ่งนี้ ฉันมีบันทึก 100,000 รายการและนี่คือการสืบค้นพื้นฐาน:

แบบสอบถาม VARCHAR FK พื้นฐาน

SELECT i.id, i.key, i.title, i.reporterUserUsername, i.assignedUserUsername, i.projectTitle, 
i.ProjectComponentTitle, i.affectedProjectVersionTitle, i.originalFixedProjectVersionTitle, 
i.fixedProjectVersionTitle, i.durationEstimate, i.storyPoints, i.dueDate, 
i.issueSecurityLevelId, i.creatorUserUsername, i.createdTimestamp, 
i.updatedTimestamp, i.issueTypeId, i.issueStatusId
FROM ProjectManagement.Issues i

แบบสอบถาม INT FK พื้นฐาน

SELECT i.id, i.key, i.title, ru.username as reporterUserUsername, 
au.username as assignedUserUsername, p.title as projectTitle, 
pc.title as ProjectComponentTitle, pva.title as affectedProjectVersionTitle, 
pvo.title as originalFixedProjectVersionTitle, pvf.title as fixedProjectVersionTitle, 
i.durationEstimate, i.storyPoints, i.dueDate, isl.title as issueSecurityLevelId, 
cu.username as creatorUserUsername, i.createdTimestamp, i.updatedTimestamp, 
it.title as issueTypeId, is.title as issueStatusId
FROM ProjectManagement2.Issues i
INNER JOIN ProjectManagement2.IssueTypes `it` ON it.id = i.issueTypeId
INNER JOIN ProjectManagement2.IssueStatuses `is` ON is.id = i.issueStatusId
INNER JOIN ProjectManagement2.Users `ru` ON ru.id = i.reporterUserId
INNER JOIN ProjectManagement2.Users `au` ON au.id = i.assignedUserId
INNER JOIN ProjectManagement2.Users `cu` ON cu.id = i.creatorUserId
INNER JOIN ProjectManagement2.Projects `p` ON p.id = i.projectId
INNER JOIN ProjectManagement2.`ProjectComponents` `pc` ON pc.id = i.projectComponentId
INNER JOIN ProjectManagement2.ProjectVersions `pva` ON pva.id = i.affectedProjectVersionId
INNER JOIN ProjectManagement2.ProjectVersions `pvo` ON pvo.id = i.originalFixedProjectVersionId
INNER JOIN ProjectManagement2.ProjectVersions `pvf` ON pvf.id = i.fixedProjectVersionId
INNER JOIN ProjectManagement2.IssueSecurityLevels isl ON isl.id = i.issueSecurityLevelId

ฉันยังเรียกใช้แบบสอบถามเหล่านี้ด้วยการเพิ่มต่อไปนี้:

  • เลือกรายการเฉพาะ (โดยที่ i.key = 43298)
  • จัดกลุ่มตาม i.id
  • สั่งซื้อโดย (it.title สำหรับ int FK, i.issueTypeId สำหรับ varchar FK)
  • ขีด จำกัด (50000, 100)
  • จัดกลุ่มและ จำกัด ร่วมกัน
  • จัดกลุ่มคำสั่งซื้อและ จำกัด ร่วมกัน

ผลลัพธ์สำหรับสิ่งเหล่านี้ที่:

QUERY TYPE: VARCHAR FK TIME / INT FK TIME


ข้อความค้นหาพื้นฐาน: ~ 4ms / ~ 52ms

เลือกรายการเฉพาะ: ~ 140ms / ~ 250ms

จัดกลุ่มตาม i.id: ~ 4ms / ~ 2.8sec

เรียงลำดับโดย: ~ 231ms / ~ 2 วินาที

จำกัด : ~ 67ms / ~ 343ms

จัดกลุ่มและ จำกัด ร่วมกัน: ~ 504ms / ~ 2 วินาที

จัดกลุ่มคำสั่งซื้อและ จำกัด ด้วยกัน: ~ 504ms /~2.3 วินาที

ตอนนี้ฉันไม่ทราบว่าการกำหนดค่าใดที่ฉันสามารถทำได้เพื่อทำให้เร็วขึ้น (หรือทั้งสองอย่าง) แต่ดูเหมือนว่า VARCHAR FK จะดูเร็วขึ้นในการสืบค้นข้อมูล (บางครั้งเร็วกว่ามาก)

ฉันเดาว่าฉันต้องเลือกว่าการปรับปรุงความเร็วนั้นคุ้มค่ากับขนาดข้อมูล / ดัชนีพิเศษหรือไม่


การทดสอบของคุณบ่งบอกถึงบางสิ่ง ฉันจะทดสอบด้วยการตั้งค่า InnoDB ต่าง ๆ (บัฟเฟอร์พูล ฯลฯ ) เนื่องจากการตั้งค่า MySQL เริ่มต้นไม่ได้รับการปรับให้เหมาะสมสำหรับ InnoDB
ypercubeᵀᴹ

คุณควรทดสอบประสิทธิภาพการแทรก / อัปเดต / ลบเนื่องจากอาจมีผลกระทบต่อขนาดดัชนี คีย์คลัสเตอร์เดียวของทุกตาราง InnoDB มักจะเป็น PK และคอลัมน์นี้ (PK) ก็จะรวมอยู่ในดัชนีอื่น ๆ นี่อาจเป็นข้อเสียอย่างใหญ่หลวงของ PKs ขนาดใหญ่ใน InnoDB และดัชนีจำนวนมากบนโต๊ะ (แต่ 32 ไบต์ค่อนข้างปานกลางไม่ใหญ่ดังนั้นจึงอาจไม่เป็นปัญหา)
ypercubeᵀᴹ

คุณควรทดสอบด้วยตารางที่ใหญ่กว่า (ในช่วงพูดประมาณ 10-100M แถวหรือใหญ่กว่า) หากคุณคาดว่าตารางของคุณอาจเติบโตสูงกว่า 100K (ซึ่งไม่ใหญ่มาก)
ypercubeᵀᴹ

@ypercube ดังนั้นฉันจึงเพิ่มข้อมูลเป็น 2 ล้านและคำสั่ง select สำหรับ int FK จะช้าลงอย่างมากโดยที่เลขต่างประเทศ varchar ยังคงค่อนข้างคงที่ คิดว่า varchar มีมูลค่าราคาในข้อกำหนดของดิสก์ / หน่วยความจำสำหรับการเพิ่มในการสืบค้นที่เลือก (ซึ่งจะมีความสำคัญในตารางนี้และอื่น ๆ อีกสองสาม)
ryanzec

เพียงตรวจสอบการตั้งค่า db (และโดยเฉพาะอย่างยิ่ง InnoDB) ของคุณด้วยก่อนที่จะสรุป ด้วยตารางอ้างอิงขนาดเล็กฉันไม่คาดหวังว่าการเพิ่มขึ้นแบบทวีคูณ
ypercubeᵀᴹ

คำตอบ:


9

ฉันปฏิบัติตามกฎต่อไปนี้สำหรับคีย์หลัก:

a) ไม่ควรมีความหมายทางธุรกิจใด ๆ - ควรเป็นอิสระจากแอปพลิเคชันที่คุณกำลังพัฒนาทั้งหมดดังนั้นฉันจึงไปหาจำนวนเต็มที่สร้างตัวเลขอัตโนมัติ อย่างไรก็ตามหากคุณต้องการคอลัมน์เพิ่มเติมที่ไม่ซ้ำใครให้สร้างดัชนีเฉพาะเพื่อสนับสนุน

b) ควรทำการเชื่อมต่อ - การเข้าร่วม varchars vs จำนวนเต็มช้าลงประมาณ 2 ถึง 3 เท่าเมื่อความยาวของคีย์หลักเพิ่มขึ้นดังนั้นคุณต้องการให้คีย์ของคุณเป็นจำนวนเต็ม เนื่องจากระบบคอมพิวเตอร์ทั้งหมดเป็นแบบไบนารี่ฉันจึงสงสัยว่า coz มันจะเปลี่ยนสตริงเป็นไบนารี่จากนั้นเปรียบเทียบกับระบบอื่นที่ช้ามาก

c) ใช้ชนิดข้อมูลที่เล็กที่สุดเท่าที่จะเป็นไปได้ - หากคุณคาดว่าตารางของคุณจะมีคอลัมน์น้อยมากที่บอกว่ามี 52 รัฐในสหรัฐอเมริกาจากนั้นให้ใช้ชนิดที่เล็กที่สุดที่อาจจะเป็น CHAR (2) สำหรับรหัส 2 หลัก (128) สำหรับคอลัมน์กับ int ขนาดใหญ่ซึ่งสามารถสูงถึง 2 พันล้าน

นอกจากนี้คุณจะมีความท้าทายในการเรียงซ้อนการเปลี่ยนแปลงของคุณจากคีย์หลักไปยังตารางอื่น ๆ ตัวอย่างเช่นการเปลี่ยนชื่อโครงการ (ซึ่งไม่ใช่เรื่องแปลก)

ไปหาจำนวนเต็มที่เพิ่มขึ้นอัตโนมัติตามลำดับสำหรับคีย์หลักของคุณและรับประสิทธิภาพ inbuilt ที่ระบบฐานข้อมูลให้พร้อมรองรับการเปลี่ยนแปลงในอนาคต


1
สตริงจะไม่เปลี่ยนเป็นไบนารี พวกเขาถูกเก็บไว้ในไบนารีตั้งแต่ต้น พวกเขาจะถูกเก็บไว้เป็นอย่างอื่นได้อย่างไร บางทีคุณอาจกำลังคิดว่าการปฏิบัติการเพื่อให้สามารถเปรียบเทียบตัวพิมพ์เล็กและใหญ่
Jon of All Trades

6

ในการทดสอบของคุณคุณไม่ได้เปรียบเทียบความแตกต่างของประสิทธิภาพของ varchar vs int keys แต่เสียค่าใช้จ่ายในการเข้าร่วมหลายครั้ง ไม่น่าแปลกใจที่การสืบค้น 1 ตารางเร็วกว่าการเข้าร่วมหลายตาราง
ข้อเสียหนึ่งของคีย์หลัก varchar คือการเพิ่มขนาดดัชนีตามที่atxdbaชี้ให้เห็น แม้ว่าตารางการค้นหาของคุณจะไม่มีดัชนีอื่น ๆ ยกเว้น PK (ซึ่งค่อนข้างไม่น่าเป็นไปได้ แต่เป็นไปได้) แต่ละตารางที่การค้นหาอ้างอิงจะมีดัชนีในคอลัมน์นี้
อีกสิ่งหนึ่งที่ไม่ดีเกี่ยวกับคีย์หลักแบบธรรมชาติคือค่าของมันสามารถเปลี่ยนแปลงได้ซึ่งทำให้เกิดการปรับปรุงแบบเรียงซ้อนมากมาย ไม่ใช่ RDMS ทั้งหมดเช่น Oracle แม้จะให้คุณใช้on update cascade. โดยทั่วไปการเปลี่ยนค่าคีย์หลักพิจารณาว่าเป็นการปฏิบัติที่ไม่ดีมาก ฉันไม่อยากจะบอกว่ากุญแจหลักตามธรรมชาตินั้นชั่วร้ายเสมอ หากค่าการค้นหามีขนาดเล็กและไม่เคยเปลี่ยนฉันคิดว่าอาจเป็นที่ยอมรับ

ตัวเลือกหนึ่งที่คุณอาจต้องการพิจารณาคือการใช้มุมมองที่เป็นรูปธรรม Mysql ไม่รองรับโดยตรง แต่คุณสามารถใช้ฟังก์ชันที่ต้องการได้ด้วยทริกเกอร์บนตารางพื้นฐาน ดังนั้นคุณจะมีตารางหนึ่งตารางที่มีทุกสิ่งที่คุณต้องการแสดง นอกจากนี้หากประสิทธิภาพเป็นที่ยอมรับอย่าต่อสู้กับปัญหาที่ไม่มีอยู่ในขณะนี้


3

ข้อเสียที่ใหญ่ที่สุดคือการทำซ้ำของ PK คุณชี้ให้เห็นการเพิ่มขึ้นของการใช้พื้นที่ดิสก์ แต่เพื่อให้ชัดเจนว่าขนาดดัชนีที่เพิ่มขึ้นนั้นเป็นเรื่องที่คุณกังวลมากขึ้น เนื่องจาก innodb เป็นดัชนีแบบกลุ่มดัชนีรองทุกตัวจะเก็บสำเนาของ PK ไว้ภายในเพื่อใช้ในการค้นหาระเบียนที่ตรงกันในที่สุด

คุณบอกว่าตารางนั้นถูกคาดหวังให้เป็น "เล็ก" (20 แถวนั้นเล็กมาก ๆ ) หากคุณมี RAM เพียงพอที่จะตั้งค่า innodb_buffer_pool_size ให้เท่ากับ

select sum(data_length+index_length) from information_schema.tables where engine='innodb';

จากนั้นทำเช่นนั้นและคุณอาจจะนั่งสวย ตามกฎทั่วไปแม้ว่าคุณจะต้องการออกจากหน่วยความจำระบบอย่างน้อย 30% - 40% สำหรับค่าใช้จ่าย mysql อื่น ๆ และแคชแคช และนั่นคือสมมติว่าเป็นเซิร์ฟเวอร์ฐานข้อมูลโดยเฉพาะ หากคุณมีสิ่งอื่นที่ทำงานบนระบบคุณจะต้องคำนึงถึงความต้องการของพวกเขาด้วย


1

นอกจากคำตอบ @atxdba - ซึ่งอธิบายว่าทำไมการใช้ตัวเลขจะดีกว่าสำหรับพื้นที่ว่างในดิสก์ฉันต้องการเพิ่มจุดสองจุด:

  1. หากตารางปัญหาของคุณใช้ VARCHAR FK และสมมติว่าคุณมี VARCHAR ขนาดเล็ก 20 ตัว (32) FK บันทึกของคุณจะมีความยาวได้ถึง 20x32bytes ในขณะที่ตารางที่กล่าวถึงของคุณเป็นตารางการค้นหาดังนั้น INT FK อาจเป็น TINYINT FK สำหรับเขตข้อมูล 20 รายการมีระเบียน 20 ไบต์ ฉันรู้ว่าหลายร้อยระเบียนจะไม่เปลี่ยนแปลงมากนัก แต่เมื่อคุณไปถึงหลายล้านคนฉันคิดว่าคุณจะประหยัดพื้นที่ได้มาก

  2. สำหรับปัญหาความเร็วฉันจะพิจารณาใช้ดัชนีครอบคลุมเนื่องจากดูเหมือนว่าสำหรับแบบสอบถามนี้คุณไม่ได้เรียกข้อมูลจำนวนมากจากตารางการค้นหาฉันจะไปครอบคลุมดัชนีและทำการทดสอบอีกครั้งของคุณด้วย VARCHAR FK / W / COVERING INDEX และ INT FK ปกติ

หวังว่ามันจะช่วยได้

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