รูปแบบประสิทธิภาพของแบบสอบถาม PostgreSQL LIKE


113

ฉันเห็นการเปลี่ยนแปลงค่อนข้างมากในเวลาตอบสนองเกี่ยวกับการLIKEสืบค้นตารางใดตารางหนึ่งในฐานข้อมูลของฉัน บางครั้งฉันจะได้ผลลัพธ์ภายใน 200-400 มิลลิวินาที (ยอมรับได้มาก) แต่บางครั้งอาจใช้เวลามากถึง 30 วินาทีในการส่งคืนผลลัพธ์

ฉันเข้าใจว่าLIKEข้อความค้นหามีทรัพยากรมาก แต่ฉันไม่เข้าใจว่าทำไมเวลาตอบกลับจึงมีความแตกต่างกันมาก ฉันได้สร้างดัชนี btree บนowner1สนาม แต่ฉันไม่คิดว่ามันจะช่วยในการLIKEสืบค้น ใครมีไอเดียบ้าง

SQL ตัวอย่าง:

SELECT gid, owner1 FORM parcels
WHERE owner1 ILIKE '%someones name%' LIMIT 10

ฉันได้ลองแล้ว:

SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%') LIMIT 10

และ:

SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('someones name%') LIMIT 10

ด้วยผลลัพธ์ที่คล้ายกัน.
จำนวนแถวของตาราง: ประมาณ 95,000

คำตอบ:


284

FTS ไม่รองรับ LIKE

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

ดัชนี Trigram สำหรับ LIKE

ติดตั้งโมดูลเพิ่มเติมpg_trgmซึ่งจัดเตรียมคลาสตัวดำเนินการสำหรับดัชนีทริกเกอร์ GIN และ GiSTเพื่อรองรับรูปแบบและรูปแบบทั้งหมดLIKEILIKEไม่ใช่เฉพาะที่ยึดด้านซ้าย:

ตัวอย่างดัชนี:

CREATE INDEX tbl_col_gin_trgm_idx  ON tbl USING gin  (col gin_trgm_ops);

หรือ:

CREATE INDEX tbl_col_gist_trgm_idx ON tbl USING gist (col gist_trgm_ops);

ตัวอย่างแบบสอบถาม:

SELECT * FROM tbl WHERE col LIKE '%foo%';   -- leading wildcard
SELECT * FROM tbl WHERE col ILIKE '%foo%';  -- works case insensitively as well

Trigrams? แล้วสตริงที่สั้นกว่าล่ะ?

คำที่มีตัวอักษรน้อยกว่า 3 ตัวในค่าดัชนียังคงใช้ได้ คู่มือ:

แต่ละคำถือว่ามีช่องว่างสองช่องนำหน้าและมีช่องว่างหนึ่งช่องต่อท้ายเมื่อกำหนดชุดของตรีโกณมิติที่อยู่ในสตริง

และรูปแบบการค้นหาที่มีตัวอักษรน้อยกว่า 3 ตัว? คู่มือ:

สำหรับการค้นหาทั้งแบบLIKEและนิพจน์ทั่วไปโปรดทราบว่ารูปแบบที่ไม่มีทริกเกอร์ที่แยกได้จะลดระดับลงเป็นการสแกนแบบเต็มดัชนี

หมายความว่าการสแกนดัชนีดัชนี / บิตแมปนั้นยังคงใช้งานได้ (แผนการสืบค้นสำหรับคำสั่งที่เตรียมไว้จะไม่หยุดทำงาน) มันจะไม่ทำให้คุณมีประสิทธิภาพที่ดีขึ้น โดยทั่วไปแล้วจะไม่มีการสูญเสียครั้งใหญ่เนื่องจากสตริง 1 หรือ 2 ตัวอักษรแทบจะไม่สามารถเลือกได้ (มากกว่าสองสามเปอร์เซ็นต์ของตารางที่ตรงกัน) และการรองรับดัชนีจะไม่ช่วยเพิ่มประสิทธิภาพในการเริ่มต้นเนื่องจากการสแกนแบบเต็มตารางทำได้เร็วขึ้น


text_pattern_ops สำหรับการจับคู่คำนำหน้า

สำหรับรูปแบบที่ยึดด้านซ้ายเท่านั้น(ไม่มีสัญลักษณ์นำหน้า) คุณจะได้รับสิ่งที่ดีที่สุดด้วยคลาสตัวดำเนินการที่เหมาะสมสำหรับดัชนี btree: text_pattern_opsหรือvarchar_pattern_ops. ทั้งคุณสมบัติในตัวของ Postgres มาตรฐานไม่จำเป็นต้องมีโมดูลเพิ่มเติม ประสิทธิภาพใกล้เคียงกัน แต่ดัชนีเล็กกว่ามาก

ตัวอย่างดัชนี:

CREATE INDEX tbl_col_text_pattern_ops_idx ON tbl(col text_pattern_ops);

ตัวอย่างแบบสอบถาม:

SELECT * FROM tbl WHERE col LIKE 'foo%';  -- no leading wildcard

หรือถ้าคุณควรเรียกใช้ฐานข้อมูลของคุณด้วยโลแคล'C' ( ไม่มีโลแคลอย่างมีประสิทธิภาพ) ทุกอย่างจะถูกจัดเรียงตามลำดับไบต์ต่อไปและดัชนี btree ธรรมดาที่มีคลาสตัวดำเนินการเริ่มต้นจะทำงาน

รายละเอียดเพิ่มเติมคำอธิบายตัวอย่างและลิงค์ในคำตอบที่เกี่ยวข้องเหล่านี้บน dba.SE:


หากไม่มีสัญลักษณ์นำหน้าบนตาราง 500K บรรทัดดัชนีจินที่มี gin_trgm_ops จะเร็วกว่า btree ถึง 10 เท่า
ลัส

@nicolas: การเปรียบเทียบขึ้นอยู่กับตัวแปรหลายตัว ความยาวคีย์การกระจายข้อมูลความยาวรูปแบบดัชนีที่เป็นไปได้สแกนเท่านั้น ... และที่สำคัญที่สุด: เวอร์ชัน Postgres ดัชนี GIN ได้รับการปรับปรุงอย่างมากในหน้า 9.4 และ 9.5 เวอร์ชันใหม่ของ pg_trgm (ที่จะออกในหน้า 9.6) กำลังจะมีการปรับปรุงเพิ่มเติม
Erwin Brandstetter

1
ถ้าฉันมีเอกสารถูกต้องโดยที่pg_trgmคุณต้องมีสตริงการสืบค้นที่มีความยาวอย่างน้อย 3 อักขระเช่นfo%จะไม่ตีดัชนี แต่ทำการสแกนแทน สิ่งที่ควรทราบ
Tuukka Mustonen

1
@TuukkaMustonen: จุดดี การสแกนดัชนี (บิตแมป) ยังคงใช้งานได้พวกเขาจะไม่ซื้อประสิทธิภาพที่ดีกว่าให้คุณ ฉันได้เพิ่มคำชี้แจงข้างต้น
Erwin Brandstetter

7

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

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


ฉันไม่ได้คิดเกี่ยวกับการสร้างดัชนีสำหรับค่าตัวพิมพ์เล็กของฟิลด์ ด้วยวิธีนี้ฉันสามารถแปลงข้อความแบบสอบถามเป็นตัวพิมพ์เล็กบนแบ็กเอนด์ก่อนที่จะทำการสืบค้น
Jason

4

คุณสามารถติดตั้งWildspeedซึ่งเป็นดัชนีประเภทอื่นใน PostgreSQL Wildspeed ทำงานร่วมกับอักขระตัวแทน% word% ได้ไม่มีปัญหา ข้อเสียคือขนาดของดัชนีซึ่งอาจมีขนาดใหญ่มาก


3

โปรดดำเนินการค้นหาด้านล่างเพื่อปรับปรุงประสิทธิภาพการสืบค้น LIKE ใน postgresql สร้างดัชนีเช่นนี้สำหรับตารางที่ใหญ่ขึ้น:

CREATE INDEX <indexname> ON <tablename> USING btree (<fieldname> text_pattern_ops)

สิ่งนี้ใช้ได้เฉพาะเมื่อรูปแบบไม่ได้ขึ้นต้นด้วยสัญลักษณ์แทน - ในกรณีนี้แบบสอบถามตัวอย่างสองรายการแรกทั้งสองจะเริ่มต้นด้วยสัญลักษณ์แทน
cbz

1

สำหรับสิ่งที่คุ้มค่าDjango ORM มักจะใช้UPPER(text)สำหรับการLIKEค้นหาทั้งหมดเพื่อให้ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่

การเพิ่มดัชนีUPPER(column::text)ช่วยเร่งระบบของฉันอย่างมากไม่เหมือนกับสิ่งอื่นใด

เท่า% นำหน้าใช่ว่าจะไม่ใช้ดัชนี ดูบล็อกนี้สำหรับคำอธิบายที่ดี:

https://use-the-index-luke.com/sql/where-clause/searching-for-ranges/like-performance-tuning


1

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

SELECT owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%');

เช่น

CREATE INDEX ix_parcels ON parcels(position(lower('someones name') in lower(owner1)));

SELECT owner1 FROM parcels
WHERE position(lower('someones name') in lower(owner1)) > 0;

ฉันรู้สึกยินดีเป็นอย่างยิ่งเมื่อการสืบค้นกลับมาอย่างรวดเร็วและตรวจสอบแล้วว่าดัชนีถูกใช้กับEXPLAIN ANALYZE:

 Bitmap Heap Scan on parcels  (cost=7.66..25.59 rows=453 width=32) (actual time=0.006..0.006 rows=0 loops=1)
   Recheck Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
   ->  Bitmap Index Scan on ix_parcels  (cost=0.00..7.55 rows=453 width=0) (actual time=0.004..0.004 rows=0 loops=1)
         Index Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
 Planning time: 0.075 ms
 Execution time: 0.025 ms

0

คำค้นหาที่คุณชอบอาจไม่สามารถใช้ดัชนีที่คุณสร้างขึ้นได้เนื่องจาก:

1) เกณฑ์ LIKE ของคุณเริ่มต้นด้วยสัญลักษณ์แทน

2) คุณเคยใช้ฟังก์ชันกับเกณฑ์ LIKE ของคุณ


0

เมื่อคุณใช้ประโยคบนคอลัมน์ที่มีฟังก์ชันเช่น LIKE, ILIKE, upper, lower เป็นต้นจากนั้น postgres จะไม่นำดัชนีปกติของคุณมาพิจารณา มันจะทำการสแกนแบบเต็มตารางผ่านแต่ละแถวดังนั้นมันจะช้า

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

create index ix_tblname_col_upper on tblname (UPPER(col) varchar_pattern_ops);

ในทำนองเดียวกันถ้าคอลัมน์ของคุณเป็นข้อความคุณก็ทำสิ่งนี้

create index ix_tblname_col_upper on tblname (UPPER(col) text_pattern_ops);

ในทำนองเดียวกันคุณสามารถเปลี่ยนฟังก์ชันส่วนบนเป็นฟังก์ชันอื่น ๆ ที่คุณต้องการได้

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