【solr全文检索】Solr全文搜索与MySQL查询性能比较

时间:2019-12-10  来源:全文检索  阅读:

测试数据量:10407608
Num Docs: 10407608

在项目中一个最常用的查询,查询某段时间内的数据,SQL查询获取数据,30s左右

SELECT * FROM `tf_hotspotdata_copy_test` WHERE collectTime BETWEEN '2014-12-06 00:00:00' AND '2014-12-10 21:31:55';

对collectTime建立索引后,同样的查询,2s,快了很多。

Solr索引



Solr查询,同样的条件,72ms

"status": 0,

    "QTime": 72,

    "params": {

      "indent": "true",

      "q": "CollectTime:[2014-12-06T00:00:00.000Z TO 2014-12-10T21:31:55.000Z]",

      "_": "1434617215202",

      "wt": "json"

    }

好吧,查询性能提高的不是一点点,用Solrj代码试试:


SolrQuery params = new SolrQuery();
params.set("q", timeQueryString);
params.set("fq", queryString);
params.set("start", 0); 
params.set("rows", Integer.MAX_VALUE);
params.setFields(retKeys);
QueryResponse response = server.query(params);



Solrj查询并获取结果集,结果集大小为220296,返回5个字段,时间为12s左右。

为什么需要这么长时间?上面的"QTime"只是根据索引查询的时间,如果要从solr服务端获取查询到的结果集,solr需要读取stored的字段(磁盘IO),再经过Http传输到本地(网络IO),这两者比较耗时,特别是磁盘IO。

时间对比:

查询条件  时间
MySQL(无索引) 30s
MySQL(有索引) 2s
Solrj(select查询) 12s

如何优化?看看只获取ID需要的时间:

SQL查询只返回id,没有对collectTime建索引,10s左右

SELECT id FROM `tf_hotspotdata_copy_test` WHERE collectTime BETWEEN '2014-12-06 00:00:00' AND '2014-12-10 21:31:55';

SQL查询只返回id,同样的查询条件,对collectTime建索引,0.337s,很快。

Solrj查询只返回id,7s左右,快了一点。

    id Size: 220296

    Time: 7340

时间对比:

查询条件(只获取ID) 时间
MySQL(无索引) 10s
MySQL(有索引) 0.337s
Solrj(select查询) 7s

继续优化。。

关于Solrj获取大量结果集速度慢的一些类似问题:

http://stackoverflow.com/questions/28181821/solr-performance#

http://grokbase.com/t/lucene/solr-user/11aysnde25/query-time-help

http://lucene.472066.n3.nabble.com/Solrj-performance-bottleneck-td2682797.html

这个问题没有好的解决方式,基本的建议都是做分页,但是我们需要拿到大量数据做一些比对分析,做分页没有意义。

偶然看到一个回答,solr默认的查询使用的是"/select" request handler,可以用"/export" request handler来export结果集,看看solr对它的说明:

It's possible to export fully sorted result sets using a special rank query parser and response writer  specifically designed to work together to handle scenarios that involve sorting and exporting millions of records. This uses a stream sorting techniquethat begins to send records within milliseconds and continues to stream results until the entire result set has been sorted and exported.

Solr中已经定义了这个requestHandler:


      {!xport}    xsort    false        query




使用/export需要字段使用docValues建立索引:



使用docValues必须要有一个用来Sort的字段,且只支持下列类型:

Sort fields must be one of the following types: int,float,long,double,string

docValues支持的返回字段:

Export fields must either be one of the following types: int,float,long,double,string

使用Solrj来查询并获取数据:


        SolrQuery params = new SolrQuery();
        params.set("q", timeQueryString);
        params.set("fq", queryString);
        params.set("start", 0);
        params.set("rows", Integer.MAX_VALUE);
        params.set("sort", "id asc");
        params.setHighlight(false);
        params.set("qt", "/export");
        params.setFields(retKeys);
        QueryResponse response = server.query(params);




一个Bug:

org.apache.solr.client.solrj.impl.HttpSolrClient$RemoteSolrException: Error from server at http://192.8.125.30:8985/solr/hotspot: Expected mime type application/octet-stream but got application/json.

Solrj没法正确解析出结果集,看了下源码,原因是Solr server返回的ContentType和Solrj解析时检查时不一致,Solrj的BinaryResponseParser这个CONTENT_TYPE是定死的:

public class BinaryResponseParser extends ResponseParser {
    public static final String BINARY_CONTENT_TYPE = "application/octet-stream";

一时半会也不知道怎么解决这个Bug,还是自己写个Http请求并获取结果吧,用HttpClient写了个简单的客户端请求并解析json获取数据,测试速度:

    String url = "http://192.8.125.30:8985/solr/hotspot/export?q=CollectTime%3A[2014-12-06T00%3A00%3A00.000Z+TO+2014-12-10T21%3A31%3A55.000Z]&sort=id+asc&fl=id&wt=json&indent=true";
    long s = System.currentTimeMillis();
    SolrHttpJsonClient client = new SolrHttpJsonClient();
    SolrQueryResult result = client.getQueryResultByGet(url);
    System.out.println("Size: "+result.getResponse().getNumFound());
    long e = System.currentTimeMillis();
    System.out.println("Time: "+(e-s));




同样的查询条件获取220296个结果集,时间为2s左右,这样的查询获取数据的效率和MySQL建立索引后的效果差不多,暂时可以接受。

因为Export fields只支持int,float,long,double,string这几个类型,如果你的查询结果只包含这几个类型的字段,那采用这种方式查询并获取数据,速度要快很多。

下面是Solr使用“/select”和“/export”的速度对比。

时间对比:

查询条件   时间
MySQL(无索引) 30s
MySQL(有索引) 2s
Solrj(select查询) 12s
Solrj(export查询) 2s

项目中如果用分页查询,就用select方式,如果一次性要获取大量查询数据就用export方式,这里没有采用MySQL对查询字段建索引,因为数据量每天还在增加,当达到亿级的数据量的时候,索引也不能很好的解决问题,而且项目中还有其他的查询需求。

我们来看另一个查询需求,假设要统计每个设备(deviceID)上数据的分布情况:

用SQL,需要33s:

SELECT deviceID,Count(*) FROM `tf_hotspotdata_copy_test` GROUP BY deviceID;

同样的查询,在对CollectTime建立索引之后,只要14s了。

看看Solr的Facet查询,只要540ms,快的不是一点点。

SolrQuery query = new SolrQuery();
query.set("q", "*:*");
query.setFacet(true);
query.addFacetField("DeviceID");
QueryResponse response = server.query(query);
FacetField idFacetField = response.getFacetField("DeviceID");
List idCounts = idFacetField.getValues();
for (Count count : idCounts) {
    System.out.println(count.getName()+": "+count.getCount());
}



时间对比:

查询条件(统计)  时间
MySQL(无索引)33s
MySQL(有索引)14s
Solrj(Facet查询)0.54s

如果我们要查询某台设备在某个时间段上按“时”、“周”、“月”、“年”进行数据统计,Solr也是很方便的,比如以下按天统计设备号为1013上的数据:


    String startTime = "2014-12-06 00:00:00";
    String endTime = "2014-12-16 21:31:55";   
    SolrQuery query = new SolrQuery();
    query.set("q", "DeviceID:1013");
    query.setFacet(true);
    Date start = DateFormatHelper.ToSolrSearchDate(DateFormatHelper.StringToDate(startTime));
    Date end = DateFormatHelper.ToSolrSearchDate(DateFormatHelper.StringToDate(endTime));
    query.addDateRangeFacet("CollectTime", start, end, "+1DAY");
    QueryResponse response = server.query(query);
    List dateFacetFields = response.getFacetRanges();
    for (RangeFacet facetField : dateFacetFields{
        List dateCounts= facetField.getCounts();
        for (org.apache.solr.client.solrj.response.RangeFacet.Count count : dateCounts) {
            System.out.println(count.getValue()+": "+count.getCount());
        }
    }




2014-12-06T00:00:00Z: 58

2014-12-07T00:00:00Z: 0

2014-12-08T00:00:00Z: 0

2014-12-09T00:00:00Z: 0

2014-12-10T00:00:00Z: 3707

2014-12-11T00:00:00Z: 8384

2014-12-12T00:00:00Z: 7803

2014-12-13T00:00:00Z: 2469

2014-12-14T00:00:00Z: 142

2014-12-15T00:00:00Z: 34

2014-12-16T00:00:00Z: 0

Time: 662

 

水平拆分表:

由于本系统采集到的大量数据和“时间”有很大关系,一些业务需求根据“时间”来查询也比较多,可以按“时间”字段进行拆分表,比如按每月一张表来拆分,但是这样做应用层代码就需要做更多的事情,一些跨表的查询也需要更多的工作。综合考虑了表拆分和使用Solr来做索引查询的工作量后,还是采用了Solr。


总结:在MySQL的基础上,配合Lucene、Solr、ElasticSearch等搜索引擎,可以提高类似全文检索、分类统计等查询性能。







Solr与Sphinx的比较

Solr版本:4.8.1
Coreseek版本:3.2.14 (基于Sphinx 0.9.9 release)

1.索引效率
     Sphinx:10-15MB/秒, 实测最高可对100GB的文本建立索引,单一索引可包含1亿条记录
    Solr:10MB/秒,亿条数据,20G索引,新浪目前也采用lucene检索
2.搜索性能
    Sphinx:高性能搜索,在2-4GB的文本数据上,平均每次检索响应时间小于0.1秒;在1.2G文本,100万条文档上进行搜索,支持高达每秒150~250次查询;
    Solr:高性能搜索,8G的索引文件,检索响应时间为150ms。高峰支持500并发/秒。
3.扩展性
    Sphinx:高扩展性
    Solr:高扩展性
4.相关度算法
    Sphinx:基于短语相似度和统计(BM25)的复合Ranking方法,支持动态计算得分
    Solr:支持动态计算得分,也支持索引时计算得分
5.是否支持短语搜索
    支持
    支持
6.是否支持分布式搜索
    支持
    支持
7.是否支持Mysql引擎
    支持
    支持
8.是否支持多个字段全文检索
    支持
    支持
9.数据交互
    http请求,Xml、Json
    http请求,Xml、Json
10.数据源
     Sphinx:mysql, pgsql, mssql, xmlpipe , xmlpipe2, odbc,python
    Solr:mysql,Xml,json,csv,odbc
11.多索引
    支持
    支持
12.多核(不同去多索引)
    Sphinx:不支持
    Solr:支持

13. 中文分词的支持比较
          sphinx目前只支持mmseg3,sphinx for chinese两种分词,目前大家使用的比较多的是mmseg3。mmseg3的词库需要预先编译,不利于词库的扩充。
          Solr目前支持的词库比较多,目前支持的有庖丁,IK,mmsegj4,通过比较分词效果,IK在分词的查全和查准的效果上更好一些。IK分词支持设置停用词和扩展词库,扩展词库每个词一行的方式进行扩展。
14. 从Mysql数据库索引数据的支持
        sphinx可以通过配置数据源的方式直接从mysql中索引数据,在数据量比较大的时候,可以设置主索引+增量索引的方式来同步数据。但是主索引需要是根据Mysql表的主键Id来设置索引范围,比如Id小于9000,000,Id大于9000,000的通过增量索引来同步。这种只适应Id小于9000,000的数据更新频繁的情况。
        如果主索引和增量索引是以记录的最后更新时间来区分的话,由于主索引和增量索引使用的是2个不同的索引文件,这就会造成主索引和增量索引中的数据不一致,导致检索时出现本不该出现的记录。这种情况就需要把增量索引合并到主索引中,如果主索引的数据比较大,不确定合并的时间需要多少。
        solr索引mysql数据时,可以配置成主索引+增量索引,主索引和增量索引之间以更新的时间戳为分割线,solr会记录每次更新的时间戳。由于solr的增量索引和增量索引使用的都是一个索引文件,所以在执行增量索引时会自动合并到最终的索引中。
15. 实时检索的支持
        sphinx本身不支持中文分词,Coreseek是现在用的最多的sphinx中文全文检索,它提供了为Sphinx设计的中文分词包LibMMSeg。Coreseek目前稳定版是3.2.14(基于Sphinx 0.9.9 release开发),该版本还不支持实时检索。目前Coresekk4.1还是测试版,测试版本支持实时检索,但是不太稳定。
        solr的索引支持实时新增、更新和删除,可以根据记录的最后更新时间实现增量更新,在增量更新数据不多的情况下,可以设置增量更新任务为10秒更新一次。从而达到数据的实时同步。

【solr全文检索】Solr全文搜索与MySQL查询性能比较

http://m.bbyears.com/aspjiaocheng/82723.html

推荐访问:solr原理 solr面试题 solr是什么
相关阅读 猜你喜欢
本类排行 本类最新