1

ES基础

概念

1.1 ElasticSearh是什么

Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,一个建立在全文搜索引擎 Apache Lucene(TM) 基础上的搜索引擎,被广泛应用于全文搜索

它可以快速、实时地存储、搜索和分析大量数据。

1.2 应用场景

  • 作为海量数据的存储工具(亿级),分布式实时文件存储,并将每一个字段都编入索引,使其可以被搜索。
  • 复杂的数据分析(ELK技术,elasticsearch+logstash+kibana)
  • 分布式搜索引擎。可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据。

1.3 集群结构

假设集群中有3台es,1台主节点,2台从节点,创建索引的时候,定义了为这个索引创建3个分片shard,1个副本replica

  1. 这个索引一共有6个分片,3个shard分片集,每个shard有1个replica副本集,所以一共就有6个分片集
  2. shard和对应的replica是不会在同一台机器上的
  3. 主节点负责节点和索引的增删改查,并不处理文档级别的增删改查

1.4 集群原理

倒排索引

正排索引:通过ID映射到相应的记录。

倒排索引:就是关键词到文档 ID 的映射,每个关键词都对应着一系列的文件,这些文件中都出现了关键词。

DocIdDoc
1谷歌地图之父跳槽 Facebook
2谷歌地图之父加盟 Facebook
3谷歌地图创始人拉斯离开谷歌加盟 Facebook
4谷歌地图之父跳槽 Facebook 与 Wave 项目取消有关
5谷歌地图之父拉斯加盟社交网站 Facebook

倒排索引:用户输入查询 Facebook ,搜索系统查找倒排索引,从中读出包含这个单词的文档,这些文档就是提供给用户的搜索结果。

  • 倒排索引中的所有词项对应一个或多个文档;
  • 倒排索引中的词项根据字典顺序升序排列
WordIdWordDocIds
1谷歌1, 2, 3, 4, 5
2地图1, 2, 3, 4, 5
3之父1, 2, 4, 5
4跳槽1, 4
5Facebook1, 2, 3, 4, 5
6加盟2, 3, 5
7创始人3
8拉斯3, 5
9离开3
104
写入数据
  • 客户端选择一个 node 发送请求过去,这个 node 就是 coordinating node (协调节点)。
  • coordinating node 对 document 进行路由,将请求转发给对应的 node(有 primary shard)。
  • 实际的 node 上的 primary shard 处理请求,然后将数据同步到 replica node
  • coordinating node 如果发现 primary node 和所有 replica node 都搞定之后,就返回响应结果给客户端。

底层原理:

  • 数据先写入内存 buffer,然后每隔 1s,将数据 refresh 到 os cache(操作系统缓存),到了 os cache 数据就能被搜索到(所以我们才说 es 从写入到能被搜索到,中间有 1s 的延迟)。
  • 每隔 5s,将数据写入 translog 文件(这样如果机器宕机,内存数据全没,最多会有 5s 的数据丢失),translog 大到一定程度,或者默认每隔 30mins,会触发 commit 操作,将缓冲区的数据都 flush 到 segment file 磁盘文件中。

总结:

  • es 是准实时的,buffer 中的数据被 refresh 操作刷入 os cache 中,这个数据才可以被搜索到
  • ES是可能会丢失数据的:translog 先写入 os cache ,默认每5 秒刷一次到磁盘,如果此时机器挂了,会丢失 5 秒钟的数据。
读数据
写请求是写入 primary shard,然后同步给所有的 replica shard;读请求可以从 primary shard 或 replica shard 读取,采用的是随机轮询算法。

可以通过 doc id 来查询,会根据 doc id 进行 hash,从而判断出 doc id 要分配到哪个 shard 上面去,即从那个 shard 去查询。

  • 客户端发送请求到任意一个 node,成为 coordinate node
  • coordinate nodedoc id 进行哈希路由,将请求转发到对应的 node,此时会使用 round-robin 随机轮询算法,在 primary shard 以及其所有 replica 中随机选择一个,让读请求负载均衡。
  • 接收请求的 node 返回 document 给 coordinate node
  • coordinate node 返回 document 给客户端。
分片路由方式

分片的路由方式类似于redis的hash槽。

当要插入文档或者查询文档时,这个文档将存到哪个分片,查询的时候到哪个分片中去查询这个文档, 是根据hash取模完成的。

ES高可用
  1. es的分片shard和副本集replica,不会位于同一个es节点(es-node)。
  2. shard会自动把内容复制到replica
  3. es shard和replica的负载均衡路由是有es内部自动实现的,外部用户无感知。
ES扩容

ES扩容过程中,用户无需去关注分片具体是怎么迁移到新es-node的, 整个过程自动完成的。

1.5 设计分析

  1. 商品数据变化时,将变化同步到ES中
  2. 查询商品列表或搜索商品时,从ES中查询数据
  3. 为了提高ES吞吐率和伸缩性以及高可用,需要搭建ES集群架构
  4. 为了方便管理和监控,需要可视化工具 es-head和kibana

使用思路:

  1. 商品数据变化时,将变化同步到es中
  2. 查询商品列表数据或搜索商品时,从es中查询数据
  3. 为了提高es吞吐率和伸缩性以及高可用,我们需要搭建一个es集群架构
  4. 为了方便管理和监控,我们需要可视化工具es-head和kibana

存储概念

ES是面向文档(document oriented)的,它可以存储整个对象或文档(document)。

ES会索引(index)每个文档的内容使之可以被搜索。

ES可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。

文档属性
  • index:索引库,类似于关系型数据库里的“数据库”—它是我们存储和索引关联数据的地方。
  • _type:类型,类似于关系型数据库中表。可以是大写或小写,不能包含下划线或逗号。我们将使用 employee 做为类型名。(ES7以后停用,固定为_doc)
  • _id:与 _index 和 _type 组合时,就可以在ELasticsearch中唯一标识(类似于主键)一个文档。当创建一个文档,你可以自定义 _id ,也可以让Elasticsearch帮你自动生成。
  • _uid:文档唯一标识(type#id)
  • _source:文档原始数据
  • _all:所有字段的连接字符串

ES集群可以包含多个索引(indices)(数据库),每个索引包含多个文档(documents)(行),然后每个文档包含多个字段(Fields)(列)。

Mapping:定义索引下的字段处理规则,即索引如何建立、索引类型、是否保存原始索引 JSON 文档、是否压缩原始 JSON 文档、是否需要分词处理、如何进行分词处理等。

关系数据库(MYSQL)数据库DB表TABLE行ROW列ColumnSchema
Elasticsearch索引库Indices (数据库)类型Types(表)文档Documents(行)字段Fields(列)Mapping

2. 索引创建

创建索引 PUT

请求地址:http://ip:端口/索引名称

注意:索引名称必须小写

示例:http://localhost:9200/goods_list

{
    "settings":{
        "number_of_shards":3,    //分片数量
        "number_of_replicas":1     //副本数量
    },
    "mappings":{
            "properties":{
                "id":{"type":"integer"},
                "goods_name":{"type":"text"},
                "goods_description":{"type":"text"},
                "goods_info":{"type":"text"}
            }
        }
}
添加数据

请求地址:http://ip:端口/索引名/_doc/商品id

注意:

  1. 添加数据时指定的某个字段不存在时,es会自动创建该字段
  2. 指定id,不存在则创建,存在则更新
指定ID创建数据:
PUT /goods_list/_doc/123
{
  "id": "123",
  "goods_name": "测试商品的名称",
  "goods_description": "测试商品的描述",
  "goods_info": "。。。"
}
ES内置ID创建(自动生成_id)
POST  /goods_list/_doc/
{
  "id": "123456",
  "goods_name": "ES内置ID创建数据",
  "goods_description": "使用ES内部进行文档数据ID的生成",
  "goods_info": "。。。"
}

3. 查询数据

  1. 获取索引中所有文档:
GET /goods_list/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "goods_name": "测试商品的名称"
          }
        }
      ]
    }
  }
}
  1. 指定ID查询
GET http://192.168.30.128:9201/goods_list/_doc/123
  1. 带条件查询
GET /goods_list/_search/
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "goods_name": "ES内置ID创建数据"
          } 
        },
        {
        "match" : {
              "goods_description": "ES内部"
          }
        }
      ]
    }
  }
}
  1. 查询部分字段
GET /{index}/{type}/{id}?_source=fullName,email
  1. 只返回文档内容,不要元数据
GET /{index}/{type}/{id}/_source
  1. 批量获取1
GET http://192.168.30.128:9201/_mget
{
    "docs": [
        {
            "_index": "goods_list", 
            "_type": "_doc", 
            "_id": 123
        }, 
        {
            "_index": "goods_list", 
            "_type": "_doc", 
            "_id": 1, 
            "_source": "email,age"
        }
    ]
}
  1. 批量获取2
GET  http://192.168.30.128:9201/goods_list/_doc/_mget
{
    "ids": [
        "123", 
        "1"
    ]
}
  1. 分页搜索
GET crm/_doc/_search?size=5 #查询5条
GET goods_list/_doc/_search?size=5&from=5 #查询6-10
GET goods_list/_doc/_search?size=5&from=10 #查询11-15 
  1. 查询字符串
http://192.168.30.128:9201/goods_list/_doc/_search?q=goods_name:商品

4. 查询条件精讲

精准查询term

查询条件都在路由的_serach下面进行,但可以多个条件进行组织和安排。

term是代表完全匹配,即不进行分词器分析,文档中必须包含整个搜索的词汇。

trem单值

字段只有一个值时候,用term关键词查询。

精准查询,不需要查询进行评分计算。constant_score表示非评分模式。

GET http://192.168.30.128:9200/good_list/_doc/_search 
{
    "query" : {  
        "constant_score" : {  
             "filter" : {  
                "term" : {  
                    "biz_id" : "1909190023901225"  
                }  
            }  
        }  
    }  
}

terms多值

字段有一多个值时候,用terms关键词查询,后跟数组

{
    "query":{
        "terms":{
            "biz_id":["1909190023901225"]
        }
    }
}

constant_score 非评分模式查询:

{  
    "query" : {  
        "constant_score" : {  
             "filter" : {  
                "terms" : {  
                    "biz_id" : ["1909190023901225","e1909190111365113"]  
                }  
            }  
        }  
    }  
}

多个字段查询:

{
    "query": [{
        "term": {
            "biz_id": "1909190023901225"
        }
    }, {
        "term": {
            "name": "sixstar"
        }
    }]
}
匹配查询match
  • match查询的时候,ES会根据你给定的字段提供合适的分析器,而term查询不会有分析器分析的过程
  • match查询相当于模糊匹配,只包含其中一部分关键词就行。
match

进行full text search或者exact value(非string字段或not_analyzed的字段),进行匹配

示例:查询字段merchant_id为2501的值,并按时间倒序

{
   "query": {
     "match": {
       "merchant_id": "2501"
     }
   },
   "sort": [
     {
       "trade_finished_time": {
         "order": "desc"
       }
     }
   ]
}
match_all

匹配所有的, 当不给查询条件时,默认全查

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

同时对查询的关键词,多个字段同时进行匹配,即多个字段是AND的关系

{
    "query":{
        "multi_match":{
            "query":"2501",
            "fields":["merchant_id","_id"]
        }
    }
}

在在fields中,按brandName(品牌名)、sortName(分类名)、productName(商品名)productKeyword(商品关键字),搜索“牛仔 弹力”关键词,brandName源值、拼音值、关键字值都是100分,sortName源值、拼音值80分,productName源值60分,productKeyword值20分,分值由高到低优先级搜索

{
  "query": {
    "multi_match": {
      "query": "牛仔 弹力",
      "fields": [
        "brandName^100",
        "brandName.brandName_pinyin^100",
        "brandName.brandName_keyword^100",
        "sortName^80",
        "sortName.sortName_pinyin^80",
        "productName^60",
        "productKeyword^20"
      ],
      "type": <multi-match-type>,
      "operator": "AND"
    }
  }
}     
match_phrase

match_phrase查询分析文本,并从分析文本中创建短语查询。
类似 match 查询, match_phrase 查询首先将查询字符串解析成一个词项列表,然后对这些词项进行搜索,但只保留那些包含 全部 搜索词项,且 位置与搜索词项相同的文档

如下,查询 quick brown、quick brown fox、 brown fox可以查询到,quick fox 查询不到

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

查询 a,b,a和b之间隔3个字符可以查询到,隔不是3个查询不到

{
    "query":{
        "match_phrase" :{
            "query":"a,b",
            "slop":3
        }
    }
}
bool查询

bool查询包含四种操作符,分别是must,should,must_not,query。它们均是一种数组,数组里面是对应的判断条件

  • must: 必须匹配,与and等价。并且参与计算分值
  • filter: 过滤子句,必须匹配,但不参与计算分值
  • must_not:必须不匹配,与not等价,常过滤子句用,但不参与计算分值
  • should: 选择性匹配,至少满足一条,与 OR 等价。参与计算分值
{  
    "query" : {  
        "filtered" : {  
            "filter" : {  
                "bool" : {  
                    "should" : [  
                        { "term" : {"merchant_id" : 100}},  
                        { "term" : {"pay_type" : "3"}}  
                    ],  
                    "must_not" : {  
                        "term" : {"trade_type" : "2"}  
                    }  
                }  
            }  
        }  
    }  
}
filter查询

过滤器,会查询对结果进行缓存,不会计算相关度,避免计算分值,执行速度非常快。

如下, 查询出statusactive的状态

{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "status": "active"
        }
      }
    }
  }
}

filter也常和range范围查询一起结合使用,range范围可供组合的选项

  • gt : 大于
  • lt : 小于
  • gte : 大于等于
  • lte :小于等于

如下,查询merchant_id值为2501下的交易数据

{
  "query": {
    "bool": {
      "must": {
        "term": {
          "merchant_id": "2501"
        }
      }, 
      "filter": {
        "range": {
          "trade_finished_time": {
            "from": "2019-09-01T00:00:00", 
            "to": "2019-09-30T23:59:59"
          }
        }
      }
    }
  }
}

must下匹配,filter进行过滤,range定义范围

{    
    "query": {    
        "bool": {    
            "must": [    
                {   
                    "match": {   
                        "title": "Search"   
                        }  
                },  
                {   
                    "match": {   
                    "content": "Elasticsearch"   
                    }  
                }  
            ],    
            "filter": [  
                {   
                    "term": {   
                        "status": "1"   
                        }  
                },  
                {   
                    "range": {   
                        "publish_date": {   
                        "gte": "2015-01-01"   
                        }  
                    }  
                }  
            ]  
        }  
     }  
} 
常见查询场景

1、查询商户ID为3582,订单号为360102199003072618,按时间范围过滤,按下单时间倒序,每次查询100条

{
    "query": {
        "bool": {
            "must": [{
                "term": {
                    "merchant_id": "3582"
                }
            }, {
                "term": {
                    "order_num": "360102199003072618"
                }
            }],
            "filter": [{
                "range": {
                    "order_time": {
                        "from": "2019-11-01T17:00:00+08:00",
                        "to": "2019-11-01T20:00:00+08:00"
                    }
                }
            }]
        }
    },
    "size": 100,
    "sort": [{
        "order_time": "desc"
    }]
}

5. 修改数据

POST /goods_list/_doc/123/_update

{
"doc": {
      "goods_name": "测试商品的名称Winner"
  }
}

6. 删除数据

DELETE  /goods_list/_doc/123

IT小马
1.2k 声望166 粉丝

Php - Go - Vue - 云原生