分布式搜索引擎-ES

2021/06/19

分布式搜索引擎-ES

参考资料

一、ES安装部署

ElasticSearch是一个基于Lucene的搜索服务器。它提供了一 个分布式多用户能力的全文搜索引擎,基于RESTful web接口。
Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。
设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

架构图:

1.1 win单机部署ES

1.2 linux单机部署ES

1.3 linux集群部署ES

二、Elasticsearch权威指南-学习

2.1 入门

1.Elasticsearch不仅仅是基于Lucene的全文搜索,我们还能这样去描述它:

  • 分布式的实时文件存储,每个字段都被索引并可被搜索
  • 分布式的实时分析搜索引擎
  • 可以扩展到上百台服务器,处理PB级结构化或非结构化数据

2.与Elasticsearch交互
如何与Elasticsearch交互取决于你是否使用Java。 Elasticsearch为Java用户提供了两种内置客户端:

  • 节点客户端(node client): 节点客户端以无数据节点(none data node)身份加入集群,换言之,它自己不存储任何数据,但是它知道数据在集群中的具体位置,并且能够直接转发请求到对应的节点上。
  • 传输客户端(Transport client): 这个更轻量的传输客户端能够发送请求到远程集群。它自己不加入集群,只简单转发请求给集群中的节点。

两个Java客户端都通过9300端口与集群交互,使用Elasticsearch传输协议(Elasticsearch Transport Protocol)。集群中的节点之间也通过9300端口进行通信。如果此端口未开放,你的节点将不能组成集群。

3.基于HTTP协议,以JSON为数据交互格式的RESTful API
其他所有程序语言都可以使用RESTful API,通过9200端口的与Elasticsearch进行通信

4.向Elasticsearch发出的请求的组成部分与其它普通的HTTP请求是一样的:

curl -X<VERB> '<PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>' -d '<BODY>'
VERB HTTP方法:GET, POST, PUT, HEAD, DELETE
PROTOCOL http或者https协议(只有在Elasticsearch前面有https代理的时候可用)
HOST Elasticsearch集群中的任何一个节点的主机名,如果是在本地的节点,那么就叫localhost
PORT Elasticsearch HTTP服务所在的端口,默认为9200
PATH API路径(例如_count将返回集群中文档的数量),PATH可以包含多个组件,例如_cluster/stats或者_nodes/stats/jvm
QUERY_STRING 一些可选的查询请求参数,例如?pretty参数将使请求返回更加美观易读的JSON数据
BODY 一个JSON格式的请求主体(如果请求需要的话)
示例:curl -XGET 'http://localhost:9200/_count?pretty' -d '
	    {   "query": {   "match_all": {}   }  }

5.返回响应
Elasticsearch返回一个类似200 OK的HTTP状态码和JSON格式的响应主体(除了HEAD请求)。上面的请求会得到如下的JSON格式的响应主体:

6.Elasticsearch是面向文档(document oriented)的
Elasticsearch是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document)。然而它不仅仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在Elasticsearch中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。这种理解数据的方式与以往完全不同,这也是Elasticsearch能够执行复杂的全文搜索的原因之一。

7.对比关系数据库
在Elasticsearch中,文档归属于一种类型(type),而这些类型存在于索引(index)中,我们可以画一些简单的对比图来类比传统关系型数据库:

8.curl方式新建索引,新建type,插入,查询(ID查,条件查,无条件查),删除,修改 记录

9.使用DSL语句查询
查询字符串搜索便于通过命令行完成特定(ad hoc)的搜索,但是它也有局限性(参阅简单搜索章节)。
Elasticsearch提供丰富且灵活的查询语言叫做DSL查询(Query DSL),它允许你构建更加复杂、强大的查询。
DSL(Domain Specific Language特定领域语言)以JSON请求体的形式出现。

10.ID搜索、简单搜索、查询字符串搜索、DSL查询(Query DSL)、过滤器(filter)、全文搜索、短语搜索

11.聚合(aggregations)
Elasticsearch有一个功能叫做聚合(aggregations),它允许你在数据上生成复杂的分析统计。它很像SQL中的GROUP BY但是功能更强大

2.2 分布式集群

1.节点node/集群cluster
一个节点就是一个Elasticsearch实例,而一个集群由一个或多个节点组成,它们具有相同的cluster.name

  • 集群:由一个或者多个节点组成,共同持有整个数据,并一起提供索引以搜索功能
  • 节点:一个节点是一个集群中的一个服务器

2.主节点(master)
集群中一个节点会被选举为主节点(master),它将临时管理集群级别的一些变更,例如新建或删除索引、增加或移除节点等。主节点不参与文档级别的变更或搜索,任何节点都可以成为主节点

3.修改集群名(默认elasticsearch,集群名相同即在同一集群中,数据共享)

4.集群健康值

  • green:一切正常
  • yellow:所有的数据都是可用的,某些副本没有被分配
  • red:某些数据不可用,集群是部分可用,会造成数据丢失

5.分片和复制

  • 分片(shard):将索引划分成多分份的能力,这些份即分片。因为在一个索引可以存储超出单个节点硬件限制的数据。 1)允许水平分割/扩展容量,2)在分片上进行分布式并发操作,提高性能/吞吐量
  • 复制(副本)replicas:创建分片的一份或者多分copy,防止分片或者节点出故障, 1)复制分片和主分片不在同一个节点上.2)搜索可以在所有的复制上运行,提高了搜索量和吞吐量

ps: 创建一个索引,默认生成5个分片,1个副本

2.3 数据

索引(类似库的概念)和文档类型(表的概念)和文档(记录):

  • 文档: 文档是一个可被索引的基础信息单元,json格式{name:“jack”}, 一个索引中可以定义多种文档类型 。
  • 文档类型: 是索引的一个上的分类,把具有一组共同字段的文档定义为一个类型
  • 索引:是ElasticSearch中存储数据的一种逻辑结构,是拥有几分相似特征的文档的集合, 索引名全部小写

增删改查:

  • 搜索数据:curl -XGET http://localhost:9200/wds/employee/1
  • 简单搜索数据(默认10条):curl -XGET curl XGET http://localhost:9200/wds/employee/_search
  • 条件查询:curl XGET http://localhost:9200/wds/employee/_search?q=last_name:Fir
  • 增加数据:curl XPUT http://localhost:9200/wds/employee/1 {…}
  • 删除数据:curl -XDELETE http://localhost:9200/wds/employee/1
  • 更新数据:curl XPUT http://localhost:9200/wds/employee/1 {…}
  • 查询所有
curl -XGET -u user:pwd 'ip:9200/index_name/_search' -H 'Content-Type: application/json' -d '
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      }
    }
  }
}'
  • 条件检索:接口日志分析–查询/hello接口日志,存储在message字段中
curl -XGET -u user:pwd 'ip:9200/index_name/_search' -H 'Content-Type: application/json' -d '
{
  "query": {
    "multi_match": {
      "query": "/hello",
      "fields": [
        "message"
      ]
    }
  }
}'

curl -XGET -u elastic:elastic 'ip:9200/index_name/_search' -H 'Content-Type: application/json' -d '  
{
  "query": {
    "bool": {
      "must": [
        {
          "match_phrase": {
            "message": {
              "query": "enterprise.demo.com",
              "slop": 0,
              "zero_terms_query": "NONE",
              "boost": 1
            }
          }
        },
        {
          "range": {
            "@timestamp": {
              "from": "2022-08-16 17:23:00.002",
              "to": "2022-08-16 17:24:00.002",
              "include_lower": true,
              "include_upper": false,
              "time_zone": "+08:00",
              "format": "yyyy-MM-dd HH:mm:ss.SSS",
              "boost": 1
            }
          }
        }
      ],
      "must_not": [
        {
          "match_phrase": {
            "message": {
              "query": "url:/hello1",
              "slop": 0,
              "zero_terms_query": "NONE",
              "boost": 1
            }
          }
        },
        {
          "match_phrase": {
            "message": {
              "query": "url:/hello2",
              "slop": 0,
              "zero_terms_query": "NONE",
              "boost": 1
            }
          }
        }
      ],
      "adjust_pure_negative": true,
      "boost": 1
    }
  }
}'
  • 删除索引
curl  -X DELETE -u user:pwd 'ip:9200/index_name'
  • 局部更新
  • Mget:(multi-get) 一次请求搜索多个文档
  • 批量 :bulk API允许我们使用单一请求来实现多个文档的create、index、update或delete
  • https请求查询
curl -k -XGET -u username:pwd 'https://192.168.0.1:9200/indexname/_search' -H 'Content-Type: application/json' -d '
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      }
    }
  }
}'

2.4 分布式增删改查

  • 1.路由
  • 2.分片交互
  • 3.新建、 索引和删除
  • 4.检索
  • 5.局部更新
  • 6.批量请求
  • 7.批量格式

2.5 搜索

  • 1.空搜索
  • 2.多索引和多类型
  • 3.分页
  • 4.查询字符串

2.6 映射和分析

  • 1.数据类型差异
  • 2.确切值对决全文
  • 3.倒排索引
  • 4.分析
  • 5.映射
  • 6.复合类型

2.7 结构化查询

1.请求体查询:非简单是查询字符串查询,请求体即花括号形式:{“json”}

2.结构化查询:使用DSL语句查询

3.查询与过滤: 查询与过滤语句非常相似,但是它们由于使用目的不同,
查询是为了找到文档,计算相关性, 过滤的目的就是缩小匹配的文档结果集,所以需要仔细检查过滤条件。
使用查询语句做全文本搜索或其他需要进行相关性评分的时候,剩下的全部用过滤语句

4.重要的查询子句:做精确匹配搜索时,最好用过滤语句,因为过滤语句可以缓存数据。

1).term过滤用于精确匹配哪些值,如数字,日期,布尔值或 not_analyzed的字符串(未经分析的文本数据)
{ "term": { "age":26}} ,{ "term": { "date" : "2014-09-01" }} ,{ "term": { "public": true }} ,{ "term": { "tag": "full_text" }}

2).terms 过滤跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值
{ "terms": { "tag": [ "search", "full_text", "nosql" ] } }

3).range过滤允许我们按照指定范围查找一批数据:
{ "range" : { "age" : { "gte" :  20, "lt" : 30 }}} 	/gt ::大于,  gte: 大于等于, lt : 小于, lte: 小于等于

4).exists 和 missing 过滤可用于查找文档中是否包含指定字段或没有某字段,类似SQL语句中IS_NULL条件
{"exists" :  { "field" : "title" }}	     //这两过滤只针对已经查出一批数据来,但想区分出某个字段是否存在的时使用。

5)bool 过滤可以用来合并多个过滤条件查询结果的布尔逻辑,包含must,must_not,should操作

6).match_all查询 ,可以查询到所有文档,是没有查询条件下的默认语句
{ "match": {  "tweet": "About Search"   } }

7).match 查询一个全文本字段,它会在真正查询之前用分析器先分析match一下查询字符

8).multi_match查询允许你做match查询的基础上同时搜索多个字段
{  "multi_match": { "query":    "full text search",   "fields":   [ "title", "body" ] } }

9).bool 查询与 bool 过滤相似,用于合并多个查询子句。不同的是,bool 过滤可以直接给出是否匹配成功, 而bool 查询要计算每一个查询子句的 _score (相关性分值)。bool 查询中必须有must或should其一

5.过滤查询:“带过滤的查询”,“查询中的过滤”

2.8 排序

  • 1.排序
  • 2.字符串排序
  • 3.相关性
  • 4.字段数据

2.9 分布式搜索

  • 1.查询阶段
  • 2.取回阶段
  • 3.搜索选项
  • 4.扫描和滚屏

2.10 索引管理

  • 1.创建删除
  • 2.设置
  • 3.配置分析器
  • 4.自定义分析器
  • 5.映射
  • 6i.根对象
  • 7.元数据中的source字段
  • 8.元数据中的all字段
  • 9.元数据中的ID字段
  • 10.动态映射 11.自定义动态映射
  • 12.默认映射
  • 13.重建索引
  • 14.别名

2.11 深入分片

1.使文本可以被搜索 2.动态索引 3.近实时搜索 4.持久化变更 5.合并段

2.12 结构化搜索

  • 1.查询准确值
  • 2.组合过滤
  • 3.查询多个准确值
  • 4.包含, 而不是相等
  • 5.范围
  • 6.处理 Null 值
  • 7.缓存
  • 8.过滤顺序

2.13 全文搜索

1.匹配查询:match是一个高级查询,主要用于全文搜索, Elasticsearch通过下面的步骤执行match查询

  • 1).检查field类型:title字段是一个字符串(analyzed),所以该查询字符串也需要被分析(analyzed)
  • 2).分析查询字符串 :查询词QUICK!经过标准分析器的分析后变成单词quick。因为我们只有一个查询词,因此match查询可以以一种低级别term查询的方式执行。
  • 3).找到匹配的文档 :term查询在倒排索引中搜索quick,并且返回包含该词的文档。
  • 4).为每个文档打分 :term查询综合考虑词频(每篇文档title字段包含quick的次数)、逆文档频率(在全部文档中title字段包含quick的次数)、包含quick的字段长度(长度越短越相关)来计算每篇文档的相关性得分_score。(更多请见相关性介绍)

2.多词查询:通过 “operator”: “and” //或”or” “minimum_should_match”: “75%” //控制精度

3.组合查询:过滤器会判断: 是否应该将文档添加到结果集? 查询还会计算文档的相关性(relevant).布尔查询接受多个用must, must_not, and should的查询子句

4.布尔匹配: 5.增加权重子句: 6.控制分析: 7.关联失效:

2.14 多字段搜索

1.多重查询字符串:bool->should内含多个:match,或者再嵌套bool->should形式,加boost权重

2.单一查询字符串:

3.最佳字段: “dis_max”: {…}

4.最佳字段查询调优:”tie_breaker”: 0.3,tie_breaker的取值范围是0到1之间的浮点数,取0时即为最佳匹配

5.多重匹配查询:multi_match对多个字段执行相同的查询

6.最佳字段、最多字段查询、跨字段查询

multi_match:{
"query" : "jack smith",
"type" : "best_filed", //best_fileds最佳字段查询, most_fields是最多字段查询,cross_fields跨字段匹配查询
"fields" : ["first_name", "last_name"]
} 

8.以字段为中心查询

9.全字段查询 :“copy_to”

2.15 模糊匹配

2.16 关系

2.17 嵌套

三、ES实战

3.1 应用场景

ES的主要应用分为两大类:

  • 搜索类(带上聚合):考虑事务性,频繁更新,与现有数据库进行同步,通过ES进行查询聚合。
  • 日志类
    • 日志收集ELK:通过beats等工具收集到kafka等Q中,通过logstash进行转换,输送到ES中,然后通过Kibana进行展示
    • 指标性收集:通过fluent-bit收集nginx日志输出到ES,然后进行指标性数据查询分析

3.2 springboot使用ES示例

// 1.config类
@Configuration
public class RestHighLevelClientConfig {
    @Value("${es.user}")
    private String user;
    
    @Value("${es.password}")
    private String password;
    
    @Value("${es.host}")
    private String host ;
    
    @Value("${es.port}")
    private int port;
    @Bean
    public RestHighLevelClient client() {
        RestHighLevelClient restClient = null;
        try {
            final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(AuthScope.ANY,
                    new UsernamePasswordCredentials(user, password));
        
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                // 信任所有
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();

            SSLIOSessionStrategy sessionStrategy = new SSLIOSessionStrategy(sslContext, NoopHostnameVerifier.INSTANCE);

            restClient = new RestHighLevelClient(
                    RestClient.builder(
                            new HttpHost(host, port, "https"))
                            .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                                @Override
                                public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                                    httpClientBuilder.disableAuthCaching();
                                    httpClientBuilder.setSSLStrategy(sessionStrategy);
                                    httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                                    return httpClientBuilder;
                                }
                            }));
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return restClient;
    }
}

//2.查询使用
@Component
@Slf4j
public class ElasticSearchService {
    private final String indices = "indexName";

    @Autowired
    private RestHighLevelClient client;
    
    @Value("${es.rest.page.size:20}")
    private  Integer pageSize;
    
    public SearchResponse searchApiCallRecords(Scroll scroll) {
        // 创建search请求,索引 indexName
        SearchRequest searchRequest = new SearchRequest(indices);
    
        // scroll 分页
        searchRequest.scroll(scroll);
        
        // 用SearchSourceBuilder来构造查询请求体 ,请仔细查看它的方法,构造各种查询的方法都在这。
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.size(pageSize);
        
        //构造QueryBuilder--5分钟内,通过web登录,并且非phone登录127.0.0.1的记录,检索字段 "@timestamp"、"message"的值
        LocalDateTime end = LocalDateTime.now();
        LocalDateTime start = end.plusMinutes(- 5);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
        QueryBuilder time = QueryBuilders.rangeQuery("@timestamp").gte(formatter.format(start)).lt(formatter.format(end)).format("yyyy-MM-dd HH:mm:ss.SSS").timeZone("+08:00");
        MatchPhraseQueryBuilder host = QueryBuilders.matchPhraseQuery("message", "127.0.0.1");
        MatchPhraseQueryBuilder loginWeb = QueryBuilders.matchPhraseQuery("message", "url:/loginWeb");
        MatchPhraseQueryBuilder loginPhone = QueryBuilders.matchPhraseQuery("message", "url:/loginPhone");
        QueryBuilder queryCriteria = QueryBuilders.boolQuery().must(host).must(loginWeb).must(time).mustNot(loginPhone);
        sourceBuilder.query(queryCriteria);
        searchRequest.source(sourceBuilder);
    
        SearchResponse response = null;
        try {
            log.info("开始es查询,查询参数: {}",  searchRequest.source());
            response = client.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("elasticsearch 请求失败", e);
            throw  new RuntimeException(e);
        }
        
        // 解析结果
        return this.checkResponse(response);
    }
    
    public SearchResponse scrollApiCallRecords(String scrollId, Scroll scroll) {
        SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
        searchScrollRequest.scroll(scroll);
        SearchResponse response = null;
        try {
            response = client.scroll(searchScrollRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("elasticsearch 请求失败", e);
            throw  new RuntimeException(e);
        }
        return this.checkResponse(response);
    }
    private SearchResponse checkResponse(SearchResponse response) {
        if (RestStatus.OK.equals(response.status())) {
            return response;
        } else {
            log.error("elasticsearch response 解析异常 -- {}", JSON.toJSONString(response));
            throw new RuntimeException("elasticsearch response 解析异常");
        }
    }
}

四、常用DSL

0.搜索所有(.select * )

{
  "query": {
    "match_all ": {}
  }
}

1.匹配查询(分析器分词),select * from t where t.tweet = “elasticsearch”

{
  "query": {
    "match": {
      "tweet": "elasticsearch"
    }
  }
}

2.精确匹配、多个精确值匹配(select * from t where name=’zhangsan’ and age=20)
term 查询会查找我们指定的精确值,

{
  "term": {
    "price": 20
  }
}
{
  "must": [
    {
      "term": {
        "tags": "search"
      }
    },
    {
      "term": {
        "tag_count": 1
      }
    }
  ]
}

3.指定多个可能的匹配条件(一个字段多个值select * from t where fieldA in(1,2,3)):
如搜索tag字段值为 A或B或C的记录

{
  "terms": {
    "tag": [
      "A",
      "B",
      "C"
    ]
  }
}

4.搜索多个字段(多个字段一个值):
如在title和body字段中搜索 “jack “

{
  "multi_match": {
    "query": "jack",
    "fields": [
      "title",
      "body"
    ]
  }
}

5.多字符串匹配 :(where fieldA = “A” and fieldB = “B”) (效果同多个term,或者terms)

{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": "War and Peace"
          }
        },
        {
          "match": {
            "author": "Leo Tolstoy"
          }
        },
        {
          "bool": {
            "should": [
              {
                "match": {
                  "translator": "Constance Garnett"
                }
              },
              {
                "match": {
                  "translator": "Louise Maude"
                }
              }
            ]
          }
        }
      ]
    }
  }
}

6.多词查询 (一个字段包含多个词)

7.短语匹配,(分词,但要求结果包含每个词),效果同6

{
  "query": {
    "match_phrase": {
      "title": "quick  brown  fox"
    }
  }
}

8.基于词项的查询、基于全文的查询

  • 基于词项的查询 : 如 term 或 fuzzy 这样的底层查询不需要分析阶段,它们对单个词项进行操作。用 term 查询词项
  • 基于全文的查询:像 match 或 query_string 这样的查询是高层查询,它们了解字段映射的信息:

9.分页,排序,聚合,取字段
GET _search //查询操作

{
	"query" : {	//查询
		"filtered" : {	//过滤
			"filter" : {
				"bool" : { 	   //合并子查询语句
					"must" : [
					  	{ "query" : { "match_phrase" : { "user" : "kimchy" } } } //短语匹配
					],
					"must_not" : [
					  	{ "term"  : { "FIELD" : "VALUE" }},
					  	{ "terms"  : { "FIELD" : [ "VALUE1", "VALUE2" ] } }
					],
					"should": [
					  	{ "term" : { "FIELD" : "VALUE" } },
					  	{ "terms" : { "FIELD" : [ "VALUE1",  "VALUE2" ] } }
					]
				}
			}
		}
	},
	"from": 0,	//分页起点数
	"size": 200,	//每页条数
	"sort": [ ],	 	//排序
  	"aggs": { },	//聚合
	"_source": {
	 	"include": [	//取哪些字段
	   		"user",
	   		"message"
	   	],
	 	"exclude": [	//	不取的字段
	  		"post_date"
	   	]
	}
}

10.控制精度
以下实例查询的是邮件正文中含有“business opportunity”字样的星标邮件或收件箱中正文中含有“business opportunity”字样的非垃圾邮件:

{
  "query": {
    "filtered": {
      "filter": {
        "bool": {
          "must": {
            "match": {
              "email": "business opportunity"
            }
          },
          "should": [
            {
              "match": {
                "starred": true
              }
            },
            {
              "bool": {
                "must": {
                  "folder": "inbox"
                },
                "must_not": {
                  "spam": true
                }
              }
            }
          ],
          "minimum_should_match": 1      //控制精度,指定符合条件数,也可以是百分比
        }
      }
    }
  }
}

11.权重boost

{
  "should": [
    {
      "match": {
        "content": {
          "query": "Elasticsearch", "boost": 3 (2)
        }
      }
    },
    {
      "match": {
        "content": {
          "query": "Lucene", "boost": 2 (3) //增加权重
        }
      }
    }
  ]
}

12.范围 (WHERE price BETWEEN 20 AND 40)、处理null

Post Directory

扫码关注公众号:暂无公众号
发送 290992
即可立即永久解锁本站全部文章