2015-08-28 8 views
8

हमारे पास एक प्रोग्राम है, जो लगातार चलता है, विभिन्न चीजें करता है, और हमारे डेटाबेस में कुछ रिकॉर्ड बदलता है। उन रिकॉर्डों को लुसीन का उपयोग करके अनुक्रमित किया जाता है। Lucene में indexWriter.deleteDocuments(..) तो indexWriter.addDocument(..) का उपयोग करकेलुसीन इंडेक्स को लिखना, एक समय में एक दस्तावेज़, समय के साथ धीमा हो जाता है

  1. खुला डाटाबेस लेनदेन, खुले Lucene IndexWriter
  2. लेनदेन में db में परिवर्तन करें, और अद्यतन उस संस्था: तो हर बार जब हम एक इकाई को बदलने हम कुछ पसंद है।
  3. यदि सब ठीक हो गए, तो डीबी लेनदेन करें और इंडेक्सवाइटर को प्रतिबद्ध करें।

यह ठीक काम करता है, लेकिन समय के साथ, indexWriter.commit() अधिक से अधिक समय लेता है। प्रारंभ में इसमें लगभग 0.5 सेकंड लगते हैं लेकिन कुछ सौ ऐसे लेन-देन के बाद इसमें 3 सेकंड से अधिक समय लगता है। मुझे संदेह नहीं है कि स्क्रिप्ट लंबे समय तक चलने में अधिक समय लगेगा।

मेरे समाधान/IndexWriter (250k के बारे में के बारे में में दस्तावेजों अब तक पहले indexWriter.deleteAll() का उपयोग कर तो सभी दस्तावेजों को फिर से जोड़कर indexWriter.addDocument(..) और indexWriter.commit(), बाहर टिप्पणी करने के लिए और पुन: पूरे सूचकांक हर अब और फिर, एक Lucene transction भीतर कर दिया गया है 14 सेकंड)। लेकिन यह स्पष्ट रूप से डेटाबेस और लुसेन द्वारा प्रदान किए गए लेनदेन संबंधी दृष्टिकोण के खिलाफ चला जाता है, जो दोनों को सिंक में रखता है, और अपडेट को हमारे टूल्स के उपयोगकर्ताओं को दिखाई देता है जो ल्यूसीन का उपयोग कर खोज रहे हैं।

यह अजीब लगता है कि मैं 14 सेकंड में 250k दस्तावेज़ जोड़ सकता हूं, लेकिन 1 दस्तावेज़ जोड़ना 3 सेकंड लेता है। मैं गलत क्या कर रहा हूं, मैं स्थिति को कैसे सुधार सकता हूं?

+1

क्या आप इसे बैकग्राउंड कार्यों के साथ ठीक कर सकते हैं? आपके पास शायद 10 सेकंड्स जुर्माना होगा, लेकिन यह कई अनुप्रयोगों के लिए ठीक हो सकता है – AdamSkywalker

+0

@AdamSkywalker - लेकिन यह धीमा और धीमा हो जाता है, जब 1hr, या 10hrs, या 2 दिन लगते हैं तो क्या होता है? –

उत्तर

11

आप क्या गलत कर रहे हैं यह मानते हुए कि लुसेन के built-in transactional capabilities में एक विशिष्ट संबंधपरक डेटाबेस की तुलना में प्रदर्शन और गारंटी है, जब they really don't। अधिक विशेष रूप से आपके मामले में, एक प्रतिबद्धता डिस्क के साथ सभी इंडेक्स फ़ाइलों को सिंक करता है, जिससे सूचकांक आकार के अनुपात में आनुपातिक समय होता है। यही कारण है कि समय के साथ आपके indexWriter.commit() अधिक से अधिक समय लगता है। ,

यह एक महंगा ऑपरेशन हो सकता है तो आप अपने आवेदन में लागत का परीक्षण और यह कर केवल जब वास्तव में जरूरी चाहिए: IndexWriter.commit() के लिए Javadoc भी चेतावनी दी है कि।

क्या आप डेटाबेस दस्तावेज की कल्पना कर सकते हैं जो आपको काम करने से बचने के लिए कह रहा है?

आपका मुख्य लक्ष्य के बाद से एक समय पर ढंग डेटाबेस दिखाई अद्यतन करता है रखने के लिए Lucene खोजों के माध्यम से, स्थिति में सुधार करने लगती है, निम्न कार्य करें:

  1. indexWriter.deleteDocuments(..) है और indexWriter.addDocument(..) ट्रिगर के बाद एक सफल डेटाबेस के लिए प्रतिबद्ध, के पहले
  2. प्रदर्शन करना indexWriter.commit() हर लेन-देन के समय-समय पर बजाय, बस आपके बदलावों के अंत में डिस्क
  3. खोज के लिए उपयोग एक SearcherManager लिए लिखा जाता है बनाने के लिए और करने के लिए समय-समय पर maybeRefresh() आह्वान करने के लिए बजाय एक उचित समय सीमा

निम्नलिखित उदाहरण प्रोग्राम है जो दर्शाता है कि समय-समय पर maybeRefresh() प्रदर्शन करके दस्तावेज़ अपडेट कैसे पुनर्प्राप्त किए जा सकते हैं। यह 100000 दस्तावेजों का सूचकांक बनाता है, का उपयोग commit() और maybeRefresh() के आवधिक आमंत्रण को सेट करने के लिए करता है, आपको एक दस्तावेज़ को अपडेट करने के लिए संकेत देता है, फिर अपडेट दिखाई देने तक बार-बार खोज करता है। कार्यक्रम समाप्त होने पर सभी संसाधनों को ठीक से साफ किया जाता है। ध्यान दें कि अद्यतन दिखाई देने पर नियंत्रण कारक तब होता है जब maybeRefresh() लागू होता है, commit() नहीं।

import java.io.IOException; 
import java.nio.file.Paths; 
import java.util.Scanner; 
import java.util.concurrent.*; 
import org.apache.lucene.analysis.standard.StandardAnalyzer; 
import org.apache.lucene.document.*; 
import org.apache.lucene.index.*; 
import org.apache.lucene.search.*; 
import org.apache.lucene.store.FSDirectory; 

public class LucenePeriodicCommitRefreshExample { 
    ScheduledExecutorService scheduledExecutor; 
    MyIndexer indexer; 
    MySearcher searcher; 

    void init() throws IOException { 
     scheduledExecutor = Executors.newScheduledThreadPool(3); 
     indexer = new MyIndexer(); 
     indexer.init(); 
     searcher = new MySearcher(indexer.indexWriter); 
     searcher.init(); 
    } 

    void destroy() throws IOException { 
     searcher.destroy(); 
     indexer.destroy(); 
     scheduledExecutor.shutdown(); 
    } 

    class MyIndexer { 
     IndexWriter indexWriter; 
     Future commitFuture; 

     void init() throws IOException { 
      indexWriter = new IndexWriter(FSDirectory.open(Paths.get("C:\\Temp\\lucene-example")), new IndexWriterConfig(new StandardAnalyzer())); 
      indexWriter.deleteAll(); 
      for (int i = 1; i <= 100000; i++) { 
       add(String.valueOf(i), "whatever " + i); 
      } 
      indexWriter.commit(); 
      commitFuture = scheduledExecutor.scheduleWithFixedDelay(() -> { 
       try { 
        indexWriter.commit(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      }, 5, 5, TimeUnit.MINUTES); 
     } 

     void add(String id, String text) throws IOException { 
      Document doc = new Document(); 
      doc.add(new StringField("id", id, Field.Store.YES)); 
      doc.add(new StringField("text", text, Field.Store.YES)); 
      indexWriter.addDocument(doc); 
     } 

     void update(String id, String text) throws IOException { 
      indexWriter.deleteDocuments(new Term("id", id)); 
      add(id, text); 
     } 

     void destroy() throws IOException { 
      commitFuture.cancel(false); 
      indexWriter.close(); 
     } 
    } 

    class MySearcher { 
     IndexWriter indexWriter; 
     SearcherManager searcherManager; 
     Future maybeRefreshFuture; 

     public MySearcher(IndexWriter indexWriter) { 
      this.indexWriter = indexWriter; 
     } 

     void init() throws IOException { 
      searcherManager = new SearcherManager(indexWriter, true, null); 
      maybeRefreshFuture = scheduledExecutor.scheduleWithFixedDelay(() -> { 
       try { 
        searcherManager.maybeRefresh(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      }, 0, 5, TimeUnit.SECONDS); 
     } 

     String findText(String id) throws IOException { 
      IndexSearcher searcher = null; 
      try { 
       searcher = searcherManager.acquire(); 
       TopDocs topDocs = searcher.search(new TermQuery(new Term("id", id)), 1); 
       return searcher.doc(topDocs.scoreDocs[0].doc).getField("text").stringValue(); 
      } finally { 
       if (searcher != null) { 
        searcherManager.release(searcher); 
       } 
      } 
     } 

     void destroy() throws IOException { 
      maybeRefreshFuture.cancel(false); 
      searcherManager.close(); 
     } 
    } 

    public static void main(String[] args) throws IOException { 
     LucenePeriodicCommitRefreshExample example = new LucenePeriodicCommitRefreshExample(); 
     example.init(); 
     Runtime.getRuntime().addShutdownHook(new Thread() { 
      @Override 
      public void run() { 
       try { 
        example.destroy(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 

     try (Scanner scanner = new Scanner(System.in)) { 
      System.out.print("Enter a document id to update (from 1 to 100000): "); 
      String id = scanner.nextLine(); 
      System.out.print("Enter what you want the document text to be: "); 
      String text = scanner.nextLine(); 
      example.indexer.update(id, text); 
      long startTime = System.nanoTime(); 
      String foundText; 
      do { 
       foundText = example.searcher.findText(id); 
      } while (!text.equals(foundText)); 
      long elapsedTimeMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime); 
      System.out.format("it took %d milliseconds for the searcher to see that document %s is now '%s'\n", elapsedTimeMillis, id, text); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } finally { 
      System.exit(0); 
     } 
    } 
} 

यह उदाहरण सफलतापूर्वक लुसीन 5.3.1 और जेडीके 1.8.0_66 का उपयोग करके परीक्षण किया गया था।

3

मेरा पहला दृष्टिकोण: अक्सर ऐसा न करें। जब आप दस्तावेज़ को हटाते और पुनः जोड़ते हैं तो आप शायद विलय को ट्रिगर करेंगे। विलय कुछ हद तक धीमा है।

यदि आप एक वास्तविक रीयल-टाइम इंडेक्स रीडर का उपयोग करते हैं तो आप अभी भी खोज सकते हैं जैसे कि यह उपयोग किया जाता है (यह हटाए गए दस्तावेज़ नहीं दिखाता है), लेकिन आपको प्रतिबद्ध जुर्माना नहीं मिलता है। यह सुनिश्चित करने के लिए कि फ़ाइल सिस्टम आपकी अनुक्रमणिका के साथ सिंक हो रहा है, आप हमेशा बाद में प्रतिबद्ध कर सकते हैं। आप अपनी अनुक्रमणिका का उपयोग करते समय ऐसा कर सकते हैं, इसलिए आपको अन्य सभी परिचालनों को अवरुद्ध करने की आवश्यकता नहीं है।

यह दिलचस्प blog post देखें (और अन्य पोस्ट भी पढ़ें, वे बड़ी जानकारी प्रदान करते हैं)।

+0

मैं समझ सकता हूं कि विलय को ट्रिगर करना धीमा हो सकता है, लेकिन क्या आप उम्मीद करेंगे कि प्रतिबद्धता समय के साथ धीमी हो जाएगी? (यदि मैं कम बार-बार प्रतिबद्ध करता हूं, तो वह केवल उस समय को छोड़ देगा जिस पर प्रतिबद्धता अस्वीकार्य रूप से धीमी हो जाती है (1 मिनट? 10 मिनट?), और चूंकि यह स्क्रिप्ट हमेशा के लिए चलता है, यह अंततः उस बिंदु को हिट करेगा।) –

+1

मैंने काम किया है लगभग 10 एम दस्तावेजों के सूचकांक आकार के साथ। मेरे लैपटॉप पर, 'प्रतिबद्ध() '10 सेकंड तक लग सकता है। लेकिन इससे कोई फर्क नहीं पड़ता कि आप 'एनटीआर' समाधान का उपयोग करते हैं, क्योंकि आपको ** प्रतिबद्ध करने के लिए ** प्रतिबद्ध() 'के लिए ** प्रतीक्षा नहीं करना है। – RobAu

संबंधित मुद्दे