ทำไมข้อความค้นหา sqlite นี้ช้าลงมากเมื่อฉันสร้างดัชนีคอลัมน์


14

ฉันมีฐานข้อมูล sqlite ที่มีสองตารางแต่ละแถวมี 50,000 แถวมีชื่อ (คน) ปลอม ฉันได้สร้างแบบสอบถามง่ายๆเพื่อค้นหาจำนวนชื่อที่มี (ชื่อที่กำหนด, ชื่อกลาง, นามสกุล) ที่ใช้ร่วมกันสำหรับทั้งสองตาราง:

select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;

เมื่อไม่มีดัชนียกเว้นในคีย์หลัก (ไม่เกี่ยวข้องกับแบบสอบถามนี้) มันจะทำงานอย่างรวดเร็ว:

[james@marlon Downloads] $ time sqlite3 generic_data_no_indexes.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131

real    0m0.115s
user    0m0.111s
sys     0m0.004s

แต่ถ้าฉันเพิ่มดัชนีลงในสามคอลัมน์ในแต่ละตาราง (ดัชนีทั้งหมดหกรายการ):

CREATE INDEX `idx_uk_givenname` ON `fakenames_uk` (`givenname` )
//etc.

จากนั้นมันจะทำงานช้าลงอย่างเจ็บปวด:

[james@marlon Downloads] $ time sqlite3 generic_data.sqlite "select count(*) from fakenames_uk inner join fakenames_usa on fakenames_uk.givenname=fakenames_usa.givenname and fakenames_uk.surname=fakenames_usa.surname and fakenames_uk.middleinitial=fakenames_usa.middleinitial;"
131

real    1m43.102s
user    0m52.397s
sys     0m50.696s

มีการสัมผัสหรือเหตุผลในการนี้หรือไม่?

นี่คือผลลัพธ์ของEXPLAIN QUERY PLANเวอร์ชันที่ไม่มีดัชนี:

0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING AUTOMATIC COVERING INDEX (middleinitial=? AND surname=? AND givenname=?)

นี่คือกับดัชนี:

0|0|0|SCAN TABLE fakenames_uk
0|1|1|SEARCH TABLE fakenames_usa USING INDEX idx_us_middleinitial (middleinitial=?)

1
ดัชนีของคุณไม่ครอบคลุม ดูเหมือนว่าคุณกำลังจัดทำดัชนีแต่ละคอลัมน์แยกกัน จะเกิดอะไรขึ้นเมื่อคุณสร้างดัชนีครอบคลุมที่มีทั้งสามคอลัมน์ในดัชนี ( middleinitial, surnameและgivenname)?
Randolph West

@ Randoph West ฉันเข้าใจสิ่งที่คุณหมายถึง แต่คุณไม่ได้ใช้คำศัพท์ที่ถูกต้อง: "ดัชนีครอบคลุม" คือสิ่งที่รวมคอลัมน์ที่ถูกเลือกด้วย ตัวอย่างเช่นสำหรับแบบสอบถามSELECT c FROM t WHERE a=1 AND b=2ดัชนีt(a,b,c)จะครอบคลุม แต่t(a,b)ไม่ใช่ ประโยชน์ของดัชนีครอบคลุมคือผลการสืบค้นทั้งหมดสามารถดึงออกมาจากดัชนีได้โดยตรงในขณะที่ดัชนีที่ไม่ครอบคลุมจะค้นหาแถวที่เกี่ยวข้องได้อย่างรวดเร็ว แต่ก็ยังจำเป็นต้องอ้างอิงข้อมูลตารางหลักเพื่อเลือกค่า
Arthur Tacca

คำตอบ:


15

ใน SQLite การรวมจะดำเนินการในลักษณะวนซ้ำซ้อนกันเช่นฐานข้อมูลผ่านหนึ่งตารางและสำหรับแต่ละแถวค้นหาแถวที่ตรงกันจากตารางอื่น

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

ในกรณีนี้มีสามดัชนีที่เป็นไปได้ หากไม่มีข้อมูลสถิติใด ๆ (ซึ่งจะถูกสร้างขึ้นโดยการเรียกใช้ANALYZE ) ฐานข้อมูลจะเลือกข้อมูลที่เล็กที่สุดเพื่อลด I / O อย่างไรก็ตามmiddleinitialดัชนีไม่มีประโยชน์เพราะไม่ลดจำนวนแถวของตารางที่ต้องดึงข้อมูลมาก และขั้นตอนเพิ่มเติมผ่านดัชนีจะเพิ่ม I / O ที่จำเป็นจริง ๆ เนื่องจากแถวของตารางไม่ได้อ่านตามลำดับ แต่สุ่ม

หากไม่มีดัชนีการค้นหาแถวที่ตรงกันจะต้องใช้การสแกนตารางแบบสมบูรณ์ของตารางที่สองสำหรับแต่ละแถวของตารางแรก สิ่งนี้จะแย่มากที่ฐานข้อมูลประเมินว่ามันคุ้มค่าที่จะสร้างแล้วปล่อยดัชนีชั่วคราวสำหรับแบบสอบถามนี้ ดัชนีชั่วคราว ("AUTOMATIC") นี้สร้างขึ้นในทุก colunms ที่ใช้สำหรับการค้นหา การดำเนินการ COUNT (*) ไม่ต้องการค่าจากคอลัมน์อื่น ๆ ดังนั้นดัชนีนี้จึงเป็นดัชนีที่ครอบคลุมซึ่งหมายความว่าไม่จำเป็นต้องค้นหาแถวของตารางที่สอดคล้องกับรายการดัชนีซึ่งช่วยประหยัดฉันได้มากกว่าเดิม / O

หากต้องการเพิ่มความเร็วข้อความค้นหานี้ให้สร้างดัชนีนี้อย่างถาวรเพื่อไม่จำเป็นต้องสร้างดัชนีชั่วคราวอีกต่อไป:

CREATE INDEX uk_all_names ON fakenames_uk(surname, givenname, middleinitial);

EXPLAIN QUERY PLAN
SELECT count(*)
FROM fakenames_uk
JOIN fakenames_usa USING (givenname, middleinitial, surname);

0|0|1|SCAN TABLE fakenames_usa
0|1|0|SEARCH TABLE fakenames_uk USING COVERING INDEX uk_all_names (surname=? AND givenname=? AND middleinitial=?)

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

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