【课程21】elasticsearch.xmind82.1KB
【课程21】lucene.xmind0.8MB
【课程21】基本用法....api.xmind83.2KB
【课程21预习】全...arch.xmind0.4MB
Lucene
为了更深入地理解ElasticSearch的工作原理,特别是索引和查询这两个过程,理解Lucene的工作原理至关重要。本质上,ElasticSearch是用Lucene来实现索引的查询功能的。
lucene的最新发布版本(7.5.0)
建立索引概念
为了对文档进行索引,Lucene 提供了五个基础的类,他们分别是 Document, Field, IndexWriter, Analyzer, Directory。下面我们分别介绍一下这五个类的用途:
Document
Document 是用来描述文档的,这里的文档可以指一个 HTML 页面,一封电子邮件,或者是一个文本文件。一个 Document 对象由多个 Field 对象组成的。可以把一个 Document 对象想象成数据库中的一个记录,而每个 Field 对象就是记录的一个字段。
Field
Field 对象是用来描述一个文档的某个属性的,比如一封电子邮件的标题和内容可以用两个 Field 对象分别描述。
Analyzer
在一个文档被索引之前,首先需要对文档内容进行分词处理,这部分工作就是由 Analyzer 来做的。Analyzer 类是一个抽象类,它有多个实现。针对不同的语言和应用需要选择适合的 Analyzer。Analyzer 把分词后的内容交给 IndexWriter 来建立索引。
IndexWriter
IndexWriter 是 Lucene 用来创建索引的一个核心的类,他的作用是把一个个的 Document 对象加到索引中来。
Directory
这个类代表了 Lucene 的索引的存储的位置,这是一个抽象类,它目前有两个实现,第一个是 FSDirectory,它表示一个存储在文件系统中的索引的位置。第二个是 RAMDirectory,它表示一个存储在内存当中的索引的位置。
搜索文档概念
Lucene 提供了几个基础的类来完成这个过程,它们分别是呢 IndexSearcher, Term, Query, TermQuery, Hits. 下面我们分别介绍这几个类的功能。
Query
这是一个抽象类,他有多个实现,比如 TermQuery, BooleanQuery, PrefixQuery. 这个类的目的是把用户输入的查询字符串封装成 Lucene 能够识别的 Query。
Term
Term 是搜索的基本单位,一个 Term 对象有两个 String 类型的域组成。生成一个 Term 对象可以有如下一条语句来完成:Term term = new Term(“fieldName”,”queryWord”); 其中第一个参数代表了要在文档的哪一个 Field 上进行查找,第二个参数代表了要查询的关键词。
TermQuery
TermQuery 是抽象类 Query 的一个子类,它同时也是 Lucene 支持的最为基本的一个查询类。生成一个 TermQuery 对象由如下语句完成: TermQuery termQuery = new TermQuery(new Term(“fieldName”,”queryWord”)); 它的构造函数只接受一个参数,那就是一个 Term 对象。
IndexSearcher
IndexSearcher 是用来在建立好的索引上进行搜索的。它只能以只读的方式打开一个索引,所以可以有多个 IndexSearcher 的实例在一个索引上进行操作。
Hits
Hits 是用来保存搜索的结果的。
倒排索引
倒排索引(Inverted Index)是一种索引数据结构。在倒排索引中,词语被映射到包含该词语的文档。
倒排索引源于实际应用中需要根据属性的值来查找记录。
这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。
由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。
比如:正常关键字对应关系应该是:“文章号”对“文章中所有关键词”。倒排索引把这个关系倒过来,变成: “关键词”对“拥有该关键词的所有文章号”。
通过使用倒排索引,可以实现快速的全文搜索。一个简单的倒排索引及其构建过程如下图所示,其中文档d1和d2的内容分别是“home sales rise in July”和“increase in home sales in July”。
对于文档d1和d2,首先进行分词处理,将文本内容划分为词语集。因为在英文文本中,单词之间均有空格,所以使用空格作为分隔符进行分词处理,得到词语集,如图中左侧一列。对于划分后的词语集,进行统计,统计词语及其出现的次数和位置,如图中右侧一列,构成倒排索引。
关键词 文章号[出现频率] 出现位置
(倒排索引及其构建过程)
- 实现时,lucene将上面三列分别作为词典文件(Term Dictionary)、频率文件(frequencies)、位置文件 (positions)保存。其中词典文件不仅保存有每个关键词,还保留了指向频率文件和位置文件的指针,通过指针可以找到该关键字的频率信息和位置信息。
- 索引时,假设要查询单词 “live”,lucene先对词典二元查找、找到该词,通过指向频率文件的指针读出所有文章号,然后返回结果。词典通常非常小,因而,整个过程的时间是毫秒级的。
项目包分析
- Package: org.apache.lucene.document
这个包提供了一些为封装要索引的文档所需要的类,比如 Document, Field。这样,每一个文档最终被封装成了一个 Document 对象。
- Package: org.apache.lucene.analysis
这个包主要功能是对文档进行分词,因为文档在建立索引之前必须要进行分词,所以这个包的作用可以看成是为建立索引做准备工作。
- Package: org.apache.lucene.index
这个包提供了一些类来协助创建索引以及对创建好的索引进行更新。这里面有两个基础的类:IndexWriter 和 IndexReader,其中 IndexWriter 是用来创建索引并添加文档到索引中的,IndexReader 是用来删除索引中的文档的。
- Package: org.apache.lucene.search
这个包提供了对在建立好的索引上进行搜索所需要的类。比如 IndexSearcher 和 Hits, IndexSearcher 定义了在指定的索引上进行搜索的方法,Hits 用来保存搜索得到的结果。
- org.apache.lucene.queryParser
包含了用于构建、解析查询条件的类。
包含了用于存储索引的类。
时序图(流程图)
见代码
索引与查询
索引过程:Lucene用用户指定好的analyzer解析用户添加的Document。当然Document中不同的Field可以指定不同的analyzer。如果用户的Document中有title和description两个Field,那么这两个Field可以指定不同的analyzer。
搜索过程:用户的输入查询语句将被选定的查询解析器(query parser)所解析,生成多个Query对象。当然用户也可以选择不解析查询语句,使查询语句保留原始的状态。在ElasticSearch中,有的Query对象会被解析(analyzed),有的不会,比如:前缀查询(prefix query)就不会被解析,精确匹配查询(match query)就会被解析。对用户来说,理解这一点至关重要。
对于索引过程和搜索过程的数据解析这一环节,我们需要把握的重点在于:倒排索引中词应该和查询语句中的词正确匹配。
流程
- Lucene的analysis模块主要负责词法分析及语言处理而形成Term。
- Lucene的index模块主要负责索引的创建,里面有IndexWriter。
- Lucene的QueryParser主要负责语法分析。
- Lucene的search模块主要负责对索引的搜索。
- Lucene的similarity模块主要负责对相关性打分的实现。
在Lucene的倒排索引中,包含字段(Field)、文档(Document)、关键字(Term)这三个部分。每一个关键字均与一个集合相映射。集合中的每一个元素为一个二元组(Document,Field),表示该文档的该字段包含此关键字。Lucene的工作原理如图所示,主要分为以下6个步骤:
- 1)为每一个待检索的文件构建Document类对象,将文件中各部分内容作为Field类对象。
- 2)使用Analyzer类实现对文档中的自然语言文本进行分词处理,并使用IndexWriter类构建索引。
- 3)使用FSDirectory类设定索引存储的方式和位置,实现索引的存储。
- 5)使用Term类表示用户所查找的关键字以及关键字所在的字段,使用QueryParser类表示用户的查询条件。
- 6)使用IndexSearcher类检索索引,返回符合查询条件的Document类对象。
查询语言
用户使用Lucene进行查询操作时,输入的查询语句会被分解成一个或者多个Term以及逻辑运算符号。一个Term,在Lucene中可以是一个词,也可以是一个短语(用双引号括引来的多个词)。如果事先设定规则:解析查询语句,那么指定的analyzer就会用来处理查询语句的每个term形成Query对象。
一个Query对象中会存在多个布尔运算符,这些布尔运算符将多个Term关联起来形成查询子句。
可视化工具Luke
(我用这个)
Hibernate Search回顾
回顾之前的课程内容。详细内容可以回顾之前的课程12的内容、开源博客项目mblog解读,其中使用到了Hibernate Search。
通过hibernate search 创建 lucene全文索引。
Hibernate Search是在apache Lucene的基础上建立的主要用于Hibernate的持久化模型的全文检索工具。像Lucene这样的检索引擎能够给我们的项目在进行检索的时候带来非常高的效率,但是它们在基本对象的检索时会有一些问题,比如不能实现检索内容跟实体的转换,Hibernate Search正是在这样的情况下发展起来的,基于对象的检索引擎,能够很方便的将检索出来的内容转换为具体的实体对象。此外Hibernate Search能够根据需要进行同步或异步的索引更新。
Hibernate Search是 hibernate 对著名的全文检索系统 Lucene 的一个集成方案,作用在于对数据表中某些内容庞大的字段(如声明为text的字段)建立全文索引,它这样通过hibernate search就可以对这些字段进行全文检索后获得相应的POJO,从而加快了对内容庞大字段进行模糊搜索的速度(sql语句中like匹配)。
原理:
Hibernate Search是给Hibernate持久化模型架构来使用的一套全文检索工具,其全文检索依赖于Lucence引擎。全文搜索引擎(lucence)会将你要查询的这个字段的词语,进行分词,直接匹配分词。
运行原理
1、构造LuceneQuery查询索引库
2、EntityManager 构造 FullTextEntityManager 查询数据库
3、合并EntityManager 和 LuceneQuery —– FullTextQuery 查询索引库和数据库
4、查询索引库 fullTextQuery.getResultSize(); 查询索引,去重id
5、查询数据库 fullTextQuery.getResultList(); 根据索引库返回 id ,查询数据库
Elasticsearch
- 简单内容的演示,先做好一个postman的数据演示例子
简介
Elasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎。无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。
但是,Lucene只是一个库。想要使用它,你必须使用Java来作为开发语言并将其直接集成到你的应用中,更糟糕的是,Lucene非常复杂,你需要深入了解检索的相关知识来理解它是如何工作的。
Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
基本概念
索引
文档
文档类型
节点
集群
分片索引
- 每个索引都有多个分片,每个分片是一个lucene索引。
- 由于单个节点由于物理机硬件限制,存储的文档是有限的,如果一个索引包含海量文档,则不能在单个节点存储。ES提供分 片机制,同一个索引可以存储在不同分片(数据容器)中,这些分片又可以存储在集群中不同节点上
备份
索引副本
时间之门
特点
- 开箱即用。安装好ElasticSearch后,所有参数的默认值都自动进行了比较合理的设置,基本不需要额外的调整。包括内置的发现机制(比如Field类型的自动匹配)和自动化参数配置。
- 天生集群。ElasticSearch默认工作在集群模式下。节点都将视为集群的一部分,而且在启动的过程中自动连接到集群中。
- 自动容错。ElasticSearch通过P2P网络进行通信,这种工作方式消除了单点故障。节点自动连接到集群中的其它机器,自动进行数据交换及以节点之间相互监控。索引分片
- 扩展性强。无论是处理能力和数据容量上都可以通过一种简单的方式实现扩展,即增添新的节点。
- 近实时搜索和版本控制。由于ElasticSearch天生支持分布式,所以延迟和不同节点上数据的短暂性不一致无可避免。ElasticSearch通过版本控制(versioning)的机制尽量减少问题的出现。
工作原理
应用场景
安装
下载linux版本,并解压。
cd elasticsearch-6.4.3
./bin/elasticsearch
默认端口9200。
root权限下直接运行elasticsearch会报错,所以需要切换到其他用户。
#新增一个用户
useradd lfq
#授权
chown -R lfq /opt/elasticsearch-*
#切换到这个用户
su lfq
安装过程中可能会遇到以下两个错误:
1、[1]: max file descriptors [65535] for elasticsearch process is too low, increase to at least [65536]
文件描述符太低
解决办法:
切换到root用户修改/etc/security/limits.conf
su root
vim /etc/security/limits.conf
文本末加入
* soft nofile 65536
* hard nofile 131072
* soft nproc 2048
* hard nproc 4096
推出当前用户,从新进入
现在ok了~
2、[2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
max_map_count 的值是指一个进程最多可用于的内存映射区(memory map areas),在调用malloc会用到,由mmap/mprotect生成。
解决办法:
切换到root用户修改配置/etc/sysctl.conf
su root
vim /etc/sysctl.conf
加入
vm.max_map_count=655360
然后使其生效
sysctl -p
ok,可以了。
3、有时候启动elasticsearch时候会提示内存不足,然后启动不了,这时候可以改小jvm最小内存
cat config/jvm.options
-Xms1g 改成 -Xms512m
如果需要外网访问,可以添加外网的ip。现在测试环境允许所有人访问~~
vim config/elasticsearch.yml
#添加下面一行即可
network.host: 0.0.0.0
#允许跨域访问,方便后面可视化工具elasticsearch-head
http.cors.enabled: true
http.cors.allow-origin: "*"
启动完毕之后,打开浏览器输入链接:
主节点配置
vim config/elasticsearch.yml
cluster.name: kobe
node.name: master
node.master: true
重启服务。
从节点1
从新解压一份到slave1文件夹
vim config/elasticsearch.yml
cluster.name: kobe
node.name: slave1
http.port: 9800
network.host: 0.0.0.0
#设置主动发现主节点地址
discovery.zen.ping.unicast.hosts: ["127.0.0.1"]
如果提示id重复,删除slave1文件夹下的data文件夹下的节点数据,然后重启即可
elasticsearch-head 是用于监控 Elasticsearch 状态的客户端插件,包括数据可视化、执行增删改查操作等。elasticsearch-head 插件的安装在 Linux 和 Windows 没什么区别,安装之前确保当前系统已经安装 nodejs 即可。
git clone git://github.com/mobz/elasticsearch-head.git
cd elasticsearch-head
npm install 或者cnpm install(需要安装淘宝镜像)
npm run start
打开浏览器 http://localhost:9100/
至此,elasticsearch-head连上elasticsearch了。
集群状态解读
head插件会以不同的颜色显示。
1. 绿色——最健康的状态,代表所有的主分片和副本分片都可用;
2. 黄色——所有的主分片可用,但是部分副本分片不可用;
3. 红色——部分主分片不可用。(此时执行查询部分数据仍然可以查到,遇到这种情况,还是赶快解决比较好。)
如果集群状态为红色,Head插件显示:集群健康值red。则说明:至少一个主分片分配失败。这将导致一些数据以及索引的某些部分不再可用。
尽管如此, ElasticSearch还是允许我们执行查询,至于是通知用户查询结果可能不完整还是挂起查询,则由应用构建者来决定。
基本用法-restful api
管理功能
维护索引
- 提供针对索引的CRUD操作(Create【PUT】, Read【GET】, Update【POST】, and Delete【POST】)
检查集群状态
curl localhost:9200/_cat/health?v
检查节点状态
curl localhost:9200/_cat/nodes?v
查询全部索引
curl localhost:9200/_cat/indices?v
适合旧版本的,新版本看postman
https://www.do1618.com/archives/1276/elasticsearch-%E4%B8%AD%E7%9A%84%E7%B4%A2%E5%BC%95%E4%B8%8E%E7%B1%BB%E5%9E%8B%E7%9A%84%E5%89%8D%E7%94%9F%E4%BB%8A%E4%B8%96/
创建索引
curl -XPUT localhost:9200/索引名/类型/id -d {//JSON格式的body体}
删除索引
curl -XDELETE localhost:9200/索引名
查询索引
curl -XGET localhost:9200/索引名/类型/id
curl -XGET localhost:9200/索引名/_search?q=
_bulk 批量操作
_search 搜索
?pretty 表示格式化输出
- mapping-结构化映射关系。为空时候是非结构化索引
详细请看postman~~~,请用postman导入以下json数据练习!
elasticsearch-test.post...tion.json9.9KB
高级查询
简单查询
条件查询
聚合查询
springboot集成ES
集成配置:
- InetSocketTransportAddress节点信息node
- TransportClient中添加阶段信息,集群可以添加多个节点。
- 设置集群配置Setting,包括cluster.name等信息
使用:
TransportClient client;
项目应用开发指南
开发指南
首先需要考虑搜索的内容主体包含的信息,如ID,标题、摘要、作者等。还需要包括排序信息(更新时间,创建时间),或需要搜索的字段等信息。
- 第二步:为索引结构建立对应的模板(DTO),字段应该与索引的字段一一对应。另外,因为key的名字都是定死的,为了统一管理,可以给每个key定义成final,方便统一管理和getset等。
- 第三步:创建searchService,主要有个两个方法,1、构建索引(创建、更新),2、删除索引。
- 首先要一个ID作为唯一要构建的索引。通过ID从数据库中查询信息出来,包括需要关联的表等信息,得到模板数据(DTO),然后去elasticsearch查询有没该索引,如果没有则创建,如果有一条就更新,如果有多条就全删除然后重新创建。
- 考虑到一些elasticsearch有不稳定的时候,所以可以给构建索引、删除索引建立一个重试机制,比如3次重试机会。
- 第四步:当业务数据发生变化时候更新对应索引信息,分为两种形式调用
- 同步调用:直接在业务方法重点调用searchService对应的方法。
- 异步调用:基于消息中间件,如kafka,rabbitmq等。
- 当用户没输入关键字的时候,默认直接从数据库查询信息。
- 当用户输入关键字的时候先从elasticsearch条件查询出数据的IDs,然后拿IDs去从数据库中取。
复杂查询
* 使用QueryBuilder
* termQuery("key", obj) 完全匹配
* termsQuery("key", obj1, obj2..) 一次匹配多个值
* matchQuery("key", Obj) 单个匹配, field不支持通配符, 前缀具高级特性
* multiMatchQuery("text", "field1", "field2"..); 匹配多个字段, field有通配符忒行
* matchAllQuery(); 匹配所有文件
* 只查询一个id的
* QueryBuilders.idsQuery(String...type).ids(Collection<String> ids)
/**
* moreLikeThisQuery: 实现基于内容推荐, 支持实现一句话相似文章查询
* {
"more_like_this" : {
"fields" : ["title", "content"],
"like_text" : "text like this one",
}
}
percent_terms_to_match:匹配项(term)的百分比,默认是0.3
min_term_freq:一篇文档中一个词语至少出现次数,小于这个值的词将被忽略,默认是2
max_query_terms:一条查询语句中允许最多查询词语的个数,默认是25
stop_words:设置停止词,匹配时会忽略停止词
min_doc_freq:一个词语最少在多少篇文档中出现,小于这个值的词会将被忽略,默认是无限制
max_doc_freq:一个词语最多在多少篇文档中出现,大于这个值的词会将被忽略,默认是无限制
min_word_len:最小的词语长度,默认是0
max_word_len:最多的词语长度,默认无限制
boost_terms:设置词语权重,默认是1
boost:设置查询权重,默认是1
analyzer:设置使用的分词器,默认是使用该字段指定的分词器
*/
* 前缀查询
*/
@Test
public void testPrefixQuery() {
QueryBuilder queryBuilder = QueryBuilders.matchQuery("user", "kimchy");
searchFunction(queryBuilder);
}
分词