使用Lucene.net提高網站搜索速度整合記錄

1.隨着網站數據量達到500萬條的時候,發現SQL數據庫若是使用LIKE語句來查詢,老是佔用CPU很忙,無論怎麼優化,速度仍是上不來;git

2.通過網上收集資料,HUBBLE.net目前雖然作得不錯,但須要配置內存給他,因爲服務器4G內存,並且運行了好幾個網站,因此考慮採用Lucene.net來作爲搜索引擎;github

3.雖然本地測試沒有問題,可是部署到64位的服務器上仍是通過了好幾天的折騰,在此都記錄一下.數據庫

在此記錄搜片神器的整個開發過程當中遇到的問題和相關的解決方案,但願你們一塊兒交流. 性能優化

Lucene軟件下載編譯整合的問題                                          

網站採用了最新Lucene.Net.3.0.3版本的代碼,而後配置盤古分詞來進行.服務器

因爲Lucene.Net升級到了3.0.3後接口發生了很大變化,原來好多分詞庫都不能用了,框架

.Net下還有一個盤古分詞(http://pangusegment.codeplex.com/),但也不支持Lucene.Net 3.0.3,園子裏的大哥們修改的項目地址:ide

https://github.com/JimLiu/Lucene.Net.Analysis.PanGu性能

下載后里面有DEMO代碼進行整合.學習

 

Lucene在軟件中集成處理                                          

因爲實時處理數據程序是WINFORM程序,因此須要採用軟件代碼來實時更新索引數據,這樣可控性高一些.測試

引用頭文件

using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Lucene.Net.Util;
using PanGu;
using Lucene.Net.Analysis.PanGu;
using PanGu.HighLight;
using FSDirectory = Lucene.Net.Store.FSDirectory;
using Version = Lucene.Net.Util.Version;

  

建立索引

 

    public static class H31Index
    {
        private static Analyzer analyzer = new PanGuAnalyzer(); //MMSegAnalyzer //StandardAnalyzer
        private static int ONEPAGENUM = 200;
        private static string m_indexPath = "";
        private static IndexWriter iw = null;
        public static void Init(string indexpath)
        {
            m_indexPath = indexpath;
        }

        public static bool OpenIndex()
        {
            try
            {

                DirectoryInfo INDEX_DIR = new DirectoryInfo(m_indexPath+"//Index");
                bool iscreate = true;
                if (INDEX_DIR.Exists)
                    iscreate = false;
                Int32 time21 = System.Environment.TickCount;
                if (iw == null)
                {
                    iw = new IndexWriter(FSDirectory.Open(INDEX_DIR), analyzer, iscreate, new IndexWriter.MaxFieldLength(32));
                    Int32 time22 = System.Environment.TickCount;
                    H31Debug.PrintLn("IndexWriter2[" + type.ToString() + "]:" + " IndexWriter:" + (time22 - time21).ToString() + "ms");
                    return true;
                }
            }
            catch (System.Exception ex)
            {
                H31Debug.PrintLn("OpenIndex" + ex.Message);
            }
            return false;
        }

        public static void CloseIndex()
        {
            try
            {
                if (iw != null)
                {
                    //if (count > 0)
                    {
                        iw.Commit();
                        iw.Optimize();
                    }
                    iw.Dispose();
                    iw = null;
                }            
            }
            catch (System.Exception ex)
            {
                H31Debug.PrintLn("CloseIndex" + ex.Message);
            }

        }

        //創建索引        
public static int AddIndexFromDB()
        {
            int res = 0;
            int count = 0;
            try
            {
                Int32 time0 = System.Environment.TickCount;
                while (count < OneTimeMax && iw != null)
                {
                    Int32 time11 = System.Environment.TickCount;
                    DataSet ds = H31SQL.GetHashListFromDB(type, startnum, startnum + ONEPAGENUM - 1, NewOrUpdate);
                    Int32 time12 = System.Environment.TickCount;
                    int cnt = ds.Tables[0].Rows.Count;
                    if (ds != null&& cnt>0)
                    {
                        Int32 time1 = System.Environment.TickCount;
                        count = count + cnt;
                        for (int i = 0; i < cnt; i++)
                        {
                            //ID,hashKey,recvTime,updateTime,keyContent,keyType,recvTimes,fileCnt,filetotalSize,Detail,viewTimes,viewLevel
                            Document doc = new Document();
                            doc.Add(new Field("ID", ds.Tables[0].Rows[i]["ID"].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));//存儲,索引
                            doc.Add(new Field("hashKey", ds.Tables[0].Rows[i]["hashKey"].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));//存儲,索引
                            doc.Add(new Field("recvTime", ds.Tables[0].Rows[i]["recvTime"].ToString(), Field.Store.YES, Field.Index.NO));//存儲,不索引
                            doc.Add(new Field("updateTime", ds.Tables[0].Rows[i]["updateTime"].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));//存儲,索引
                            doc.Add(new Field("keyContent", ds.Tables[0].Rows[i]["keyContent"].ToString(), Field.Store.YES, Field.Index.ANALYZED));//存儲,索引
                            //PanGuFenCiTest(ds.Tables[0].Rows[i]["keyContent"].ToString());
                            string typeid=ds.Tables[0].Rows[i]["keyType"].ToString();
                            if(typeid.Length<2)
                                typeid=type.ToString();
                            doc.Add(new Field("keyType", typeid, Field.Store.YES, Field.Index.NO));//存儲,不索引
                            doc.Add(new Field("recvTimes", ds.Tables[0].Rows[i]["recvTimes"].ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));//存儲,索引
                            doc.Add(new Field("fileCnt", ds.Tables[0].Rows[i]["fileCnt"].ToString(), Field.Store.YES, Field.Index.NO));//存儲,不索引
                            doc.Add(new Field("filetotalSize", ds.Tables[0].Rows[i]["filetotalSize"].ToString(), Field.Store.YES, Field.Index.NO));//存儲,不索引
                            doc.Add(new Field("Detail", ds.Tables[0].Rows[i]["Detail"].ToString(), Field.Store.YES, Field.Index.NO));//存儲,不索引
                            doc.Add(new Field("viewTimes", ds.Tables[0].Rows[i]["viewTimes"].ToString(), Field.Store.YES, Field.Index.NO));//存儲,不索引
                            doc.Add(new Field("viewLevel", ds.Tables[0].Rows[i]["viewLevel"].ToString(), Field.Store.YES, Field.Index.NO));//存儲,不索引
                            iw.AddDocument(doc);
                        }
                        ds = null;
                        Thread.Sleep(10);
                    }
                    else
                    {
                        res = 1;
                        break;
                    }
                }

                Int32 time10 = System.Environment.TickCount;
                H31Debug.PrintLn("AddIndexFromDB[" + type.ToString() + "],Building index done:" + startnum.ToString() + " Time:" + (time10 - time0).ToString() + "ms");

            }
            catch (System.Exception ex)
            {
                H31Debug.PrintLn(ex.StackTrace);
            }

            return res; 
        }
建立索引代碼

  

查詢代碼

        //網站搜索代碼
        public static void Search(string keyword,int typeid,int pageNo)
        {
            int onePage=20;//一頁多少
            int TotalNum=1000;//一次加載多少

            if (pageNo < 0) pageNo = 0;
            if (pageNo * onePage > TotalNum)
                pageNo = TotalNum / onePage;

            //索引加載的目錄
            DirectoryInfo INDEX_DIR = new DirectoryInfo(m_indexPath+"//Index//index1");
            IndexSearcher searcher = new IndexSearcher(FSDirectory.Open(INDEX_DIR), true);
            QueryParser qp = new QueryParser(Version.LUCENE_30, "keyContent", analyzer);
            Query query = qp.Parse(keyword); 
            //Console.WriteLine("query> {0}", query);

            //設置排序問題
            Sort sort = new Sort(new SortField[]{new SortField("recvTimes", SortField.INT, true),new SortField("updateTime", SortField.STRING, true)});

            //設置高亮顯示的問題
            PanGu.HighLight.SimpleHTMLFormatter simpleHTMLFormatter = new PanGu.HighLight.SimpleHTMLFormatter("<font color=\"red\">", "</font>");
            PanGu.HighLight.Highlighter highlighter =new PanGu.HighLight.Highlighter(simpleHTMLFormatter,new Segment());
            highlighter.FragmentSize = 50;

            TopFieldDocs tds = searcher.Search(query,null, 1000, sort);
            Console.WriteLine("TotalHits: " + tds.TotalHits);

            /* 計算顯示的條目 */
            int count = tds.ScoreDocs.Length;
            int start = (pageNo - 1) * onePage;
            int end = pageNo * onePage > count ? count : pageNo * onePage;
            //返回集合列表
            for (int i = start; i < end; i++)
            {
                Document doc = searcher.Doc(tds.ScoreDocs[i].Doc);
                string contentResult = highlighter.GetBestFragment(keyword, doc.Get("keyContent").ToString());
                Console.WriteLine(contentResult + ">>" + doc.Get("recvTimes") + "<<" + doc.Get("updateTime"));
            }

            searcher.Dispose();
        }

 

 服務器部署的問題                                          

 當你以爲本地都運行的好好的時候,發現到服務器上根本就運行不了,一直報錯.

因爲Lucene.net最新版本直接使用了net4.0,服務器是64們的WIN2003,並且運行的網站都仍是32位的net2.0的DLL,因此升級到4.0怎麼也出不來

1.運行顯示的錯誤是提示沒有.net4.0的框架,須要註冊.net4.0

直接到網上找如何搞定顯示ASP.NET選項卡的問題,最後找到文章方法是:

中止iis直接刪除C:/WINDOWS/system32/inetsrv/MetaBase.xml中的Enable32BitAppOnWin64="TRUE" 行

重啓IIS後選項卡到是出來了,但net.2.0的網站所有掛掉,運行不起來,sosobta.com網站也運行不起來,

Enable32BitAppOnWin64的意思是容許運行32們的程序,因此此方法不行.

 

2.另外找的文章都是從新註冊net 4.0   

C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -i

開始執行試了好屢次,沒有效果,這裏也容許了,從新安裝了好幾回Net4.0.

 

3.最後一次在中止IIS後,再次所有註冊net2.0,4.0,而後

cscript %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs SET W3SVC/AppPools/Enable32bitAppOnWin64 1
重啓IIS後,出現的錯誤終於再也不是上面的.

新錯誤是:

Server Application Unavailable

4.經過網上查找資料 

解決辦法: 在IIS中新建一個應用程序池,而後選中你的 基於.net
framework4.0的虛擬目錄,點「屬性」-》在「應用程序池」 中選擇剛纔新建的的應用程序池,點擊「肯定」。

 最後服務器網站sosobt.com終於運行起來了.

  Lucene.net搜索的效果                                          

1.通過目前測試,目前服務器4G的內存搜索速度比之前須要5S左右的LIKE強了不少倍,基本上都在1S之內;

2.因爲Lucene索引是基於文件的索引,從而使SQL數據庫的使用壓力減輕很多,這樣給其它程序的總體壓力減小很多,網站效果還行.

3.目前500萬的數據從新創建一次索引須要1小時左右,可是網站運行的時候去更新索引速度目前感受比較慢.好比要更新點擊次數和更新時間等時,發現新的問題來了,這塊的時間比較長.

4.目前考慮的方案是幾天一次所有從新創建索引一次,平時只是添加數據.

但願有了解的朋友在此留言指教下lucene.net方面的性能優化問題,你們一塊兒共同窗習進步.

 

你們看累了,就移步到娛樂區http://www.sosobta.com 去看看速度如何,休息下...

但願你們多多推薦哦...你們的推薦纔是下一篇介紹的動力...

相關文章
相關標籤/搜索