es
ElasticSearch
ElasticSearch和Lucene的关系
ElasticSearch是基于Lucene做了一些封装和增强的
核心概念
- 索引
- 字段类型(mapping)
- 文档
概述
Node 与 Cluster
Elastic本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个elastic实例。单个elastic称为一个节点(node)。一组节点构成一个集群(cluster)
index
Elastic会索引所有字段,经过处理后会写入一个反向索引(inverted index)。查找数据的时候,直接查找该索引。所以,Elastic数据管理顶层单位就叫做index索引。他是单个数据库的同义词。每个index即数据库的名字必须是小写。通过一下命令查看所有的索引
1 |
|
document
Index里面的单条记录又称为Document(文档),多条document构成了一个Index。
Document使用Json格式表示
1 |
|
同一个index里面的document不要求有相同的结构scheme,但是最好保持相同,这样有利于提高搜索效率。
type
Document可以分组,比如weather这个index里面可以按城市分组(北京上海),也可以按照其后分组,这种分组叫做type,他是虚拟的逻辑分组,用来过滤document。
不同的type应该有相似的结构scheme,举例来说,id字段不能 在这个组里是字符串在另一个组里是数值。这与关系型数据库截然不同。MySQL里面不同表里的相同字段的类型是不受彼此影响的。现在在elastic里面性质完全不同的数据比如product和logs应该存成两个index而不是一个index里面的两个type(虽然可以做到)
下面可以列出每个index所包含的type
1 |
|
在elastic 6.X中每个index只允许包含一个type,7.X版本将彻底移除type
简称es,是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身的扩展性很好,可以扩展到上百台服务器,处理PB级别的数据。es也是用Java开发并使用Lucene作为其核心来实现所有的索引和搜索的功能,但是他的目的是通过简单的restfulAPI来隐藏Lucene的复杂性,从而让全文搜索变得简单。
历史
多年前,一个叫做Shay Banon的刚结婚不久的失业开发者,由于妻子要去伦敦学习厨师,她便跟着去了。在找工作的过程中为了给妻子构建一个食谱的搜索引擎,他便开始构建一个早期版本的Luence。
直接基于Luence的工作比较困难,所以Shay开始抽象Luence代码以便Java程序员可以在应用中添加搜索功能。他发布了他的第一个开源项目叫做Compass
后来Shay找到一份工作,这份工作在高性能和内存数据网格的分布式环境中,因此高性能的、实时的、分布式的搜索引擎也是理所当然的。然后他决定重写Compass库使其成为一个独立的服务叫做ElasticSearch
第一个公开版本出现于2010年二月分,在那之后ElasticSearch已经成为GitHub上最受欢迎的项目之一,代码贡献者超过300人。一家主营ElasticSearch的公司就此成立,他们一边提供服务一边开发新功能
ElasticSearch和solr的差别
Elasticsearch是一个实时分布式搜索和分析引擎。它让你以前所未有的速度处理大数据成为可能。它用于全文搜索、结构化搜索、分析以及将这三者混合使用︰维基百科使用Elasticsearch提供全文搜索并高亮关键字,以及输入实时搜索(search-asyou-type)和搜索纠错(did-you-mean)等搜索建议功能。英国卫报使用Elasticsearch结合用户日志和社交网络数据提供给他们的编辑以实时的反馈,以便及时了解公众对新发表的文章的回应。StackOverflow结合全文搜索与地理位置查询,以及more-like-this功能来找到相关的问题和答案。Github使用Elasticsearch检索1300亿行的代码。但是Elasticsearch不仅用于大型企业,它还让像DataDog以及Klout这样的创业公司将最初的想法变成可扩展的解决方案。Elasticsearch可以在你的笔记本上运行,也可以在数以百计的服务器上处理PB级别的数据。Elasticsearch是一个基于Apache Lucene(TM)的开源搜索引擎。无论在开源还是专有领域,Lucene可以被认为是迄今为止最先进、性能最好的、功能最全的搜索引擎库。Elasticsearch也使用lava开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。Elasticsearch也使用lava开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
Solr简介
Solr是Apache下的一个顶级开源项目,采用lava开发,它是基于Lucene的全文搜索服务器。Solr提供了比Lucene更为丰富的查询语言,同时实现了可配置、可扩展,并对索引、搜索性能进行了优化Solr可以独立运行,运行在Jetty、Tomcat等这些Servlet容器中,Solr索引的实现方法很简单,用POST方法向Solr服务器发送一个描述Field及其内容的XML文档,Solr根据xml文档添加、删除、更新索引。Solr搜索只需要发送HTTP GET请求,然后对Solr返回Xml、json等格式的查询结果进行解析,组织页面布局。Solr不提供构建UI的功能,Solr提供了一个管理界面,通过管理界面可以查询Solr的配置和运行情况。Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的文件,生成索引;也可以通过提出查找请求,并得到返回结果。Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口。用户可以通过http请求,向搜索引擎服务器提交一定格式的文件,生成索引;也可以通过提出查找请求,并得到返回结果。
ElasticSearch和Solr的比较


ElasticSearch VS Solr总结
- es 开箱即用,解压就可以用,非常简单。Solr安装比较复杂
- Solr利用zookeeper进行分布式管路,而ElasticSearch自身带有分布式协调管理的功能
- Solr支持更多的格式,比如Json、xml、CSV、,而es仅支持JSON但是当前用的最多 的还是JSON
- Solr提供更多的功能,而ES本身更注重于核心功能,高级的功能由第三方插件提供比如IK,和图形界面kibana
- Solr查询快,但是更新索引时慢(即插入删除慢),用于电商等查询多的应用;ES建立索引快(即查询快)及时性查询快,用于Facebook新浪等搜索。Solr是传统搜索应用的有利解决方案,但是ElasticSearch更适用于新型的实时搜索应用
- Solr比较成熟没有一个更大、更成熟的用户、开发和贡献者社区,而es相对开发维护者比较少,更新太快学习成本高
下载
1 |
|
默认情况下ES只允许本机访问,如果需要远程访问可以修改ES安装目录下面的config/elasticsearch.yml文件,去掉network.host的注释,将他的值改为0.0.0.0,然后重新启动
1 |
|
启动
双击/bin/elasticsearch.bat 文件
安装可视化界面
配置跨域
1
2
3
4# 打开跨域支持
http.cors.enabled: true
# 设置为允许所有人访问
http.cors.allow-origin: "*"启动指令
1
21. npm install
2. net run start
初学可以把es当作数据库,可以建立索引(库),文档(库中的数据)这个head我们就把它当作数据展示工具
了解Elk
ELK是Elasticsearch、Logstash、Kibana三大开源框架首字母大写简称。市面上也被成为Elastic Stack。其中Elasticsearch是一个基于Lucene、分布式、通过Restful方式进行交互的近实时搜索平台框架。像类似百度、谷歌这种大数据全文搜索引擎的场景都可以使用Elasticsearch作为底层支持框架,可见Elasticsearch提供的搜索能力确实强大,市面上很多时候我们简称Elasticsearch为es。Logstash是ELK的中央数据流引擎,用于从不同目标(文件/数据存储/MQ)收集的不同格式数据,经过过滤后支持输出到不同目的地(文件/MQ/redis/elasticsearch/kafka等 )。Kibana可以将elasticsearch的数据通过友好的页面展示出来,提供实时分析的功能。市面上很多开发只要提到ELK能够一致说出它是一个日志分析架构技术栈总称,但实际上ELK不仅仅适用于日志分析,它还可以支持其它任何数据分析和收集的场景,日志分析和收集只是更具有代表性。并非唯一性。
安装Kibana
Kibana是一个针对Elasticsearch的开源分析及可视化平台,用来搜索、查看交互存储在Elasticsearch索引中的数据。使用Kibana ,可以通过各种图表进行高级数据分析及展示。Kibana让海量数据更容易理解。它操作简单,基于浏览器的用户界面可以快速创建仪表板( dashboard )实时显示Elasticseaich查询动态。设置Kibana非常简单。无需编码或者额外的基础架构,几分钟内就可以完成Kibana安装并启动Elasticsearch索引监测。
目录
在/config/kibana.yml里面进行汉化
elasticsearch是面向文档的,关系型数据库和elasticsearch的对比
关系型数据库 | ElasticSearch |
---|---|
数据库 DB | 索引(includes) |
表(tables) | types(慢慢在弃用) |
行(rows) | document |
字段(columns) | fields |
elasticsearch(集群)中可以包含多个索引(数据库),每个索引中可以包含多个类型(表),每个类型下又包含多个文档(行),每个文档中又包括多个字段(列)
物理设计
elasticsearch在后台把每个 索引划分成多个分片 ,每分分片可以在集群中的不同服务器间迁移,一个人就是一个集群!默认的集群名就是elasticsearch
文档
就是我们的一条条数据
之前说es是面向文档的,那么就意味着索引和搜索数据的最小单位是文档,elasticsearch中,文档有几个重要属性
- 自我包含,一篇文档同时包含字段和对应的值,也就是同时包含key:value
- 可以是层次型的,一个文档中包含自文档,复杂的逻辑实体就是这么来的。{就是一个json对象,fastjson进行自动转换!}
- 灵活的结构,文档不依赖预先定义的模式,我们知道关系型数据库中要提前定义字段才能使用,在elasticsearch中,对于字段是非常灵活的,有时候,我们可以忽略该字段,或者动态的添加一个新字段。
尽管我们可以灵活的新增或者忽略某个字段,但是,每个字段的类型非常重要,比如一个年龄字段类型可以是字符串也可以是整形,因为elasticsearch会保存字段和类型之间的映射及其他设置。这种映射具体到每个映射的每种类型,这也是为什么在elasticsearch中,类型有时候也称为映射类型
类型
类型是文档的逻辑容器,就像关系型数据库一样,表格是行的容器。类型中对于字段的定义称为映射,比如name映射为字符串类型。我们说文档是无模式的,它们不需要拥有映射中所定义的所有字段,比如新增一个字段,那么elasticsearch是怎么做的呢?elasticsearch会自动的将新字段加入映射,但是这个字段的不确定它是什么类型,elasticsearch就开始猜,如果这个值是18,那么elasticsearch会认为它是整形。但是elasticsearch也可能猜不对,所以最安全的方式就是提前定义好所需要的映射,这点跟关系型数据库殊途同归了,先定义好字段,然后再使用,别整什么幺蛾子。
索引
就是数据库 索引是映射类型的容器。elasticsearch中的索引是一个非常大的文档集合。索引存储了映射类型的字段和其他设置。然后他们被存储到各个分片上了。我们来研究下分片是如何工作的
物理设计:节点和分片如何工作
一个集群这至少有一个节点,而一个 节点就是一个elasticsearch进程,节点可以有多个索引默认的,如果你创建索引,那么索引将会有五个分片(primary shard,又称主分片)构成的,每一个主分片 会有一个副本(replica shard,又称复制分片)
倒排锁引
elasticsearch使用的是一种称为倒排锁引的结构,采用Luence倒排锁引作为底层。这种结构适用于快速的全文搜索,一个锁引有文档中所有不重复的列表构成,对于每一个词,都有一个包含他的文档列表。例如,现在有两个文档,每个文档包含如下内容:
为了创建倒排锁引,我们首先要将每个文档拆分成独立的词(或称为词条或者tokens),然后创建一个包含所有不重复的词条的排序列表,然后列出每个词条出现哪个文档:
现在我们试图搜索to forever,只需要查看包含每个词条的文档 score
两个文档都匹配,但是第一个文档比第二个匹配程度更高。如果没有别的条件,现在,这两个包含关键字的文档都将返回。
IK分词器
安装
下载完毕之后解压放到elasticsearch插件中即可
重启ElasticSearch
也可以通过elasticsearch-plugin list来查看加载进来的插件
使用kibana测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37GET _analyze
{
"analyzer": "ik_smart",
"text": "Dean"
}
GET _analyze
{
"analyzer": "ik_max_word",
"text": "隔壁老樊"
}
//
{
"tokens" : [
{
"token" : "隔壁",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "老",
"start_offset" : 2,
"end_offset" : 3,
"type" : "CN_CHAR",
"position" : 1
},
{
"token" : "樊",
"start_offset" : 3,
"end_offset" : 4,
"type" : "CN_CHAR",
"position" : 2
}
]
}IK分词器增加自己的配置
新建一个.dic文件
在里面添加我们需要的词语
把我们的字典配置ik的配置文件中
再次测试
Rest风格说明
基础测试
创建一个索引
1
2
3
4
5
6
7PUT /索引名/类型名/文档id
{请求体}
PUT /test1/type1/1
{
"name":"Dean超给力",
"age":18
}~~~ json
// 新建一个索引,这一个就相当于是一种规范对字段有约束,就类似于MySQL类里面的字段类型
PUT /test2
{
“mappings”: {"properties": { "name":{ "type": "text" }, "age":{ "type": "long" }, "birthday":{ "type": "date" } }
}
}1
2
3
4
5
6
7
如果我们的文档字段没有指定类型,那么es就会给我们配置默认的字段类型
**扩展**:通过命令查看elasticsearch索引情况
~~~ bash
GET _cat/
修改
1
2
3
4
5
6POST /test2/_doc/2/_update
{
"doc":{
"name":"法外狂徒王五"
}
}查
1
2
3
4// 查找所有的
POST /test2/_doc/_search
// 获取指定id的
GET /test2/_doc/2删除索引
1
2
3DELETE test1
// 删除指定的文档
DELETE /test2/_doc/2关于文档的操作
添加数据
1
2
3
4
5
6PUT /test2/_doc/5
{
"name":"王富国",
"age":18,
"birthday":"1999-03-07"
}查询数据
1
2
3
4// 查询所有的数据
POST /test2/_search
// 查询指定的
GET /test2/_doc/3更新数据
1
2
3
4
5
6PUT /test2/_doc/5
{
"name":"王国副",
"age":18,
"birthday":"1999-03-07"
}
集成springboot
导入依赖
1 |
|
配置配置类
1 |
|
测试
1 |
|
获取客户端索引是否存在
1 |
|
添加一个文档
1 |
|
我们可以将需要添加的文档写成一个java实体,然后将 Java实体转换为json然后再放到请求体中
判断文档是否存在
1 |
|
获取文档的相关信息
1 |
|
查询操作
1 |
|
1 |
|
实战
爬虫
数据问题?数据库获取,消息队列获取,都可以成为数据源
爬取数据:获取请求返回的页面信息,筛选出我们想要的数据
jsoup包
导入依赖
写一个爬取网页的工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55package com.dean.deanjd.utils;
import com.dean.deanjd.pojo.Content;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Component
public class HtmlPraseUtils {
public static void main(String[] args) throws IOException {
new HtmlPraseUtils().PraseJD("java").forEach(System.out::println);
}
public List<Content> PraseJD(String keywords) throws IOException {
HashMap<String, String> header = new HashMap<>();
header.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9");
header.put("Accept-Language", "zh-cn,zh;q=0.5");
header.put("Accept-Charset", "GB2312,utf-8;q=0.7,*;q=0.7");
header.put("Connection", "keep-alive");
header.put("cookie","qrsc=3");
header.put("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36");
// 构建url
String url = "https://search.jd.com/Search?keyword=" + keywords;
// 解析网页 (jsoup返回的Document就是浏览器的document对象)
// 直接请求会跳转至登录界面因为请求头里没有cookie或者token请求会被拦截
Document document = Jsoup.connect(url).headers(header).get();
Element jGoodsList = document.getElementById("J_goodsList");
// 获取所有的li标元素
Elements elements = jGoodsList.getElementsByTag("li");
// 获取元素中的内容,这里的el就是每一个li标签了
ArrayList<Content> goodList = new ArrayList<>();
for (Element el : elements) {
// 关于这种图片特别多的网站,所有的图片都是延迟加载的
String img = el.getElementsByTag("img").eq(0).attr("data-lazy-img");
String price = el.getElementsByClass("p-price").eq(0).text();
String title = el.getElementsByClass("p-name").eq(0).text();
Content content = new Content();
content.setImg(img);
content.setPrice(price);
content.setTitle(title);
goodList.add(content);
}
return goodList;
}
}说一下这里需要注意的问题
- 现在的网页想要请求服务器,后端服务器会对请求先进行拦截,看你有没有携带cookie或者token,如果没有cookie或者token直接拒绝访问请求,要求重新登录。所以这里有的时候是不能直接进行connect的有的时候要设置头部信息
- 一般有大量图片的网站,他们的图片都不是直接加载的,都是懒加载,所以想要获取图片的时候要注意它对应的属性
- 通过connect获得页面后我们就可以像js一样操作页面了
- getElementsByTag获取指定的标签
- getElementById获取指定id的元素
- el.getElementsByTag(“img”).eq(0).attr(“data-lazy-img”),意思是获取img标签里的data-lazy-img的属性值
- el.getElementsByClass(“p-price”).eq(0).text();获取类p-price的文字,eq(0)代表第一个
编写服务层将爬取到的数据插入到es
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 把解析的数据放到es中,并返回是否插入成功
public Boolean praseContent(String keywords) throws IOException {
// 解析数据
List<Content> contentList = htmlPraseUtils.PraseJD("java");
BulkRequest bulkRequest = new BulkRequest();
bulkRequest.timeout("2m");
for (Content content : contentList) {
bulkRequest.add(
// 需要操作的数据库
new IndexRequest("good_list")
// 往数据库插入的内容
.source(JSON.toJSONString(content), XContentType.JSON)
);
}
// 执行请求
BulkResponse responses = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
return !responses.hasFailures();
}查找数据
前面我们已经爬取到了数据,并 把数据存到了es里面,现在我们要从es里面是把数据读出来
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29指定搜索哪个索引
SearchRequest searchRequest = new SearchRequest("good_list");
// 新建一个搜索
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 构造搜索条件
TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("title", keywords);
// 指定搜索的分页
searchSourceBuilder.from(pageNum);
searchSourceBuilder.size(pageSize);
// 根据查询条件构造出一个完整的查询
searchSourceBuilder.query(termQueryBuilder);
searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
// 把这个完整的搜索和索引结合起来
searchRequest.source(searchSourceBuilder);
// 客户端执行搜索
SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);
ArrayList<Map<String, Object>> list = new ArrayList<>();
// 获取返回的结果
for (SearchHit hit : searchResponse.getHits().getHits()) {
list.add(hit.getSourceAsMap());
}
return list;
}前端调用后端es查询的接口,前端拿到数据后直接把数据渲染到模板上去。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!