ประสิทธิภาพของการใช้ CHAR กับ VARCHAR ในฟิลด์ขนาดคงที่คืออะไร


58

ฉันมีคอลัมน์ที่จัดทำดัชนีซึ่งจัดเก็บ MD5 แฮช ดังนั้นคอลัมน์จะเก็บค่า 32 อักขระเสมอ ไม่ว่าจะด้วยเหตุผลใดก็ตามสิ่งนี้ถูกสร้างขึ้นเป็น varchar แทนที่จะเป็นถ่าน มันคุ้มค่าปัญหาของการโยกย้ายฐานข้อมูลเพื่อแปลงเป็นถ่านหรือไม่ นี่คือใน MySQL 5.0 พร้อม InnoDB


6
คำเตือนคำถามนี้และคำตอบถูกเขียนก่อน InnoDB และ utf8 คือค่าเริ่มต้น
Rick James

คำตอบ:


56

คำถามที่คล้ายกันถูกถามก่อน

ความหมายของประสิทธิภาพของขนาด MySQL VARCHAR

นี่คือข้อความที่ตัดตอนมาจากคำตอบของฉัน

คุณต้องตระหนักถึงข้อเสียของการใช้ CHAR กับ VARCHAR

ด้วยฟิลด์ CHAR สิ่งที่คุณจัดสรรคือสิ่งที่คุณได้รับ ตัวอย่างเช่น CHAR (15) จัดสรรและจัดเก็บ 15 ไบต์ไม่ว่าคุณจะใส่อักขระในฟิลด์อย่างไร การจัดการสตริงนั้นง่ายและตรงไปตรงมาเนื่องจากขนาดของเขตข้อมูลสามารถคาดเดาได้ทั้งหมด

ด้วยฟิลด์ VARCHAR คุณจะได้รับเรื่องราวที่แตกต่างอย่างสิ้นเชิง ตัวอย่างเช่น VARCHAR (15) จะจัดสรรข้อมูลแบบไดนามิกมากถึง 16 ไบต์สูงสุด 15 สำหรับข้อมูลและอย่างน้อย 1 ไบต์เพิ่มเติมเพื่อจัดเก็บความยาวของข้อมูล หากคุณมีสตริง 'hello' ในการจัดเก็บที่ใช้เวลา 6 ไบต์ไม่ใช่ 5 การจัดการสตริงต้องดำเนินการตรวจสอบความยาวบางรูปแบบในทุกกรณี

การแลกเปลี่ยนจะเห็นได้ชัดเจนยิ่งขึ้นเมื่อคุณทำสองสิ่ง: 1. จัดเก็บล้านหรือหลายพันล้านแถว 2. การทำดัชนีคอลัมน์ที่มีทั้งแบบ CHAR หรือ VARCHAR

TRADEOFF # 1 เห็นได้ชัดว่า VARCHAR มีข้อได้เปรียบเนื่องจากข้อมูลที่มีความยาวผันแปรจะสร้างแถวที่เล็กลงและทำให้ไฟล์ทางกายภาพมีขนาดเล็กลง

TRADEOFF # 2 เนื่องจากเขตข้อมูล CHAR ต้องการการจัดการสตริงน้อยลงเนื่องจากความกว้างของฟิลด์คงที่การค้นหาดัชนีเทียบกับช่อง CHAR จะเร็วกว่าค่าเฉลี่ย 20% ของเขตข้อมูล VARCHAR นี่ไม่ใช่การคาดเดาใด ๆ ในส่วนของฉัน หนังสือการออกแบบและปรับแต่งฐานข้อมูล MySQL ทำสิ่งที่ยอดเยี่ยมบนโต๊ะ MyISAM เพื่อพิสูจน์สิ่งนี้ ตัวอย่างในหนังสือทำสิ่งต่อไปนี้:

ALTER TABLE tblname ROW_FORMAT=FIXED;

คำสั่งนี้บังคับให้ VARCHARs ทั้งหมดทำงานเป็น CHARs ฉันทำสิ่งนี้ที่งานก่อนหน้าของฉันในปี 2550 และเอาตาราง 300GB และเร่งการค้นหาดัชนีขึ้น 20% โดยไม่เปลี่ยนแปลงอะไรเลย มันทำงานตามที่เผยแพร่ อย่างไรก็ตามมันสร้างตารางเกือบสองเท่าในขนาด แต่มันกลับไปที่การแลกเปลี่ยน # 1

คุณสามารถวิเคราะห์ข้อมูลที่เก็บไว้เพื่อดูสิ่งที่ MySQL แนะนำสำหรับการกำหนดคอลัมน์ เพียงเรียกใช้สิ่งต่อไปนี้กับตารางใด ๆ :

SELECT * FROM tblname PROCEDURE ANALYSE();

สิ่งนี้จะสำรวจทั้งตารางและแนะนำคำนิยามคอลัมน์สำหรับทุกคอลัมน์ตามข้อมูลที่มีค่าฟิลด์ขั้นต่ำค่าฟิลด์สูงสุดและอื่น ๆ บางครั้งคุณต้องใช้สามัญสำนึกกับการวางแผน CHAR กับ VARCHAR นี่เป็นตัวอย่างที่ดี:

หากคุณกำลังจัดเก็บที่อยู่ IP มาสก์สำหรับคอลัมน์ดังกล่าวมีความยาวสูงสุด 15 อักขระ (xxx.xxx.xxx.xxx) ฉันจะกระโดดขึ้นมาทันทีCHAR(15)ใน heartbeat เพราะความยาวของที่อยู่ IP จะไม่แตกต่างกันมากนักและความซับซ้อนที่เพิ่มขึ้นของการจัดการสตริงที่ควบคุมโดยไบต์เพิ่มเติม คุณยังคงสามารถทำการPROCEDURE ANALYSE()ต่อต้านคอลัมน์ดังกล่าวได้ มันอาจแนะนำ VARCHAR ด้วยซ้ำ เงินของฉันจะยังคงเป็น CHAR มากกว่า VARCHAR ในกรณีนี้

ปัญหา CHAR vs VARCHAR สามารถแก้ไขได้ผ่านการวางแผนที่เหมาะสมเท่านั้น ด้วยพลังอันยิ่งใหญ่มาพร้อมความรับผิดชอบที่ยิ่งใหญ่ (ถ้อยคำที่เบื่อหู แต่จริง)

UPDATE

เมื่อพูดถึง MD5 การคำนวณstrlenภายในควรถูกกำจัดเมื่อสลับรูปแบบแถวทั้งหมด ไม่จำเป็นต้องเปลี่ยนการกำหนดเขตข้อมูล

ถ้าคีย์ MD5 เป็นเพียง VARCHAR ปัจจุบันฉันจะไปสำหรับมันและแปลงรูปแบบแถวของตารางจะคงที่ หากมีตัวเลขที่สำคัญของเขตข้อมูล VARCHAR อื่น ๆ อยู่พวกเขาก็จะได้รับประโยชน์เช่นกัน ในการแลกเปลี่ยนตารางจะขยายเป็นสองเท่าของขนาด แต่ข้อความค้นหาควรเร่งได้เร็วขึ้นประมาณ 20% โดยไม่ต้องปรับแต่งเพิ่มเติม


1
ฉันคิดว่าฉันจะใช้ตัวอักษรตัวหนึ่ง (4) หรือบางอย่างเช่นเลขจำนวนเต็มที่ไม่ได้ลงนามสำหรับที่อยู่ IP
Jack Douglas

@JackPDouglas คุณถูกต้องในจุดนั้น
RolandoMySQLDBA

ยังไม่ได้จัดเก็บดัชนีที่มีความยาวคงที่ใช่หรือไม่ ฉันไม่เข้าใจวิธีการเปลี่ยนรูปแบบการจัดเก็บข้อมูลเป็นความยาวคงที่ช่วยปรับปรุงการค้นหาดัชนี คุณหมายถึงการสแกนตารางที่ปรับปรุงแล้วหรือไม่
Marcus Adams

1
@JackDouglas, ทำไมไม่bitและbinary?
Pacerier

@ เพียร์ที่ดีกว่านี้ฉันเห็นด้วย :)
แจ็คดักลาส

19

ดูเหมือนว่าคุณจะประหยัด1 ไบต์ต่อค่าหรือประมาณ 3% charโดยการแปลงไป อาจไม่คุ้มค่าหากคุณเก็บMD5ในฐานสิบหกอยู่ดี - คุณสามารถประหยัด 50% ได้โดยใช้ a binaryแทน

ขอบคุณ Ovais (ดูความคิดเห็น) สำหรับการชี้ให้เห็นว่าchar(32)สามารถใช้งานได้มากกว่า 32 ไบต์หากคุณใช้ชุดอักขระแบบมัลติไบต์

ขอบคุณ Rick James ที่ชี้ให้เห็นว่าคุณควรใช้unhexฟังก์ชั่นในการแปลงสตริง hex เป็น binary:

create table foo(bar varbinary(100));
insert into foo(bar) values(md5('a')); 
insert into foo(bar) values(unhex(md5('a'))); 
select length(bar) from foo;
| ความยาว (แถบ) |
| ----------: |
| 32 |
| 16 |

db <> ซอที่นี่


โทรดีในการเปลี่ยนเป็นไบนารี
RThomas

ฉันกำลังวางแผนที่จะแปลงให้เป็นไบนารี ตอนนี้ที่ฉันคิดเกี่ยวกับมันขนาดไม่ควรจะแตกต่างกันเพียงแค่ขึ้นอยู่กับว่าฉันใช้ไบต์หรืออักขระเนื่องจากการเข้ารหัสของเราคือ utf-8 หรือฉันผิด
Jason Baker

@ Jason - การเข้ารหัสใช้ไม่ได้กับbinary- หรือฉันเข้าใจผิด?
Jack Douglas

3
สำหรับคอลัมน์ถ่าน (32) ที่มีชุดอักขระ utf-8 ทุกค่าจะต้องมีขนาด 32x3 ไบต์สำหรับการจัดเก็บ ทำไมคุณต้องตั้งค่าแฮ MD5 ให้เป็น utf-8 การแปลงเป็นไบนารี (32) จะต้องมี 32 ไบต์ต่อค่า
ovais.tariq

1
เปลี่ยนไปไม่น้อยมากนอกจากคุณจะใช้BINARY UNHEX()นั่นคือคุณสามารถจัดเก็บUNHEX(MD5(x))เป็น 16 ไบต์BINARY(16)เพื่อประหยัดพื้นที่ที่สำคัญกว่าการจัดเก็บลงในMD5(x) CHAR(32) CHARACTER SET ascii
Rick James

15

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

http://dev.mysql.com/doc/refman/5.0/en/char.html

โปรดสังเกตความคิดเห็นแรกในเอกสารที่ฉันลิงก์ไปด้านบน ... "CHAR จะเพิ่มความเร็วในการเข้าถึงของคุณหากบันทึกทั้งหมดมีขนาดคงที่นั่นคือถ้าคุณใช้วัตถุขนาดตัวแปรใด ๆ คุณอาจทำให้พวกเขาทั้งหมด ขนาดตัวแปรคุณไม่ได้รับความเร็วโดยใช้ CHAR ในตารางที่มี VARCHAR ด้วย "


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