mysql 单表2000多万条数据,模糊查询如何秒回

        mysql 单表数据2000多万,没有分区、没有表、没有分库,表存储的是企业信息,例如:企业名称、经营范围、注册地址等信息。现在需要通过关键词模糊匹配企业名称,搜索出匹配的数据。因为模糊匹配,所以直接在企业名称字段上建索引是没有效果的。如果直接用sql语句写,例如:select * from tab1 where name like ‘%搜搜一下%’,那简直是噩梦般,绝对是超时,并且mysql会卡死。

        研究了一下各种索引组件,最后决定采用lucene.net,版本:3.0.3.0

如何安装lucene.net,其实很简单,就是直接通过vs2015的项目-》管理NuGet程序包..,然后搜索:lucene.net,最后点击下载安装就可以了,如下图:

安装完Lucene.net后,就需要建索引,2000多万条数据,如果是单线程创建索引,那大概需要48小时左右。 如果是多线程,那就看线程的数量,还有服务器的性能了。2000多万条数据创建完索引后,占了20G的空间左右。

创建索引的代码如下:

1、先打开索引目录

 //创建索引目录
            if (!System.IO.Directory.Exists(indexDir))
            {
                System.IO.Directory.CreateDirectory(indexDir);
            }
            FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexDir),
                new NativeFSLockFactory());
            //IndexReader:对索引库进行读取的类
            bool isExist = IndexReader.IndexExists(directory);
            //是否存在索引库文件夹以及索引库特征文件
            if (isExist)
            {
                //如果索引目录被锁定(比如索引过程中程序异常退出或另一进程在操作索引库),则解锁
                //Q:存在问题 如果一个用户正在对索引库写操作 此时是上锁的 而另一个用户过来操作时 将锁解开了 于是产生冲突 --解决方法后续
                if (IndexWriter.IsLocked(directory))
                {
                    IndexWriter.Unlock(directory);
                }
            }
            //IndexWriter第三个参数:true指重新创建索引,false指从当前索引追加....此处为新建索引所以为true
            writer = new IndexWriter(directory, new PanGuAnalyzer(), true,
                Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);

 2、写入索引

   Document doc = new Document();
                doc.Add(new Field("id", id, Field.Store.YES, Field.Index.NOT_ANALYZED));//进行索引,但是不进行分词,如果身份证号、姓名、ID等,适用于精确搜索
                doc.Add(new Field("name", qymc, Field.Store.YES, Field.Index.ANALYZED));//存储且索引,适用模糊搜索
                doc.Add(new Field("qymc2", qymc, Field.Store.YES, Field.Index.NOT_ANALYZED));//存储且索引,但是不进行分词,适用于精确搜索
                doc.Add(new Field("fddbr", fddbr, Field.Store.YES, Field.Index.NO));//不进行索引
                writer.AddDocument(doc);

特殊说明一下:

//Index.ANALYZED:进行分词和索引,适用于标题、内容等
                //Index.NOT_ANALYZED:进行索引,但是不进行分词,如果身份证号、姓名、ID等,适用于精确搜索
                //Index.ANALYZED_NOT_NORMS:进行分词但是不存储norms信息,这个norms中包括了创建索引的时间和权值等信息
                //Index.NOT_ANALYZED_NOT_NORMS:即不进行分词也不存储norms信息
                //Index.NO:不进行索引

创建完索引后,接下来就是查询索引,查询索引时,不能频繁打开索引,我是将IndexSearcher写成单例模式,只在最开始时打开一次。

查询索引的代码如下:

    
                IndexSearcher search = luceneSingle.IndexSearcher;

                //Occur.SHOULD表示条件关系为“or”,
                //Occur.MUST表示“and”,
                //Occur.MUST_NOT表示“not”
                BooleanQuery bq = new BooleanQuery();


                //省份
                Query pnameQuery = null;
                if (!string.IsNullOrEmpty(name))
                {
                    //pname不进行分词,直接模糊匹配
                    pnameQuery = new PrefixQuery(new Term("name", name));
                    bq.Add(pnameQuery, Occur.MUST);
                }


                TopScoreDocCollector collector = TopScoreDocCollector.Create(PageIndex * PageSize, true);
                search.Search(bq, null, collector);


                //起始数
                int start = PageSize * (PageIndex - 1);
                //结束数
                int limit = PageSize;

   //分页查询
                var hits = collector.TopDocs(start, limit).ScoreDocs;
                //总条数
                int numTotalHits = collector.TotalHits;

                Maigou.Common.Log.OperateLog.WriteLog("numTotalHits:" + numTotalHits, "lucene_");

                //总页数
                var pageCount = 0;
                //总条数
                var totalSize = numTotalHits;
                if (totalSize != 0)
                {
                    if ((totalSize % 10) != 0)
                        pageCount = (totalSize / 10) + 1;
                    else
                        pageCount = (totalSize / 10);
                }

for (int i = 0; i < hits.Length; i++)
                {
                    var hit = hits[i];
                    Document doc = search.Doc(hit.Doc);

                    #region 数据
                    var id = doc.Get("id").ToString(); 
                    var model = new qyModel()
                    {
                        //id = Zhangwoo.Core.DESEncrypt.Encrypt(id, "87654321"),
                        id= id,
                        qymc = doc.Get("qymc").ToString(),
                        fddbr = fddbr,
                        tel = telStr,
                        addr = doc.Get("addr").ToString(),
                        email = doc.Get("email").ToString(),
                        zczb = doc.Get("zczb").ToString(),
                        jyfw = doc.Get("jyfw").ToString(),
                        pageCount = pageCount
                    };
                    list.Add(model);
                    #endregion
                }

如果要体验查询速度,可以在:搜搜一下,免费企业信息查询(www.sousouyixia.com)试一下查询速度。

这里分词是采用盘古分词的,听说盘古分词已经停止更新了。  我实际使用,也发现盘古分词不是很全,有些关键词没办法搜搜到。  如何有其他更好的分词算法,也可以帮推荐一下。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值