【基础理论】数据检索问题与解决方案、Solr和ES性能比较(一)

1 数据检索问题

1.1 大规模数据如何检索?

当系统数据量上了10亿、100亿条的时候,我们在做系统架构的时候通常会从以下角度去考虑:
1)用什么数据库好?(MySQL、sybase、Oracle、达梦、神通、MongoDB、Hbase…)
2)如何解决单点故障;(lvs、F5、A10、Zookeep、MQ)
3)如何保证数据安全性;(热备、冷备、异地多活)
4)如何解决检索难题;(数据库代理中间件:mysql-proxy、Cobar、MaxScale等;)
5)如何解决统计分析问题;(离线、近实时)
##1.2 传统数据库的应对解决方案?
对于关系型数据,我们通常采用以下或类似架构去解决查询瓶颈和写入瓶颈:
解决要点:
1)通过主从备份解决数据安全性问题;
2)通过数据库代理中间件心跳监测,解决单点故障问题;
3)通过代理中间件将查询语句分发到各个slave节点进行查询,并汇总结果
4)通过分表分库解决读写效率问题
【基础理论】数据检索问题与解决方案、Solr和ES性能比较(一)

1.3 非关系型数据库的解决方案?

对于Nosql数据库,以redis为例,其它原理类似:
解决要点:
1)通过副本备份保证数据安全性;
2)通过节点竞选机制解决单点问题;
3)先从配置库检索分片信息,然后将请求分发到各个节点,最后由路由节点合并汇总结果
【基础理论】数据检索问题与解决方案、Solr和ES性能比较(一)

1.4 完全把数据放入内存怎么样?

完全把数据放在内存中是不可靠的,实际上也不太现实,当我们的数据达到PB级别时,按照每个节点96G内存计算,
在内存完全装满的数据情况下,我们需要的机器是:1PB=1024T=1048576G
节点数=1048576/96=10922个
实际上,考虑到数据备份,节点数往往在2.5万台左右。成本巨大决定了其不现实!
从前面我们了解到,把数据放在内存也好,不放在内存也好,都不能完完全全解决问题。
全部放在内存速度问题是解决了,但成本问题上来了。 为解决以上问题,从源头着手分析,通常会从以下方式来寻找方法:
1、存储数据时按有序存储;
2、将数据和索引分离;
3、压缩数据;
全文检索技术: — 检索的数据是一些菲关系型数据,word,pdf,html

2 全文检索技术

2.1 什么是全文检索?

什么叫做全文检索呢?这要从我们生活中的数据说起。
我们生活中的数据总体分为两种:结构化数据和非结构化数据。
• 结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等。

对结构化数据的搜索: 如对数据库的搜索,用SQL语句。再如对元数据的搜索,如利用windows搜索对文件名,类型,修改时间进行搜索等。
• 非结构化数据:指不定长或无固定格式的数据,如 互联网数据、邮件,word文档等。
对非结构化数据的搜索: 如用Google和百度可以搜索大量内容数据。
对非结构化数据顺序扫描很慢,对结构化数据的搜索却相对较快,那么把我们的非结构化数据想办法弄得有一定结构不就行了吗?这就是全文检索的基本思路,也即将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。这部分从非结构化数据中提取出的然后重新组织的信息,我们称之索引 。
非结构化数据又一种叫法叫全文数据。
按照数据的分类,搜索也分为两种:
对非结构化数据也即全文数据的搜索主要有两种方法:顺序扫描法和反向索引法。
• 顺序扫描法:所谓顺序扫描法,就是顺序扫描每个文档内容,看看是否有要搜索的关键字,实现查找文档的功能,也就是根据文档找词。
• 反向索引法:所谓反向索引,就是提前将搜索的关键字建成索引,然后再根据索引查找文档,也就是根据词找文档。
这种先建立索引,再对索引进行搜索文档的过程就叫全文检索(Full-text Search)。

2.2 全文检索场景

• 搜索引擎
• 站内搜索
• 系统文件搜索

2.3 实时搜索与传统搜索

通常来说,传统搜索都是一些“静态”的搜索,即用户搜索的只是从信息库里边筛选出来的信息。而百度推出的实时搜索功能,改变了传统意义上的静态搜索模式,用户对于搜索的结果是实时变化的。
举个例子,用户在搜索“华山”、“峨眉山”等景点时,实时观看各地景区画面。以华山景区为例,当用户在搜索框中输入“华山”时,点击右侧“实时直播——华山”,即可实时观看华山靓丽风景,并能在华山长空栈道、北峰顶、观日台三个视角之间切换。同时,该直播引入广受年轻人欢迎的“弹幕”模式,用户在观看风景时可以同时发表评论,甚至进行聊天互动。

2.4 全文检索相关技术

  1. Lucene:如果使用该技术实现,需要对Lucene的API和底层原理非常了解,而且需要编写大量的Java代码。
  2. Solr:使用java实现的一个web应用,可以使用rest方式的http请求,进行远程API的调用。
  3. ElasticSearch(ES):可以使用rest方式的http请求,进行远程API的调用。

2.5 Solr和ES的比较

• 当单纯的对已有数据进行搜索时,Solr更快。
【基础理论】数据检索问题与解决方案、Solr和ES性能比较(一)
• 当实时建立索引时, Solr会产生io阻塞,查询性能较差, Elasticsearch具有明显的优势。
【基础理论】数据检索问题与解决方案、Solr和ES性能比较(一)
• 随着数据量的增加,Solr的搜索效率会变得更低,而Elasticsearch却没有明显的变化。
【基础理论】数据检索问题与解决方案、Solr和ES性能比较(一)
• 大型互联网公司,实际生产环境测试,将搜索引擎从Solr转到Elasticsearch以后的平均查询速度有了50倍的提升。
【基础理论】数据检索问题与解决方案、Solr和ES性能比较(一)

2.5.2 Elasticsearch 与 Solr 的比较总结

• 二者安装都很简单;
• Solr 利用 Zookeeper 进行分布式管理,而 Elasticsearch 自身带有分布式协调管理功能;
• Solr 支持更多格式的数据,而 Elasticsearch 仅支持json文件格式;
• Solr 官方提供的功能更多,而 Elasticsearch 本身更注重于核心功能,高级功能多有第三方插件提供;
• Solr 在传统的搜索应用中表现好于 Elasticsearch,但在处理实时搜索应用时效率明显低于 Elasticsearch。
最终的结论:
Solr 是传统搜索应用的有力解决方案,但 Elasticsearch 更适用于新兴的实时搜索应用。

3 全文检索的流程分析

3.1 什么是索引

有人可能会说,对非结构化数据顺序扫描很慢,对结构化数据的搜索却相对较快(由于结构化数据有一定的结构可以采取一定的搜索算法加快速度),那么把我们的非结构化数据想办法弄得有一定结构不就行了吗?
这种想法很天然,却构成了全文检索的基本思路,也即将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。
这部分从非结构化数据中提取出的然后重新组织的信息,我们称之索引
这种说法比较抽象,举几个例子就很容易明白,比如字典,字典的拼音表和部首检字表就相当于字典的索引,对每一个字的解释是非结构化的,如果字典没有音节表和 部首检字表,在茫茫辞海中找一个字只能顺序扫描。然而字的某些信息可以提取出来进行结构化处理,比如读音,就比较结构化,分声母和韵母,分别只有几种可以 一一列举,于是将读音拿出来按一定的顺序排列,每一项读音都指向此字的详细解释的页数。我们搜索时按结构化的拼音搜到读音,然后按其指向的页数,便可找到 我们的非结构化数据——也即对字的解释

3.2 流程总览

搜索基本的流程实现:
【基础理论】数据检索问题与解决方案、Solr和ES性能比较(一)

全文检索的流程分为两大流程:索引创建、搜索索引
• 索引创建:将现实世界中所有的结构化和非结构化数据提取信息,创建索引的过程。
• 搜索索引:就是得到用户的查询请求,搜索创建的索引,然后返回结果的过程。
【基础理论】数据检索问题与解决方案、Solr和ES性能比较(一)

想搞清楚全文检索,必须要搞清楚下面三个问题:

  1. 索引库里面究竟存些什么?(Index)
  2. 如何创建索引?(Indexing)
  3. 如何对索引进行搜索?(Search)

3.3 原始内容

原始内容是指要索引和搜索的内容。
原始内容包括互联网上的网页、数据库中的数据、磁盘上的文件等。

3.4 获得文档

也就是采集数据,从互联网上、数据库、文件系统中等获取需要搜索的原始信息,这个过程就是信息采集。
采集数据的目的是为了将原始内容存储到Document对象中。
如何采集数据?

  1. 对于互联网上网页,可以使用工具将网页抓取到本地生成html文件。
  2. 数据库中的数据,可以直接连接数据库读取表中的数据。
  3. 文件系统中的某个文件,可以通过I/O操作读取文件的内容。
    在Internet上采集信息的软件通常称为爬虫或蜘蛛,也称为网络机器人,爬虫访问互联网上的每一个网页,将获取到的网页内容存储起来。

3.5 创建文档对象

创建文档的目的是统一数据格式(Document),方便文档分析。
【基础理论】数据检索问题与解决方案、Solr和ES性能比较(一)

说明:

  1. 一个Document文档中包括多个域(Field),域(Field)中存储内容。
  2. 这里我们可以将数据库中一条记录当成一个Document,一列当成一个Field

3.6 分析文档(重点)

分析文档主要是对Field域进行分析,分析文档的目的是为了索引。
【基础理论】数据检索问题与解决方案、Solr和ES性能比较(一)

说明:分析文档主要通过分词组件(Tokenizer)和语言处理组件(Linguistic Processor)完成

3.6.1 分词组件

分词组件工作流程(此过程称之为Tokenize)

  1. 将Field域中的内容进行分词(不同语言有不同的分词规则)
  2. 去除标点符号。
  3. 去除停用词(stop word)。
    经过分词(Tokenize)之后得到的结果成为词元(Token)。
    所谓停词(Stop word)就是一种语言中最普通的一些单词,由于没有特别的意义,因而大多数情况下不能成为搜索的关键词,因而创建索引时,这种词会被去掉而减少索引的大小。
    英语中停词(Stop word)如:“the”,“a”,“this”等。
    对于每一种语言的分词组件(Tokenizer),都有一个停词(stop word)集合。
    示例(Document1的Field域和Document2的Field域是同名的):
    • Document1的Field域:
    Students should be allowed to go out with their friends, but not allowed to drink beer.
    • Document2的Field域:
    My friend Jerry went to school to see his students but found them drunk which is not allowed.
    • 在我们的例子中,便得到以下词元(Token):
    “Students”,“allowed”,“go”,“their”,“friends”,“allowed”,“drink”,“beer”,“My”,“friend”,“Jerry”,“went”,“school”,“see”,“his”,“students”,“found”,“them”,“drunk”,“allowed”。
    将得到的词元(Token)传给语言处理组件(Linguistic Processor)

3.6.2 语言处理组件

语言处理组件(linguistic processor)主要是对得到的词元(Token)做一些同语言相关的处理。
对于英语,语言处理组件(Linguistic Processor)一般做以下几点:

  1. 变为小写(Lowercase)。
  2. 将单词缩减为词根形式,如“cars”到“car”等。这种操作称为:stemming。
  3. 将单词转变为词根形式,如“drove”到“drive”等。这种操作称为:lemmatization。
    语言处理组件(linguistic processor)的结果称为词(Term)。Term是索引库的最小单位。
    • 在我们的例子中,经过语言处理,得到的词(Term)如下:
    “student”,“allow”,“go”,“their”,“friend”,“allow”,“drink”,“beer”,“my”,“friend”,“jerry”,“go”,“school”,“see”,“his”,“student”,“find”,“them”,“drink”,“allow”。
    也正是因为有语言处理的步骤,才能使搜索drove,而drive也能被搜索出来。

3.7 Lucene相关度排序

3.7.1 什么是相关度排序

相关度排序是查询结果按照与查询关键字的相关性进行排序,越相关的越靠前。比如搜索“Lucene”关键字,与该关键字最相关的文章应该排在前边。

3.7.2 相关度打分

Lucene对查询关键字和索引文档的相关度进行打分,得分高的就排在前边。
如何打分呢?Lucene是在用户进行检索时实时根据搜索的关键字计算出来的,分两步:

  1. 计算出词(Term)的权重
  2. 根据词的权重值,计算文档相关度得分。
    什么是词的权重?
    通过索引部分的学习,明确索引的最小单位是一个Term(索引词典中的一个词)。搜索也是从索引域中查询Term,再根据Term找到文档。Term对文档的重要性称为权重,影响Term权重有两个因素:
    • Term Frequency (tf):
    指此Term在此文档中出现了多少次。tf 越大说明越重要。
    词(Term)在文档中出现的次数越多,说明此词(Term)对该文档越重要,如“Lucene”这个词,在文档中出现的次数很多,说明该文档主要就是讲Lucene技术的。
    • Document Frequency (df):
    指有多少文档包含此Term。df 越大说明越不重要。
    比如,在一篇英语文档中,this出现的次数更多,就说明越重要吗?不是的,有越多的文档包含此词(Term), 说明此词(Term)太普通,不足以区分这些文档,因而重要性越低。

3.7.3 设置boost值影响相关度排序

boost是一个加权值(默认加权值为1.0f),它可以影响权重的计算。在索引时对某个文档中的field设置加权值,设置越高,在搜索时匹配到这个文档就可能排在前边。

3.8 Lucene的Field域

3.8.1 Field属性

Field是文档中的域,包括Field名和Field值两部分,一个文档可以包括多个Field,Document只是Field的一个承载体,Field值即为要索引的内容,也是要搜索的内容。
• 是否分词(tokenized)
o 是:作分词处理,即将Field值进行分词,分词的目的是为了索引。
比如:商品名称、商品描述等,这些内容用户要输入关键字搜索,由于搜索的内容格式大、内容多需要分词后将语汇单元建立索引
o 否:不作分词处理
比如:商品id、订单号、身份证号等
• 是否索引(indexed)
o 是:进行索引。将Field分词后的词或整个Field值进行索引,存储到索引域,索引的目的是为了搜索。
比如:商品名称、商品描述分析后进行索引,订单号、身份证号不用分词但也要索引,这些将来都要作为查询条件。
o 否:不索引。
比如:图片路径、文件路径等,不用作为查询条件的不用索引
• 是否存储(stored)
o 是:将Field值存储在文档域中,存储在文档域中的Field才可以从Document中获取。
比如:商品名称、订单号,凡是将来要从Document中获取的Field都要存储。
o 否:不存储Field值
比如:商品描述,内容较大不用存储。如果要向用户展示商品描述可以从系统的关系数据库中获取。

3.8.2 Field设计

Field域如何设计,取决于需求,比如搜索条件有哪些?显示结果有哪些?
• 商品id:
是否分词:不用分词,因为不会根据商品id来搜索商品
是否索引:不索引,因为不需要根据商品ID进行搜索
是否存储:要存储,因为查询结果页面需要使用id这个值。
• 商品名称:
是否分词:要分词,因为要根据商品名称的关键词搜索。
是否索引:要索引。
是否存储:要存储。
• 商品价格:
是否分词:要分词,lucene对数字型的值只要有搜索需求的都要分词和索引,因为lucene对数字型的内容要特殊分词处理,需要分词和索引。
是否索引:要索引
是否存储:要存储
• 商品图片地址:
是否分词:不分词
是否索引:不索引
是否存储:要存储
• 商品描述:
是否分词:要分词
是否索引:要索引
是否存储:因为商品描述内容量大,不在查询结果页面直接显示,不存储。
常见问题:
不存储是指不在lucene的索引域中记录,目的是为了节省lucene的索引文件空间。
如果要在详情页面显示描述,解决方案:
从lucene中取出商品的id,根据商品的id查询关系数据库(MySQL)中item表得到描述信息。