วิธีการค้นหารายการสตริง / ระเบียนที่มีขนาดใหญ่มากอย่างรวดเร็วบนฐานข้อมูล


32

ฉันมีปัญหาดังต่อไปนี้: ฉันมีฐานข้อมูลที่มีมากกว่า 2 ล้านบันทึก แต่ละระเบียนมีเขตข้อมูลสตริง X และฉันต้องการแสดงรายการของระเบียนที่เขตข้อมูล X ประกอบด้วยสตริงที่แน่นอน แต่ละระเบียนมีขนาดประมาณ 500 ไบต์

เพื่อให้เป็นรูปธรรมมากขึ้น: ใน GUI ของแอปพลิเคชันของฉันฉันมีช่องข้อความที่ฉันสามารถป้อนสตริงได้ ด้านบนของฟิลด์ข้อความฉันมีตารางที่แสดงเรคคอร์ด (N แรกเช่น 100) ที่ตรงกับสตริงในฟิลด์ข้อความ เมื่อฉันพิมพ์หรือลบอักขระหนึ่งตัวในฟิลด์ข้อความเนื้อหาของตารางจะต้องได้รับการอัปเดตทันที

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

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

แก้ไข

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

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


2
2 ล้านเรคคอร์ด * 500 ไบต์ = ข้อมูล 1 GB นั่นเป็นข้อมูลจำนวนมากที่จะค้นหาไม่ว่าคุณจะไปทางไหน - แต่ละค่าของ X น่าจะเป็นค่าที่ไม่ซ้ำกันหรือคุณจะมีหลายระเบียนที่มีค่า X เท่ากันหรือไม่

1
นั่นจะเป็นข้อมูลจำนวนมากที่พยายามเก็บไว้ในหน่วยความจำเป็นแคชเพื่อการดึงข้อมูลอย่างรวดเร็ว นั่นจะเท่ากับมากกว่า 1GB ต่อเซสชันผู้ใช้
maple_shaft

ความคิดเห็นก่อนหน้าของฉันถือว่าเป็นเว็บแอปพลิเคชัน นี่เป็นเว็บแอปพลิเคชันหรือไม่
maple_shaft

มันเป็นแอปพลิเคชั่นบนเดสก์ท็อป ค่าในการบันทึกไม่จำเป็นต้องซ้ำกัน นอกจากนี้ฉันกำลังค้นหาซับสตริงไม่ตรงกับที่แน่นอน
Giorgio

@maple_shaft: ฉันจะแคชเฉพาะระเบียนที่ฉันได้เข้าถึงเมื่อเร็ว ๆ นี้ ถ้าฉันเปลี่ยนสตริงแบบสอบถามและระเบียนยังคงตรงกันมันยังคงอยู่ในแคช
Giorgio

คำตอบ:


20

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

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

ตอนนี้ปัญหาของคุณได้รับการกำหนดเป็นต้องค้นหาไฟล์ข้อความที่มีชุดของสตริง มีสองความเป็นไปได้ที่นี่

  1. การจับคู่สตริงย่อยถ้า blobs ข้อความของคุณเป็นเหล็กหรือคำเดียว (ไม่มีช่องว่าง) และคุณต้องค้นหาสตริงย่อยโดยพลการภายใน ในกรณีเช่นนี้คุณต้องแยกวิเคราะห์ไฟล์ทุกไฟล์เพื่อค้นหาไฟล์ที่ดีที่สุดที่เป็นไปได้ หนึ่งใช้อัลกอริทึมเช่นอัลกอริทึม Boyer Moor ดูสิ่งนี้และสิ่งนี้เพื่อดูรายละเอียด สิ่งนี้เทียบเท่ากับ grep - เนื่องจาก grep ใช้สิ่งที่คล้ายกันภายใน แต่คุณอาจทำอย่างน้อย 100 grep (กรณีที่เลวร้ายที่สุด 2 ล้าน) ก่อนกลับมา

  2. ค้นหาดัชนี ที่นี่คุณกำลังสมมติว่าข้อความมีชุดคำและการค้นหา จำกัด เฉพาะความยาวคำที่กำหนด ในกรณีนี้เอกสารจะถูกจัดทำดัชนีตามคำที่เป็นไปได้ทั้งหมด ซึ่งมักเรียกว่า "การค้นหาข้อความแบบเต็ม" มีจำนวนอัลกอริทึมในการทำเช่นนี้และจำนวนโครงการโอเพ่นซอร์สที่สามารถใช้โดยตรง หลายของพวกเขานอกจากนี้ยังสนับสนุนการค้นหาป่าการ์ด ฯลฯ การค้นหาโดยประมาณดังนี้
    Apache Lucene: http://lucene.apache.org/java/docs/index.html
    b. OpenFTS: http://openfts.sourceforge.net/
    ค สฟิงซ์http://sphinxsearch.com/

เป็นไปได้มากหากคุณต้องการ "คำที่คงที่" เป็นคำค้นหาแนวทางที่สองจะรวดเร็วและมีประสิทธิภาพมาก


2
นี่เป็นแนวคิดที่น่าสนใจ แต่ดูเหมือนว่าไม่น่าเป็นไปได้ที่นักพัฒนาซอฟต์แวร์สามารถค้นหาข้อมูลที่เป็นข้อความได้เร็วกว่าและมีประสิทธิภาพมากกว่าเอ็นจิ้นฐานข้อมูล 1GB คนที่ฉลาดกว่าคุณและฉันทำงานหนักกว่าเครื่องมือเพิ่มประสิทธิภาพข้อความค้นหาเพื่อทำสิ่งนั้นและมันก็ไร้เดียงสานิดหน่อยที่จะคิดว่าคุณสามารถทำได้อย่างมีประสิทธิภาพมากขึ้น
maple_shaft

4
@maple_shaft ตัวอย่างที่ฉันให้ไม่ใช่เอ็นจิ้นฐานข้อมูล RDBMS พวกมันเหมือน "เสิร์ชเอ็นจิ้น" มากกว่าถ้าคุณต้องการเรียก มีความแตกต่างทางแนวคิดอย่างมากระหว่างการเลือกรายการออกจากดัชนี (หรือตารางแฮช) เมื่อเทียบกับการค้นหาข้อมูลขนาด 1GB อีกครั้งทุกครั้งที่แบบสอบถามเริ่มทำงาน ดังนั้นสิ่งที่ฉันแนะนำไม่ใช่การบิดเล็กน้อย
Dipan Mehta

ดูเหมือนว่าความคิดที่น่าสนใจ แต่ฉันสงสัยว่ามันจะทำงานอย่างไร ฉันจะมีมากกว่า 2 000 000 ไฟล์แต่ละขนาดประมาณครึ่งกิโลไบต์ หรือคุณแนะนำให้มีมากกว่าหนึ่งระเบียนต่อไฟล์? ฐานข้อมูลต่างกันอย่างไร
Giorgio

ฉันไม่มั่นใจว่าสิ่งนี้จะต้องมีประสิทธิภาพดีกว่าพูดดัชนี fulltext SQL
Kirk Broadhurst

@Giorgio - ใช่ว่าเป็นเครื่องมือค้นหาข้อความแบบเต็มจะทำงาน ความแตกต่างที่สำคัญที่นี่คือเพจที่มีการจัดทำดัชนีไว้ล่วงหน้ากับการค้นหาในหน่วยความจำ (อีกครั้งสำหรับทุกครั้งที่มีการสืบค้น)
Dipan Mehta

21

เทคโนโลยีที่คุณกำลังมองหาคือการจัดทำดัชนีข้อความแบบเต็ม RDBMS ส่วนใหญ่มีความสามารถในตัวที่สามารถใช้งานได้ที่นี่หรือคุณสามารถใช้บางอย่างเช่น Lucene ถ้าคุณต้องการที่จะเล่นและ / หรือใช้มันในหน่วยความจำ


1
ในความคิดของฉันตัวเลือก fulltext ใน RDBMS ใด ๆ เป็นวิธีแก้ปัญหาเพื่อให้ทำสิ่งที่ไม่ได้ออกแบบมาสำหรับ: "ค้นหาในข้อมูลที่ไม่เกี่ยวข้องที่ไม่มีโครงสร้างบางกอง" หากคุณกำลังสร้างเครื่องมือค้นหาคุณเพียงแค่ไม่ใช้ RDBMS มันอาจทำงานได้กับชุดข้อมูลขนาดเล็ก แต่ lakcs ปรับขนาดใด ๆ การค้นหากองข้อมูลที่ไม่มีโครงสร้างไม่ใช่เล็บดังนั้นอย่าใช้ค้อน ใช้เครื่องมือที่เหมาะสมสำหรับงาน
Pieter B

8

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


1
ใช่! ฉันกำลังคิดเกี่ยวกับโครงสร้างของต้นไม้และฉันจำได้ว่ามีบางสิ่งที่คล้ายกันซึ่งอาจเหมาะกับฉัน แต่ฉันจำไม่ได้ว่ามีคู่ชีวิตเพราะฉันไม่เคยใช้มัน เกี่ยวกับข้อกำหนดในการเก็บข้อมูล: โปรดจำไว้ว่าฉันต้องดึงเฉพาะรายการ N แรกเท่านั้น (เช่น N = 100) เนื่องจากไม่มีความเหมาะสมในการเติมตารางที่มีจำนวนการเข้าชม 20,000 ครั้ง ดังนั้นแต่ละโหนดของ trie จะชี้ไปที่รายการ N มากที่สุด นอกจากนี้ฉันลืมที่จะพูดถึงว่าฉันต้องการการเข้าถึงที่รวดเร็ว แต่ฉันไม่ต้องการการอัปเดตที่รวดเร็วเนื่องจากข้อมูลถูกโหลดเพียงครั้งเดียว ความคิดของทั้งคู่เกี่ยวกับดัชนีที่แปรเปลี่ยนสามารถใช้งานได้จริง ๆ !
Giorgio

1
คำตอบที่ดี แต่เป็นคุณทราบเป็น Trie เป็นที่ดีสำหรับการจับคู่เริ่มต้นของคำพูดของคุณ แต่อย่างรวดเร็วจะได้รับความซับซ้อนและมีขนาดใหญ่มากถ้าจับคู่ substring ใด ๆ ...
เคิร์ก Broadhurst

ในการทดลองครั้งแรกฉันพยายามสร้างชุดของสตริงย่อยทั้งหมดที่ปรากฏในสตริงที่ฉันต้องค้นหาซึ่งหากฉันเข้าใจถูกต้องให้สอดคล้องกับเส้นทางของทั้งคู่ ฉันได้รับการยกเว้นหน่วยความจำไม่เพียงพอ (ด้วย 256M ของฮีปสำหรับ JVM) ที่ความยาวย่อย 6 ดังนั้นฉันกลัวว่าวิธีนี้จะไม่สามารถทำได้เว้นแต่ฉันจะทำอะไรผิด
Giorgio

5

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

ทางเลือกหนึ่งคือการเก็บรวบรวมระบุเอกลักษณ์ของระเบียนเหล่านี้ที่คุณอย่างชัดเจนไม่ต้องการที่จะดึงข้อมูลจากแบบสอบถามและรวมถึงพวกเขาอาจจะเป็นในหรือNOT INNOT EXISTS

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

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


maple_shaft และ @Wyatt Barnett: ขอบคุณมากสำหรับคำแนะนำ ฉันจะต้องอ่านและลองวิธีแก้ปัญหาที่แตกต่างกัน ไม่ใช่ฐานข้อมูลทั้งหมดที่รองรับการจัดทำดัชนีแบบเต็ม MySQL (ซึ่งฉันใช้อยู่) กำลังทำอยู่ ( dev.mysql.com/doc/refman/5.5/en/fulltext-search.html ) ฉันจะพยายามทำการทดสอบและรายงานที่นี่
Giorgio

2

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

กล่าวโดยย่อคือคุณสูญเสียการสนับสนุนธุรกรรม DB หากคุณใส่ Lucene ลงในสคีมาข้อมูลของคุณ


1
ปัญหาตามที่ระบุไว้ดูเหมือนจะไม่เหมาะสำหรับ RDMS แต่อย่างใด
Pieter B

1

คุณเคยพิจารณาสฟิงซ์หรือไม่? http://sphinxsearch.comหากคุณสามารถใช้เครื่องมือของบุคคลที่สามสิ่งนี้จะเป็นสิ่งที่ดีที่สุดสำหรับสิ่งที่คุณพยายามจะทำมันมีประสิทธิภาพมากขึ้นในการค้นหาข้อความแบบเต็มกว่า RDBMS ใด ๆ ที่ฉันใช้เป็นการส่วนตัว


3
และโหวตลงสำหรับ?
twigg

1

มันค่อนข้างแปลกที่ไม่มีคำตอบใดที่แสดงคำว่า"ดัชนีกลับหัว"ซึ่งเป็นเทคโนโลยีที่ใช้แก้ปัญหาทั้งหมดที่คล้ายกับ Apache Lucene และอื่น ๆ

ดัชนีฤvertedษีคือการแมปจากคำไปยังเอกสาร ("ดัชนีกลับระดับระเบียน") หรือแม้แต่ตำแหน่งคำที่แม่นยำภายในเอกสาร ("ดัชนีกลับหัวระดับคำ")

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

ดังนั้นคิดเกี่ยวกับดัชนีที่มี tuples (คำ, ไฟล์, สถานที่) เมื่อคุณมีเช่น ("inverted", "foo.txt", 123) คุณเพียงแค่ตรวจสอบว่า ("index", "foo.txt", 124) เป็นส่วนหนึ่งของดัชนีเพื่อค้นหาวลีแบบเต็ม "inverted index" .

แม้ว่าฉันจะไม่แนะนำให้คุณปรับใช้เสิร์ชเอ็นจิ้นแบบเต็มข้อความตั้งแต่ต้น แต่ก็มีประโยชน์ที่จะทราบว่าเทคโนโลยีเช่น Apache Lucene ทำงานอย่างไร

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

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