ระหว่างutf8_general_ci
และutf8_unicode_ci
มีความแตกต่างในแง่ของประสิทธิภาพหรือไม่
utf8[mb4]_unicode_ci
คุณอาจจะชอบutf8[mb4]_unicode_520_ci
มากกว่านี้
utf8mb4_0900_ai_ci
ของ
ระหว่างutf8_general_ci
และutf8_unicode_ci
มีความแตกต่างในแง่ของประสิทธิภาพหรือไม่
utf8[mb4]_unicode_ci
คุณอาจจะชอบutf8[mb4]_unicode_520_ci
มากกว่านี้
utf8mb4_0900_ai_ci
ของ
คำตอบ:
การเปรียบเทียบทั้งสองนี้เป็นทั้งการเข้ารหัสอักขระ UTF-8 ความแตกต่างอยู่ในวิธีการเรียงลำดับข้อความและเปรียบเทียบ
หมายเหตุ: ใน MySQL คุณต้องใช้มากกว่าutf8mb4
utf8
พลุกพล่านutf8
เป็นข้อบกพร่องของการใช้งาน UTF-8 จาก MySQL รุ่นแรกซึ่งยังคงมีเพียงความเข้ากันได้แบบย้อนหลัง utf8mb4
รุ่นคงได้รับชื่อ
หมายเหตุ: MySQL เวอร์ชั่นใหม่กว่านี้ได้ปรับปรุงกฎการเรียงลำดับ Unicode ให้ใช้งานได้ภายใต้ชื่อเช่น utf8mb4_0900_ai_ci
สำหรับกฎที่เทียบเท่าโดยยึดตาม Unicode 9.0 - และไม่มี _general
ตัวแปรที่ เทียบเท่า คนที่อ่านข้อความนี้ในขณะนี้อาจจะใช้หนึ่งใน collations ใหม่เหล่านี้แทนการอย่างใดอย่างหนึ่งหรือ_unicode
สิ่งที่เขียนไว้ด้านล่างส่วนใหญ่ไม่ได้เป็นที่สนใจอีกต่อไปหากคุณสามารถใช้หนึ่งในการเปรียบเทียบที่ใหม่กว่าแทน_general
ความแตกต่างที่สำคัญ
utf8mb4_unicode_ci
ขึ้นอยู่กับกฎ Unicode อย่างเป็นทางการสำหรับการจัดเรียงและเปรียบเทียบสากลซึ่งเรียงลำดับอย่างถูกต้องในหลากหลายภาษา
utf8mb4_general_ci
เป็นชุดกฎการเรียงลำดับที่เรียบง่ายซึ่งมีจุดมุ่งหมายที่จะทำเช่นเดียวกับที่ทำได้ในขณะที่ใช้ช็อตคัทจำนวนมากที่ออกแบบมาเพื่อปรับปรุงความเร็ว มันไม่เป็นไปตามกฎ Unicode และจะส่งผลให้เกิดการเรียงลำดับหรือเปรียบเทียบที่ไม่พึงประสงค์ในบางสถานการณ์เช่นเมื่อใช้ภาษาหรืออักขระบางอย่าง
บนเซิร์ฟเวอร์ที่ทันสมัยการเพิ่มประสิทธิภาพนี้จะเป็นเพียงเล็กน้อยเท่านั้น มันถูกคิดค้นขึ้นในช่วงเวลาที่เซิร์ฟเวอร์มีประสิทธิภาพของ CPU เพียงเล็กน้อยในคอมพิวเตอร์ทุกวันนี้
ประโยชน์ของการutf8mb4_unicode_ci
มากกว่าutf8mb4_general_ci
utf8mb4_unicode_ci
ซึ่งใช้กฎ Unicode สำหรับการเรียงลำดับและการเปรียบเทียบใช้อัลกอริทึมที่ค่อนข้างซับซ้อนสำหรับการจัดเรียงที่ถูกต้องในหลากหลายภาษาและเมื่อใช้อักขระพิเศษหลากหลายชนิด กฎเหล่านี้ต้องคำนึงถึงอนุสัญญาเฉพาะภาษา ทุกคนไม่เรียงลำดับตัวละครของพวกเขาในสิ่งที่เราจะเรียกว่า 'ลำดับตามตัวอักษร'
เท่าที่ภาษาลาติน (เช่น "ยุโรป") ไม่มีความแตกต่างระหว่างการเรียงลำดับ Unicode และการutf8mb4_general_ci
เรียงลำดับแบบง่ายใน MySQL แต่ยังคงมีความแตกต่างเล็กน้อย:
ตัวอย่างเช่นการเรียง Unicode เรียง "ß" เช่น "ss" และ "Œ" เช่น "OE" เป็นคนที่ใช้อักขระเหล่านั้นตามปกติจะต้องการในขณะที่utf8mb4_general_ci
เรียงพวกเขาเป็นอักขระเดี่ยว (สมมุติว่า "s" และ "e" ตามลำดับ) .
อักขระ Unicode บางตัวถูกกำหนดเป็นเพิกเฉยซึ่งหมายความว่าไม่ควรนับรวมถึงลำดับการเรียงและการเปรียบเทียบควรย้ายไปยังอักขระถัดไปแทน utf8mb4_unicode_ci
จัดการเหล่านี้อย่างถูกต้อง
ในภาษาที่ไม่ใช่ภาษาละตินเช่นภาษาเอเชียหรือภาษาที่มีตัวอักษรที่แตกต่างกันอาจจะมีมากมากขึ้นความแตกต่างระหว่างการเรียงลำดับ Unicode และง่ายutf8mb4_general_ci
เรียงลำดับ ความเหมาะสมของutf8mb4_general_ci
จะขึ้นอยู่กับภาษาที่ใช้อย่างมาก สำหรับบางภาษามันค่อนข้างไม่เพียงพอ
คุณควรใช้อะไร
แทบจะไม่มีเหตุผลที่จะใช้utf8mb4_general_ci
อีกต่อไปเพราะเราทิ้งไว้ข้างหลังจุดที่ความเร็วของ CPU ต่ำพอที่ความแตกต่างของประสิทธิภาพจะมีความสำคัญ ฐานข้อมูลของคุณจะถูก จำกัด ด้วยคอขวดอื่น ๆ อย่างแน่นอน
ในอดีตบางคนแนะนำให้ใช้utf8mb4_general_ci
ยกเว้นเมื่อการเรียงลำดับที่แม่นยำจะมีความสำคัญพอที่จะประเมินต้นทุนการปฏิบัติ วันนี้ค่าใช้จ่ายด้านประสิทธิภาพนั้นหายไปหมดและนักพัฒนากำลังรักษาความเป็นสากลมากขึ้น
มีการถกเถียงกันอยู่ว่าถ้าความเร็วสำคัญกับคุณมากกว่าความแม่นยำคุณอาจไม่ต้องทำการเรียงลำดับใด ๆ เลย มันไม่สำคัญที่จะทำให้อัลกอริทึมเร็วขึ้นหากคุณไม่ต้องการให้มันถูกต้อง ดังนั้นutf8mb4_general_ci
การประนีประนอมที่อาจไม่จำเป็นสำหรับเหตุผลด้านความเร็วและอาจไม่เหมาะสำหรับเหตุผลด้านความแม่นยำ
สิ่งหนึ่งที่ฉันจะเพิ่มคือแม้ว่าคุณจะรู้ว่าแอปพลิเคชันของคุณสนับสนุนเฉพาะภาษาอังกฤษ แต่ก็อาจจำเป็นต้องจัดการกับชื่อของผู้คนซึ่งมักจะมีตัวอักษรที่ใช้ในภาษาอื่น ๆ . การใช้กฎ Unicode สำหรับทุกสิ่งช่วยเพิ่มความอุ่นใจว่าคน Unicode ที่ฉลาดมากทำงานหนักมากเพื่อให้การเรียงลำดับทำงานได้อย่างถูกต้อง
ชิ้นส่วนหมายถึงอะไร
ประการแรกci
คือสำหรับการเรียงลำดับและการเปรียบเทียบแบบคำนึงถึงขนาดตัวพิมพ์ ซึ่งหมายความว่ามันเหมาะสำหรับข้อมูลที่เป็นข้อความและกรณีไม่สำคัญ การเปรียบเทียบประเภทอื่นคือcs
( bin
คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่) สำหรับข้อมูลต้นฉบับที่กรณีสำคัญและสำหรับการเข้ารหัสที่ต้องตรงกันบิตสำหรับบิตซึ่งเหมาะสำหรับเขตข้อมูลที่เข้ารหัสข้อมูลไบนารีจริง ๆ (รวมถึงตัวอย่างเช่น Base64) การเรียงลำดับตามตัวพิมพ์เล็กและตัวพิมพ์ใหญ่นำไปสู่ผลลัพธ์แปลก ๆ และการเปรียบเทียบแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่อาจส่งผลให้ค่าซ้ำซ้อนแตกต่างกันในกรณีตัวอักษรเท่านั้นดังนั้นการเรียงตามตัวพิมพ์ใหญ่ - ตัวพิมพ์เล็ก และอาจมีความสำคัญเช่นกันและการเปรียบเทียบแบบไบนารีอาจเหมาะสมกว่า
ถัดไปunicode
หรือgeneral
อ้างถึงกฎการเรียงลำดับและการเปรียบเทียบเฉพาะ - โดยเฉพาะอย่างยิ่งวิธีที่ข้อความถูกทำให้เป็นมาตรฐานหรือเปรียบเทียบ มีกฎหลายชุดสำหรับการเข้ารหัสอักขระ utf8mb4 โดยมีunicode
และgeneral
เป็นสองชุดที่พยายามทำงานได้ดีในภาษาที่เป็นไปได้ทั้งหมดมากกว่าหนึ่งชุด ความแตกต่างระหว่างกฎทั้งสองชุดนี้เป็นหัวข้อของคำตอบนี้ โปรดทราบว่าunicode
ใช้กฎจาก Unicode 4.0 MySQL รุ่นล่าสุดเพิ่มชุดกฎunicode_520
โดยใช้กฎจาก Unicode 5.2 และ0900
(วางส่วน "unicode_") โดยใช้กฎจาก Unicode 9.0
และสุดท้ายutf8mb4
คือการเข้ารหัสอักขระที่ใช้ภายใน ในคำตอบนี้ฉันกำลังพูดถึงการเข้ารหัสตาม Unicode เท่านั้น
utf8_general_ci
: มันใช้งานไม่ได้ มันเป็นการย้อนกลับไปสู่ยุคสมัยที่ไม่ดีของ ASCII ในฐานะคนเฝ้าประตูเมื่อห้าสิบปีก่อน การจับคู่แบบไม่คำนึงถึงตัวอักษรแบบ Unicode ไม่สามารถทำได้หากไม่มีแผนผังการพับจาก UCD ตัวอย่างเช่น“ Σίσυφος” มีสามซิกมาสอยู่ในนั้น หรือตัวพิมพ์เล็กของ "TSCHüẞ" คือ "tschüβ" แต่ตัวพิมพ์ใหญ่ของ "tschüβ" คือ "TSCHÜSS" คุณสามารถพูดถูกหรือเร็ว ดังนั้นคุณต้องใช้utf8_unicode_ci
เพราะถ้าคุณไม่สนใจเรื่องความถูกต้องมันเป็นเรื่องไม่สำคัญที่จะทำให้มันเร็วอย่างไม่มีที่สิ้นสุด
"か" == "が"
"ǽ" == "æ"
สำหรับการเรียงลำดับสิ่งนี้สมเหตุสมผล แต่อาจเป็นเรื่องที่น่าแปลกใจเมื่อเลือกผ่านอีควิตี้
utf8mb4
เป็นทางเลือกเดียวที่ถูกต้อง เมื่อutf8
คุณติดอยู่ใน MySQL-only บางตัวตัวแปร 3 ไบต์ของ UTF8 ที่เฉพาะ MySQL (และ MariaDB) เท่านั้นที่รู้ว่าต้องทำอะไร ส่วนที่เหลือของโลกจะใช้ UTF8 ซึ่งสามารถมีได้ถึง 4 ไบต์ต่อตัวละคร MySQL ที่ devs เรียกไม่ถูกเข้ารหัส homebrew ของพวกเขาutf8
และไม่ทำลายกันได้ย้อนหลังตอนนี้พวกเขามีการอ้างถึง UTF8 utf8mb4
จริง
ฉันต้องการทราบว่าประสิทธิภาพการทำงานที่แตกต่างระหว่างการใช้งานutf8_general_ci
กับอะไรutf8_unicode_ci
แต่ฉันไม่พบการวัดประสิทธิภาพใด ๆ ที่ระบุไว้บนอินเทอร์เน็ตดังนั้นฉันจึงตัดสินใจสร้างการวัดประสิทธิภาพด้วยตนเอง
ฉันสร้างตารางง่าย ๆ ที่มี 500,000 แถว:
CREATE TABLE test(
ID INT(11) DEFAULT NULL,
Description VARCHAR(20) DEFAULT NULL
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;
จากนั้นฉันก็เติมข้อมูลด้วยการสุ่มโดยรันโพรซีเดอร์นี้
CREATE PROCEDURE randomizer()
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE random CHAR(20) ;
theloop: loop
SET random = CONV(FLOOR(RAND() * 99999999999999), 20, 36);
INSERT INTO test VALUES (i+1, random);
SET i=i+1;
IF i = 500000 THEN
LEAVE theloop;
END IF;
END LOOP theloop;
END
แล้วฉันจะสร้างต่อไปนี้วิธีการจัดเก็บเพื่อมาตรฐานที่เรียบง่ายSELECT
, SELECT
มีLIKE
, และการเรียงลำดับ ( SELECT
กับORDER BY
):
CREATE PROCEDURE benchmark_simple_select()
BEGIN
DECLARE i INT DEFAULT 0;
theloop: loop
SELECT *
FROM test
WHERE Description = 'test' COLLATE utf8_general_ci;
SET i = i + 1;
IF i = 30 THEN
LEAVE theloop;
END IF;
END LOOP theloop;
END;
CREATE PROCEDURE benchmark_select_like()
BEGIN
DECLARE i INT DEFAULT 0;
theloop: loop
SELECT *
FROM test
WHERE Description LIKE '%test' COLLATE utf8_general_ci;
SET i = i + 1;
IF i = 30 THEN
LEAVE theloop;
END IF;
END LOOP theloop;
END;
CREATE PROCEDURE benchmark_order_by()
BEGIN
DECLARE i INT DEFAULT 0;
theloop: loop
SELECT *
FROM test
WHERE ID > FLOOR(1 + RAND() * (400000 - 1))
ORDER BY Description COLLATE utf8_general_ci LIMIT 1000;
SET i = i + 1;
IF i = 10 THEN
LEAVE theloop;
END IF;
END LOOP theloop;
END;
ในขั้นตอนการจัดเก็บไว้ข้างต้นutf8_general_ci
การเปรียบเทียบจะใช้ แต่แน่นอนในระหว่างการทดสอบที่ผมใช้ทั้งสองและutf8_general_ci
utf8_unicode_ci
ฉันเรียกแต่ละกระบวนงานที่เก็บไว้ 5 ครั้งสำหรับการเปรียบเทียบแต่ละครั้ง (5 ครั้งutf8_general_ci
และ 5 ครั้งutf8_unicode_ci
) จากนั้นคำนวณค่าเฉลี่ย
ผลลัพธ์ของฉันคือ:
benchmark_simple_select()
utf8_general_ci
: 9,957 ms utf8_unicode_ci
: 10,271 ms ในมาตรฐานนี้ใช้utf8_unicode_ci
ช้ากว่าutf8_general_ci
3.2%
benchmark_select_like()
utf8_general_ci
: 11,441 ms utf8_unicode_ci
: 12,811 ms ในการวัดประสิทธิภาพนี้ใช้utf8_unicode_ci
ช้ากว่าutf8_general_ci
12%
benchmark_order_by()
utf8_general_ci
: 11,944 ms utf8_unicode_ci
: 12,887 ms ในการวัดประสิทธิภาพนี้ใช้utf8_unicode_ci
ช้ากว่าutf8_general_ci
7.9%
utf8_general_ci
มันน้อยเกินไปที่จะคุ้มค่าที่จะใช้
CONV(FLOOR(RAND() * 99999999999999), 20, 36)
สร้างเฉพาะ ASCII เท่านั้นและไม่มีอักขระ Unicode ที่จะดำเนินการโดยอัลกอริทึมของการเปรียบเทียบ 2) Description = 'test' COLLATE ...
และDescription LIKE 'test%' COLLATE ...
ประมวลผลสตริงเดียวเท่านั้น ("ทดสอบ") ตอนรันไทม์ใช่ไหม 3) ในแอปจริงคอลัมน์ที่ใช้ในการสั่งซื้ออาจจะถูกจัดทำดัชนีและความเร็วในการจัดทำดัชนีในการเปรียบเทียบที่แตกต่างกันด้วยข้อความที่ไม่ใช่ ASCII จริงอาจแตกต่างกัน
โพสต์นี้จะอธิบายอย่างดีมาก
กล่าวโดยย่อ: utf8_unicode_ci ใช้อัลกอริทึมการจัดเรียง Unicode ตามที่กำหนดไว้ในมาตรฐาน Unicode ในขณะที่ utf8_general_ci เป็นคำสั่งการเรียงลำดับที่ง่ายขึ้นซึ่งส่งผลให้ผลลัพธ์การเรียงลำดับ "แม่นยำน้อยลง"
utf8_unicode_ci
และแสร้งทำเป็นอีกอันหนึ่งไม่มีอยู่
utf8_general_ci
อาจเหมาะสำหรับคุณ
ดูคู่มือ mysql, ชุดอักขระ Unicode :
สำหรับชุดอักขระ Unicode ใด ๆ การดำเนินการที่ดำเนินการโดยใช้การเปรียบเทียบ _general_ci นั้นเร็วกว่าการเปรียบเทียบ _unicode_ci ตัวอย่างเช่นการเปรียบเทียบสำหรับการเปรียบเทียบ utf8_general_ci นั้นเร็วกว่า แต่ถูกต้องน้อยกว่าการเปรียบเทียบสำหรับ utf8_unicode_ci เหตุผลสำหรับสิ่งนี้คือ utf8_unicode_ci รองรับการแมปเช่นการขยาย นั่นคือเมื่ออักขระหนึ่งตัวเปรียบเทียบเท่ากับชุดของอักขระอื่น ตัวอย่างเช่นในภาษาเยอรมันและภาษาอื่น ๆ “ ß” เท่ากับ“ ss” utf8_unicode_ci ยังรองรับตัวย่อและอักขระที่ละเว้น utf8_general_ci คือการเปรียบเทียบแบบดั้งเดิมที่ไม่รองรับการขยายตัวย่อหรือตัวอักษรที่เพิกเฉย มันสามารถทำการเปรียบเทียบแบบหนึ่งต่อหนึ่งระหว่างตัวละคร
ดังนั้นเพื่อสรุป utf_general_ci ใช้ชุดการเปรียบเทียบที่เล็กกว่าและถูกต้อง (ตามมาตรฐาน) น้อยกว่า utf_unicode_ci ซึ่งควรใช้ทั้งมาตรฐาน ชุด general_ci จะเร็วขึ้นเนื่องจากมีการคำนวณน้อยกว่า
utf8_unicode_ci
และแสร้งทำเป็นว่ารถที่ใช้ไม่ได้
0
และ1
ไม่ใช่บูล :) EG เลือกจุดทางภูมิศาสตร์ในกล่องขอบเขตเป็นการประมาณ 'จุดใกล้เคียง' ซึ่งไม่ดีเท่ากับการคำนวณระยะทางระหว่างจุดและจุดอ้างอิงและการกรองบนนั้น แต่ทั้งคู่เป็นการประมาณค่าและในความเป็นจริงความถูกต้องทั้งหมดนั้นไม่สามารถทำได้ ดูเส้นขนานที่ชายฝั่งและIEEE 754
1/3
กล่าวโดยย่อ:
หากคุณต้องการลำดับการเรียงที่ดีกว่า - ใช้utf8_unicode_ci
(นี่เป็นวิธีที่ต้องการ)
แต่ถ้าคุณสนใจประสิทธิภาพการทำงานอย่างเต็มที่ - ใช้งานutf8_general_ci
แต่รู้ว่ามันล้าสมัยไปเล็กน้อย
ความแตกต่างในแง่ของประสิทธิภาพมีน้อยมาก
ในขณะที่เราสามารถอ่านได้ที่นี่ ( Peter Gulutzan ) มีความแตกต่างในการเรียงลำดับ / การเปรียบเทียบตัวอักษรโปแลนด์ "Ł" (L กับโรคหลอดเลือดสมอง - html esc:) Ł
(กรณีที่ต่ำกว่า: "ł" - html esc:) ł
- เรามีสมมติฐานดังนี้
utf8_polish_ci Ł greater than L and less than M
utf8_unicode_ci Ł greater than L and less than M
utf8_unicode_520_ci Ł equal to L
utf8_general_ci Ł greater than Z
ในจดหมายฉบับภาษาโปแลนด์Ł
คือหลังจากที่ตัวอักษรและก่อนที่L
M
ไม่มีการเข้ารหัสใดที่ดีกว่าหรือแย่กว่านี้ขึ้นอยู่กับความต้องการของคุณ
การเรียงลำดับและการจับคู่อักขระมีความแตกต่างใหญ่สองอย่าง:
เรียงลำดับ :
utf8mb4_general_ci
ลบสำเนียงทั้งหมดและเรียงลำดับทีละรายการซึ่งอาจสร้างผลลัพธ์การเรียงลำดับที่ไม่ถูกต้องutf8mb4_unicode_ci
เรียงลำดับที่ถูกต้องการจับคู่ตัวละคร
พวกเขาจับคู่ตัวละครแตกต่างกัน
ยกตัวอย่างเช่นในutf8mb4_unicode_ci
คุณมีi != ı
แต่มันถือutf8mb4_general_ci
ı=i
name="Yılmaz"
ตัวอย่างเช่นสมมติคุณมีแถวด้วย แล้วก็
select id from users where name='Yilmaz';
จะส่งคืนแถวหากการจัดวางเป็นutf8mb4_general_ci
แต่ถ้าเป็นการจัดวางด้วยutf8mb4_unicode_ci
จะไม่ส่งคืนแถว!
บนมืออื่น ๆ ที่เรามีที่a=ª
และß=ss
ในซึ่งเป็นกรณีที่ไม่อยู่ในutf8mb4_unicode_ci
utf8mb4_general_ci
ดังนั้นลองจินตนาการว่าคุณมีแถวอยู่name="ªßi"
แล้ว
select id from users where name='assi';
จะกลับมาแถวถ้าการจัดระเบียบเป็นutf8mb4_unicode_ci
แต่จะไม่ได้utf8mb4_general_ci
กลับมาแถวจัดระเบียบถ้ามีการตั้งค่า
รายการเต็มรูปแบบของการแข่งขันในแต่ละการจัดระเบียบอาจจะพบได้ที่นี่
ตามโพสต์นี้มีประโยชน์อย่างมากใน MySQL 5.7 เมื่อใช้ utf8mb4_general_ci แทน utf8mb4_unicode_ci: https://www.percona.com/blog/2019/02/27/charset-and-collation-set-impec -ON-MySQL ประสิทธิภาพ /