zoukankan      html  css  js  c++  java
  • 使用Lucene.net提升网站搜索速度整合记录

    使用Lucene.net提升网站搜索速度整合记录

     

    1.随着网站数据量达到500万条的时候,发现SQL数据库如果使用LIKE语句来查询,总是占用CPU很忙,不管怎么优化,速度还是上不来;

    2.经过网上收集资料,HUBBLE.net目前虽然做得不错,但需要配置内存给他,由于服务器4G内存,而且运行了好几个网站,所以考虑采用Lucene.net来做为搜索引擎;

    3.虽然本地测试没有问题,但是部署到64位的服务器上还是经过了好几天的折腾,在此都记录一下.

    在此记录搜片神器的整个开发过程中遇到的问题和相关的解决方案,希望大家一起交流. 

    Lucene软件下载编译整合的问题                                          

    网站采用了最新Lucene.Net.3.0.3版本的代码,然后配置盘古分词来进行.

    由于Lucene.Net升级到了3.0.3后接口发生了很大变化,原来好多分词库都不能用了,

    .Net下还有一个盘古分词(http://pangusegment.codeplex.com/),但也不支持Lucene.Net 3.0.3,园子里的大哥们修改的项目地址:

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

    下载后里面有DEMO代码进行整合.

    Lucene在软件中集成处理                                          

    由于实时处理数据程序是WINFORM程序,所以需要采用软件代码来实时更新索引数据,这样可控性高一些.

    引用头文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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 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的网站全部挂掉,运行不起来,http://h31bt.com网站也运行不起来,

    Enable32BitAppOnWin64的意思是允许运行32们的程序,所以此方法不行.

    2.另外找的文章都是重新注册net 4.0   

    C:WINDOWSMicrosoft.NETFrameworkv4.0.30319aspnet_regiis.exe -i

    开始执行试了好多次,没有效果,这里也允许了,重新安装了好几次Net4.0.

    3.最后一次在停止IIS后,再次全部注册net2.0,4.0,然后

    cscript %SYSTEMDRIVE%inetpubadminscriptsadsutil.vbs SET W3SVC/AppPools/Enable32bitAppOnWin64 1
    重启IIS后,出现的错误终于不再是上面的.

    新错误是:

    Server Application Unavailable

    4.通过网上查找资料 

    解决办法: 在IIS中新建一个应用程序池,然后选中你的 基于.net 
    framework4.0的虚拟目录,点“属性”-》在“应用程序池” 中选择刚才新建的的应用程序池,点击“确定”。

     最后服务器网站http://h31bt.com终于运行起来了.

      Lucene.net搜索的效果                                          

    1.经过目前测试,目前服务器4G的内存搜索速度比以前需要5S左右的LIKE强了很多倍,基本上都在1S以内;

    2.由于Lucene索引是基于文件的索引,从而使SQL数据库的使用压力减轻不少,这样给其它程序的整体压力减少不少,网站效果还行.

    3.目前500万的数据重新建立一次索引需要1小时左右,但是网站运行的时候去更新索引速度目前感觉比较慢.比如要更新点击次数和更新时间等时,发现新的问题来了,这块的时间比较长.

    4.目前考虑的方案是几天一次全部重新建立索引一次,平时只是添加数据.

    希望有了解的朋友在此留言指教下lucene.net方面的性能优化问题,大家一起共同学习进步.

    大家看累了,就移步到娱乐区http://h31bt.com 去看看速度如何,休息下...

    希望大家多多推荐哦...大家的推荐才是下一篇介绍的动力...

     
     
    分类: C#数据库
    标签: Lucene.net
  • 相关阅读:
    在安卓上用Termux安装sqlmap
    地址转换函数
    字节操作函数
    主机字节序和网络字节序
    20191231 Spring官方文档(Core 1.13-1.14)
    20191230 Spring官方文档(Core 1.12)
    20191230 Tomcat权威指南-读书摘要系列【归档】
    20191230 On Java8【归档】
    20191227 Java8 日期时间API
    20191226 Spring官方文档(Core 1.11)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3388703.html
Copyright © 2011-2022 走看看