Elasticsearch 分布式 可扩展 是一个分布式RESTful搜索和分析引擎 简称ES,它与Solr都是建立在 Apache Lucene™ 基础上的搜索引擎。
它可以帮助你用前所未有的速度去处理大规模数据。Elasticsearch允许您以任何方式执行和组合多种类型的搜索
- 结构化,非结构化,地理位置,度量标准。
ES国内外使用优秀案例
1) 2013年初,GitHub抛弃了Solr,采取ElasticSearch 来做PB级的搜索。 “GitHub使用ElasticSearch搜索20TB的数据,包括13亿文件和1300亿行代码”。
2)维基百科:启动以elasticsearch为基础的核心搜索架构。
3)SoundCloud:“SoundCloud使用ElasticSearch为1.8亿用户提供即时而精准的音乐搜索服务”。
4)百度:百度目前广泛使用ElasticSearch作为文本数据分析,采集百度所有服务器上的各类指标数据及用户自定义数据,通过对各种数据进行多维分析展示,辅助定位分析实例异常或业务层面异常。目前覆盖百度内部20多个业务线(包括casio、云分析、网盟、预测、文库、直达号、钱包、风控等),单集群最大100台机器,200个ES节点,每天导入30TB+数据。
**
最全Elasticsearch学习
**
1.相关概念
Lucene:是一个开放源代码的全文检索引擎工具包,功能强大,接入复杂。
Solr:是一个高性能,采用Java5开发,基于Lucene的全文搜索服务器。
Elasticsearch:是一个分布式,基于Lucene的全文搜索服务器。
ES首先使用场景: 记录与日志分析和数据可视化(ELK)、全文检索、高使用频率、多条件组合、及时响应等。
ES基础概念:
node: 节点,它是作为群集一部分的单个服务器,存储数据并参与群集的索引和搜索功能,形成集群的每个服务器称为节点。
cluster: 集群,由多个节点组成的架构。
index: 索引,相当于关系型数据库的database。
shard: 索引分片,索引可以设置多个分片(可以把一个分片放到一个节点上进行性能的优化)。
replicas: 索引副本,ES创建索引默认5个分片和1个副本,1个副本是对5个分片分别进行1个副本备份(5个分片也就5个备份)。
type: 类型,相当于关系型数据库的table。
doucment: 文档,相当于关系型数据库表中的行,而列对应的就是ES的字段。
Elasticsearch优点:
1)分布式实时文件存储,可将每一个字段存入索引,使其可以被检索到。
2)实时分析的分布式搜索引擎。
分布式:索引分拆成多个分片,每个分片可有零个或多个副本。集群中的每个数据节点都可承载一个或多个分片,并且协调和处理各种操作;
负载再平衡和路由在大多数情况下自动完成。
3)可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据。也可以运行在单台PC上(已测试)
4)支持插件机制,分词插件、同步插件、Hadoop插件、可视化插件等。
Elasticsearch缺点:
只有一名开发者(当前Elasticsearch GitHub组织已经不只如此,已经有了相当活跃的维护者)
还不够自动(不适合当前新的Index Warmup API)
这里网上搜集下Elasticsearch与Solr的比较,让大家有个基本认识区分
1 ES 最大的特点就是支持分布式。
2.在搜索数据中单纯的对已有数据进行搜索时,Solr更快。
3.ES在实时建立索引时, Elasticsearch具有明显的优势,Solr会产生io阻塞,查询性能较差。
4.随着数据量的增加,Solr的搜索效率会变得更低,而Elasticsearch却没有明显的变化。
2.安装
自己查阅安装(很简单,到处都有就不说了)
3.相关语法
命令分类
索引管理:
创建、配置(type)、删除索引(Index)
文档管理:
创建、修改、删除文档(doucment)
数据查询:
空查询、指定索引查询,指定类型查询、请求体查询(过滤查询,匹配度查询)
提示 理解完ES的语法结构,自己在去学习ES JAVA API的操作就很明白。 | |
---|---|
ES请求方式:
在请求中以JSON数据格式发送请求,ES的数据返回也是一个JSON格式(请看下列语法)。
GET(查询)、 POST(查询,新增,修改)、 PUT(新增,修改) 、 DELETE(删除)
通常规范可以用GET来查询,PUT来修改,POST来新增,DELETE来删除。(后面会解释)
注:关键字请求语句虽然是模拟HTTP关键字的请求模式,但是不要混为一谈。
在关键字GET中,发送请求并不是以一个get协议去发送一个请求,实际上内部还是以一个POST请求发送。(知道这么回事就OK!)路径访问拼接: ——
ES/index/type/doucment
ES 提供 _search端点访问ES服务器(如_cat还有很多很多,对于我们现在使用意义不大,具体查看ES官方文档,这里针对讲解6.0以上的版本)
例如命令: GET /_search 或者 GET /movies/_search
http://localhost:9200/_search - 搜索所有索引和所有类型。
http://localhost:9200/movies/_search - 在电影索引中搜索所有类型
http://localhost:9200/movies/movie/_search - 在电影索引中显式搜索电影类型的文档。
一 【索引管理】:创建、配置(type)、删除索引(Index)
索引管理-创建索引
语法:
PUT /索引名称
{
“settings”:{
“number_of_shards”:分片数目,
“number_of_replicas”:副本数量
},
“mappings”:{…}
}
注:
1.不配置参数也可以创建,创建索引默认5个分片一个副本.(PUT /索引名称 {})
2.索引名称必须小写,不能出现下划线开头,不能使用逗号。
3.索引的创建只能用PUT。在ES中,路径映射对“/”做了处理(一级目录前索引名称的“/”可以省略,为了规范我们统一都加上)
索引管理-配置索引
语法:
PUT /索引名称
{
“settings”:{
“number_of_shards”:5, //5个分片数目
“number_of_replicas”:0 //不要副本
},
“mappings”:{
“type名称”:{
“properties”:{
“字段名称1”:{
“type”:“字段1类型”, //ES字段类型后面列出来
“analyzer”:“分词器类型” //分词器后面列出来
},
“字段名称2”:{
“type”:“字段2类型”,
“analyzer”:“分词器类型”
}
}
}
}
}
比如:
#kibana dev tools配置索引
PUT /sms
{
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
},
"mappings": {
"course":{
"properties":{
"id":{
"type":"keyword"
},
"courseName":{
"type":"text"
},
"title":{
"type":"text",
"analyzer": "ik_max_word"
}
}
}
}
}
注:1.建议在创建索引时,最好创建type,定义分词器。(type不指定语义也能通过) 2.在集群环境中 每台集群需要安装IK分词器,每台机子需要重启ES才能生效
索引管理-删除索引
语法:
DELETE /索引名称
注:通过GET /_cat/indexs/?v 查看删除的索引名称("/?v"开启详细输出)
1.ES字段类型
注:ES5.0之后数据类型有所变化,主要是对字符串类型进行了细分。
string变成了text、keyword
2.分词器类型
注:分词器主要对text类型进行拆分,然后进行关键字匹配。(不同分词器拆分策略不一样) 常用IK分词器
es自带了许多内置的Analyzer分析器,无需配置就可以直接在index中使用比如:
标准分词器(standard):不支持中文,根据Unicode文本分割算法。它会移除大部分的标点符号,小写分词后的term,支持停用词。
简单分词器(simple):不支持中文,该分词器会在遇到非字母时切分字符串,小写所有的term。
空格分词器(whitespace):不支持中文,遇到空格字符时切分字符串, 停用词分词器(stop):类似简单分词器,同时支持移除停用词。
关键词分词器(keyword):无操作分词器,会输出与输入相同的内容作为一个single term。
语言分词器(language):支持许多基于特定语言的分词器,比如德语,俄语,日语等等
签名分词器(fingerprint):是一个专家分词器,会产生一个签名,可以用于去重检测。
自定义分词器(custom):很多分词器无法满足,不支持中文分词器,可以自定义IK分词器。
二 【文档管理】:创建、修改、删除文档
****文档管理-新增文档++修改文档****
PUT /索引名称/类型名称/文档ID
{
"file_name":"file_value",
...
}
或
POST /索引名称/类型名称/文档ID
{
"file_name":"file_value",
...
}
注:
1.指定索引,类型名称和文档ID情况下,PUT和POST语法执行效果一样(ID存在两个也都会修改)。
2.修改:在ID存在情况下,执行会进行修改操作(在es系统中,他的修改其实是,旧数据
标记删除,新数据通过新增)这样速度会快很多,具体在去研究。
3.PUT和POST区别在于:
PUT必须指定文档ID,不然语义错误,而POST可以不用指定文档ID,会自动生成一串ID字符。
同时POST在不指定文档ID的情况下,在继续新增,会产生不同的ID字符。
建议:基于这些 我们通常规范可以用GET来查询,PUT来修改,POST来新增,DELETE删除
4.在创建doucment时,如果该index没有定义type (创建索引可以不创建type),es系统
会自动新增对应type。(知道有这么个情况就OK!) 对于大量数据一般都会导入。
文档管理-删除文档
DELETE /索引名称/类型名称/文档ID
三 【数据查询】:空查询、指定索引查询,指定类型查询、请求体查询(过滤查询,匹配度查询)
这里只讲解常用查询(跨度查询,地理查询等不讲解)
语法:
GET /_search //空查询
GET /索引名称/类型名称/文档ID //指定索引查询,指定类型查询
GET /索引名称/类型名称/_search
例如:
GET /_search //查询所有库中的结果
GET /le/user/01 //查询le库中user表下01的字段信息
GET /le/user/_search //查询le库中user下的所有字段信息
查询结果分析(查询之后ES会给你一个JSON的数据结构里面包含信息):
took: 搜索请求耗费时间,单位为毫秒(ms 毫秒,s 秒,m分)。
_shards: 查询中参与分片的总数,以及这些分片成功和失败的个数。
timed_out:表示查询是否超时(?timeout=1ms)
hist:查询出来的结果集,数据格式为JSON格式。
请求体查询(过滤查询,匹配度查询)
拆开两个类:过滤查询,匹配度查询来详细讲解,这样更容易理解查询机制
当然我们要知道在实际运用中,过滤和匹配度查询也是可以组合一起查询的。
请求体查询一般使用query子语句进行实现
过滤查询:对文档进行匹配,不进行打分,条件多为精准查询。效率比较快
(意思为查询条件不是模糊的,没有匹配度的概念,比如性别男或者女)
常用filter + 其他子句进行。
语句:
GET /索引名称/类型名称/文档ID
{
"query":{
"bool":{
"filter":{
"term":{
"sex":1
}
}
}
}
}
文档ID可以不给,这个类似于数据库的主键。
注:这里是一个过滤查询 同时也是个复合查询,bool子句不是唯一的,(后面会说)
通常在query下的子句都会进行打分,filter子句不会进行打分所以嵌套在bool子句的后面进行一
个精准查询条件。
在查询中打分高的数据在前面,低的在后面。
匹配度查询:对文档进行匹配,进行打分,条件多为全文检索查询。
原理基于全文索引,过滤查询也是基于全文索引,对查询文本进行分词,对分词出来的关键字去做索引,然后去匹配。
常用match子句实现
语句:
GET /索引名称/类型名称/文档ID
{
"query":{
"match":{
"name":"刘"
}
}
}
注:字段指定的匹配条件是name字段里面包含"刘"的这个词。
了解查询子句:(注:可以用于精准查询,全文检索查询)
(有很多)常用子句:match 、 term 、terms 、range
match:
match查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
如果你使用 match 查询一个全文本字段,它会在真正查询之前用分析器先分析match一下查询字符:
{ "match": { "tweet": "About" } }
如果用match下指定了一个确切值,在遇到数字,日期,布尔值或者not_analyzed 的字符串时,它将为你搜索你给定的值:
{ "match": { "age": 26 }} { "match": { "date": "2000-01-01" }} { "match": { "public": true }} { "match": { "tag": "full_text" }}
注: 做精确匹配搜索时,你最好用过滤语句(term 、filter),因为过滤语句可以缓存数据,效率更加快。
match查询只能就指定某个确切字段某个确切的值进行搜索,而你要做的就是为它指定正确的字段名以避免语法错误。
term:
term 过滤 term主要用于精确匹配哪些值,比如数字,日期,布尔值,你也可以理解类似于等于
{ "term": { "age": 26 }} { "term": { "date": "2000-01-01" }} { "term": { "public": true }} { "term": { "tag": "full_text" }}
完整的例子, sex字段完全匹配成数据:
{ "query": { "term": { "sex": 1 } } } 注:大家认真看了上面的注解没有(注解有写过,子句:可以用于精准查询,全文检索查询) 在上面完整的例子中term直接在query子句下使用(其实语法是通过的,但是不推荐这么用) 上面也说过在query子句下使用子句查询会对结果进行打分,而term是一个精确 查询,所以打分意义不大,还影响查询效率(知道这么回事就OK)。
terms:
terms 过滤 terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。
如果某个字段指定了多个值,那么文档需要一起去做匹配:{ "terms": { "tag": [ "search", "full_text", "nosql" ] } }
完整的例子,在ES中状态是数字类型的字段,可以不用加" "。:
{ "query": { "terms": { "status": [ "full_text", "nosql" ] } } }
range:
range过滤允许我们按照指定范围查找一批数据:
{ "range": { "age": { "gte": 20, //大于等于(加e就多个等于) "lt": 30 //小于 } } } 注:gt:大于, gte:大于等于, lt :小于, lte:小于等于
一个完整的例子, 请求页面耗时大于1秒的数据,upstream_response_time 是 nginx 日志中的耗时,ES中是数字类型。
{ "query": { "range": { "upstream_response_time": { "gt": 1 } } } }
结合不同的子句进行查询 我们可以叫复合查询,也可以说是组合查询。
复合查询: 同时对多个字段进行匹配检索,并且根据一系列的标准来过滤的复合查询
实现方式: constant_score 、 bool 、dis_max 、function_score 、boosting
深入了解不同的子句,自己查阅官方讲解,这里只介绍bool。
复合查询 自己用的多了就知道怎么回事了,这里简单让大家知道怎么回事。
- bool : must (必须)、must_not(否定)、filter (过滤,准确)、should(影响打分)
must
子句(查询)必须出现在匹配的文档中,并有助于得分。
must_not
子句(查询)不得出现在匹配的文档中。子句在过滤器上下文中执行,这意味着忽略评分并考虑使用子句进行高速缓存。由于忽略了评分,因此0会返回所有文档的分数。filter
子句(查询)必须出现在匹配的文档中。但是不同于
must查询的分数将被忽略。过滤器子句在过滤器上下文中执行,这意味着忽略评分并考虑使用子句进行高速缓存。
should
子句(查询)应出现在匹配的文档中。如果 bool查询位于查询上下文中并且具有mustor filter子句,则bool即使没有
should查询匹配,文档也将与查询匹配。在这种情况下,这些条款仅用于影响分数。如果bool查询是过滤器上下文,
或者既没有must或者filter至少有一个should查询必须与文档匹配,以便与bool查询匹配。可以通过设置minimum_should_match参数来显式控制此行为
。
列举语法:
GET /_search
{
"query":{
"bool":{
"过滤": ----filter{ term,range ...}
"匹配度":----must,should{ match ... }
}
}
}
GET /_search
{
"query": {
"bool": {
"filter": { "term" : { "age" : 1}},
"must": { "match": { "title": "quick" }},
"must_not": { "match": { "title": "lazy" }},
"should": [
{ "match": { "title": "brown" }},
{ "match": { "title": "dog" }}
]
}
}
}
分页查询:
通过from size两个参数(和传统分页差不多,没什么难理解的)
from:分页的开始位置(从0开始)
size:页面大小
比如:第一页取10行数据
如果默认from:0 size:10 就是从每个shard取前10,然后统一排序取1到10
比如:第二页取10行数据,from值以此类推
如果from:10 size:10 那就是每个shard取前20,然后统一排序取11到20。
总结:
ES基础概念:node(节点),cluster(集群),type(类型),doucment(文档),
index(索引),shard(索引分片) replicas(副本)
ES文档三元素:_index + _type + _ID
ES请求拼接:es路径/index/type/doucment
ES常用命令:索引管理 + 文档管理 + 数据查询
ES查询子句{
组合查询:bool(must 、must_not、filter、should)
过滤查询:filter
匹配度:match
子句: term 、terms 、range (嵌套在过滤或匹配度子句下面)
}
查询例子太多了oneself查阅资料(查询就不一一介绍)。
ES java 官方文档:
https://www.elastic.co/guide/en/elasticsearch/client/java-api/6.2/index.html
熟悉以上知识,然后就可以进行JAVA客户端的ES搜索运用啦(这里不详细说)。
1.在项目中引入Maven依赖(选择你用的版本 https://search.maven.org/)
需要注意的是,版本不同连接语法和其他用法也有小的差别
<dependencies> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>transport</artifactId> <version>6.4.2</version> </dependency> </dependencies>
2.通过java api 建立连接,关闭连接
注:9300 为 ES 的默认 Transport 端口,节点和客户端的通讯
@Component @RestController public class EsUtils { private final static Logger logger = LoggerFactory.getLogger(EsUtils.class); private static TransportClient client = null; @Autowired private EsConfig esConfig; public void getConnection() throws UnknownHostException { logger.info("es getConnection start ..."); Settings settings = Settings.builder() .put("cluster.name", esConfig.getClusterName()) .build(); if (client == null) { client = new PreBuiltTransportClient(settings) .addTransportAddress(new TransportAddress(InetAddress.getByName(esConfig.getIp()), esConfig.getPort())); logger.info(BasicRestStatus.OK.toString(),"ElasticsearchClient 连接成功"); } }
3 .请求ES构建
/** * 请求ES构建 * * @param esSettingDto * @return */ public SearchRequestBuilder esBuilder(EsSettingDto esSettingDto) throws Exception { logger.info("es esBuilder start info: " + esSettingDto); SearchRequestBuilder searchRequestBuilder = client.prepareSearch(esSettingDto.getIndexSmsCourse(), esSettingDto.getIndexSmsTeacher()); searchRequestBuilder.setTypes(esSettingDto.getTypeCourseName(), esSettingDto.getTypeTeacherName()); //节点切片之间抓取数据的策略(QUERY_THEN_FETCH :检索然后抓取,QUERY_AND_FETCH :检索并且抓取) searchRequestBuilder.setSearchType(SearchType.QUERY_THEN_FETCH); //是否根据匹配度进行排行 searchRequestBuilder.setExplain(true); return searchRequestBuilder; }
4.新增索引、配置索引 、
//新增索引
public void addIndex() throws IOException{
//通过client调用admin()方法,在调用所有索引 indices(),在这之下可以进行增,删,改
client.admin().indices().prepareCreate("索引名称").get(); //创建一个默认的索引
注:到这里了,自己可以写个mian入口,进行方法测试。查看索引库是否创建成功!
}
//配置索引
public void addIndex() throws IOException {
// 创建一个默认的index
client.admin().indices().prepareCreate("sc_31").get();
//内容构建器(更加直观的构建一个JSON数据格式,以调用方法的形式去构建)
XContentBuilder content = XContentFactory.jsonBuilder()
.startObject()// { "开启对象结构"
.startObject("properties")// "properties" : {
.startObject("name") // "name": {
.field("type", "text")// "type" : "text",
.field("analyzer", "ik_max_word")// "analyzer" : "ik_max_word"
.endObject() // } "结束对象结构"
.startObject("age")
.field("type", "integer")
.endObject()
.startObject("sex")
.field("type", "integer")
.endObject()
.startObject("address") // "address": {
.field("type", "text")// "type" : "text",
.field("analyzer", "ik_max_word")// "analyzer" : "ik_max_word"
.endObject()
.endObject() // }
.endObject();// }
client.admin().indices().preparePutMapping("sc_31")
.setType("user")
//主要设置属性source(source 有四种重载)
//在setSource中使用String 重载方法格式如下(其他重载实现格式自己查询了解,这里只写两种参考)
//XContentType.JSON 是以一个JSON字符串的格式定义
//.setSource("{\"properties\":{\"name\":{\"type\":\"text\",\"analyzer\":\"ik_max_word\",}}}", XContentType.JSON)
//XContentBuilder 重载方法
.setSource(content)
.get();
// 关闭
client.close();
}
// 添加文档
public void addDocument() throws IOException {
XContentBuilder content = XContentFactory.jsonBuilder()
.startObject()
.field("name", "王老五")
.field("age", 88)
.field("sex", 1)
.field("address", "北京市海淀区成府路")
.endObject();
client.prepareIndex("sc_31", "user", "01") // POST sc_31/user/id
.setSource(content)
.get();
client.close();
}
针对JAVA API 就不写了,实现ES集群模式,对于Es搜索功能实现(类似于腾讯课堂),实战代码参考可以找我。