ES 快速入门

1.什么是 Elasticsearch ?
  • 使用 java 语言开发的一套开源的全文搜索引擎
  • 用于搜索、日志管理、安全分析、指标分析、业务分析、应用性能监控等多个领域
  • 底层基于 Lucene 开源库开发,提供 restAPI,可以被任何语言调用
  • 支持分布式部署,可水平扩展
  • 更新迭代快、社区活跃、文档丰富
2.Elasticsearch 的基础概念

集群(Cluster)

  • Elasticsearch 集群部署使其可以随时可用和并按需扩容,并保证数据的安全性
  • 通过启动参数 cluster.name 修改集群名称,默认名称为 elasticsearch
  • 下面我们部署一个集群,包括三个节点,默认是 9200 端口启动,启动后通过 http://127.0.0.1:9200 可以查看运行情况
bin/elasticsearch -E node.name=node1 -E cluster.name=myEs -d
bin/elasticsearch -E node.name=node2 -E cluster.name=myEs -d
bin/elasticsearch -E node.name=node3 -E cluster.name=myEs -d

节点(Node)
一个Elasticsearch集群(下面简称ES集群)是由许多节点(Node)构成的,Node可以有不同的类型,通过以下配置,可以产生四种不同类型的Node:

conf/elasticsearch.yml:
    node.master: true/false
    node.data: true/false
  • 一个节点是一个 Java 进程实例,一台机器可以运行多个实例,一般情况下一台机器只允许一个节点

  • 一个集群有一个或者多个节点

  • 通过启动参数 node.name 定义节点名称

  • 每个节点都保存了集群的状态信息,只有 Master 节点可以修改集群的状态信息

  • 集群状态信息包括:所有节点信息、索引、Mapping、Settings、分片路由等信息

    Master-eligible 节点

    • 每个节点启动,默认自己是一个 Master-eligible 节点
    • 可以通过启动参数 node.master: false 禁止当前启动节点是 Master-eligible 节点
    • 所有 Master-eligible 都可以参与选主流程,成为 Master 节点

    Data 节点:

    • 保存分片数据的节点
    • 在数据扩展上起了很大的作用
    • 通过启动参数 node.data:true 设置

索引(Index)

  • 一个集群下面可以新建多个索引,索引体现了逻辑空间概念
  • 索引是一类相似文档的集合,是文档的容器,类比关系型数据库中的一张表的 Schema 的概念
  • 每个索引有自己的 Mapping 用于定义文档的字段名和字段类型
  • 每个索引有自己的 Settings 用于定义不同的数据分布,也就是索引使用分片的情况

分片(Shard)

  • 分片是物理空间概念,索引中的数据都分布在分片上
  • 一个分片就是运行的一个 Lucene 的实例
  • 分片分为主分片和副分片,一般主分片和副分片应该分布在不同的节点上
  • 主分片用于解决数据水平扩展的问题,主分片的数目在索引创建后指定,后续不容许修改(number_of_shards)
  • 副分片用来解决数据高可用问题,是主分片的拷贝,数量可以动态调整(number_of_replicas)
    分片数的设定
    • 分片数设置太小,影响后续水平扩展,单个分片数据量太大将导致数据重新分配耗时
    • 分片数设置过大,影响搜索结果的相关性打分,影响搜索结果数据准确性
    • 分片数设置过大,导致单个节点上会有过多的分片,资源浪费,浪费性能

文档(Document)

  • 文档是所有可搜索数据的最小单位,类似关系数据库中某张表中的一行记录
  • 文档会被序列化成 JSON 格式,JSON 对象由字段组成
  • 每个字段都有对应的字段类型,类型可以自己指定,也可以使用 ElasticSearch 自动推算
  • JSON 文档支持数组和嵌套
  • 每个文档都有一个唯一性 ID,可以自己指定,也可以系统自动生成
  • 一个文档主要的元信息如下:
    1. _index: 文档所属的索引名
    2. _type: 文档所属的类型名
    3. _id: 文档的唯一ID
    4. _source: 文档存储的 Json 数据
    5. _version:文档的版本信息
    6. _score: 相关性打分
3.Elasticsearch高阶概念

Elasticsearch插件
Elasticsearch 还提供了插件功能,用户可以根据自己的需求安装相应的插件满足搜索,分析,安全,管理,数据备份等功能,比如我们可以安装 elasticsearch-analysis-ik 来满足我们的中文分词功能。

bin/elasticsearch-plugin install 安装插件
bin/elasticsearch-plugin list 展示插件列表
可以访问 localhost:9200/_cat/plugins 接口显示已经安装的插件

Elasticsearch 类比关系型数据库
Elasticsearch 相对关系型数据库,更适合相关性、高性能全文检索,并且支持 restAPI 调用,而关系型数据库更适合事务性要求较强的场景,此外 Elasticsearch 权限管理没有关系数据库那么细。以下是两者概念上的类比:

关系型数据库ElasticSearch
TableIndex
RowDoucment
ColumnField
SchemaMapping
SQLDSL(domain-specific language)

我们一直认为ES中的“index”类似于关系型数据库的“database”,而“type”相当于一个数据表。ES的开发者们认为这是一个糟糕的认识。例如:关系型数据库中两个数据表示是独立的,即使他们里面有相同名称的列也不影响使用,但ES中不是这样的。

我们都知道elasticsearch是基于Lucene开发的搜索引擎,而ES中不同type下名称相同的filed最终在Lucene中的处理方式是一样的。举个例子,两个不同type下的两个user_name,在ES同一个索引下其实被认为是同一个filed,你必须在两个不同的type中定义相同的filed映射。否则,不同type中的相同字段名称就会在处理中出现冲突的情况,导致Lucene处理效率下降。

去掉type能够使数据存储在独立的index中,这样即使有相同的字段名称也不会出现冲突,就像ElasticSearch出现的第一句话一样“你知道的,为了搜索····”,去掉type就是为了提高ES处理数据的效率。

除此之外,在同一个索引的不同type下存储字段数不一样的实体会导致存储中出现稀疏数据,影响Lucene压缩文档的能力,导致ES查询效率的降低。

3.1 倒排索引

Elasticsearch 使用一种称为”倒排索引"的结构,它适用于快速的全文搜索。一个倒排索引由文档中所有不重复词的列表构成,对于其中每个词,有一个包含它的文档列表,这样可以通过某个单词快速的找到其所在的文档:

  • 倒排索引包含两部分:单词词典(Team Dictionary)和倒排列表(Posting List)
  • 单词词典记录单词到倒排列表的关联关系,一般通过 B+ 树或者哈希链表实现
  • 倒排列表记录单词对应的文档结合,由倒排索引项组成
  • 倒排索引项由文档ID(docId),词频(term frequencies),单词位置(term postion),偏移量(character offsets)组成
  • ElasticSearch 中默认会对文档中的每个字段做倒排索引,可以强行指定不对某些字段设置倒排索引
3.2 Analysis
  • Analysis 是把全文本转换为一系列单词的过程,也叫分词,内部通过 Analyzer 分词器实现,可以使用 ElasticSearch 内置的分词器或者定制化分词器
  • 在写入文档数据时,需要对 TEXT 字段做分词然后建立倒排索引;在搜索查询时,需要对输入的查询语句进行分词,然后通过倒排索引进行搜索
  • Analyzer 分词器的组成:
  1. Character Filters: 对原始文本进行预处理,比如去除 HTML,字符串替换,正则匹配替
  2. Tokenizer: 按照规则切分单词,ES 内置的有 whitespace/standard/uax_url_email/pattern/keyword/path hierarchy 等 Tokenizer
  3. Token Filter: 对切分的单词进行加工,例如小写转换(Lowercase),删除 stopwords(stop),增加同义词(synonym)等
  • ElasticSearch 内置分词器有:
  1. Standard Analyzer:默认分词器,按词切换,小写处理
  2. Simple Analyzer: 按照非字母切分,小写处理
  3. Stop Analyzer: 小写处理,停用词过滤
  4. Whitespace Analyzer:按照空格切分,不转换小写
  5. Keyword Analyzer: 不分词,直接将输入当作输出
  6. Patter Analyzer: 正则表达式分词
  7. Language Analyzer:提供 30 多种常见语言的分词器
  8. Customer Analyzer:自定义分词器
  • 常用的中文分词器:
  1. icu_analyzer: bin/elasticsearch-plugin install analysis-icu
  2. ik: bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.3.0/elasticsearch-analysis-ik-6.3.0.zip
  3. thulac: https://github.com/microbun/elasticsearch-thulac-plugin
3.3 normalizer

normaizer 实际上是和analyzer类似,都是对字符串类型的数据进行分析和处理的工具,它们之间的区别是

  1. normalizer只对keyword类型的字段有效
  2. normalizer处理后的结果只有一个token
  3. normalizer只有char_filter和filter,没有tokenizer,也即不会对字符串进行分词处理
3.4 Mapping

Mapping 主要用于定义索引的字段名称和数据类型以及倒排索引等相关配置,Mapping 可以系统自动推断生成,也可以由用户自己定义,下面我们看一个简单 Mapping 的格式

//该索引包含三个字段
//name,类型是 long,不支持索引搜索
//phone,类型是 keyword,对于值为空的情况可以使用"NULL"字符串来搜索
//name,类型是 text,并定义了索引级别,以及自定义的分词器
{
  "mappings" : {
      "properties" : {
        "id" : {
          "type" : "long",
          "index": false
        },
        "name" : {
          "type" : "text",
          "index_options": "positions",
          "copy_to": "fullName",
          "fields": {
            "english_comment":{
              "type": "text",
              "analyzer": "english",
              "search_analyzer": "english"
            }
          }
        },
        "phone" : {
          "type" : "keyword",
          "null_value": "NULL"
        }
      }
    }
}

字段的属性

  • type:字段的数据类型,并没有提供专门数组类型,通过 TEXT 字段实现,Elasticsearch 主要支持以下几种数据类型:

    1. Text:默认情况下会进行分词
    2. Keyword:不会进行分词,全文本匹配
    3. Date:日期类型
    4. Integer/Floating:整数/浮点数
    5. Boolean:布尔类型
    6. IPv4 & IPv6
    7. 特殊类型:geo_point & geo_shape & percolator
  • index:表示该字段是否可以被搜索,是否需要建立倒排索引

  • index_options 属性:控制倒排索引记录的内容,内容越多,占用空间越大,text 类型默认是 positions,其它默认是 docs

    1. docs: 记录 docId
    2. freqs:记录 docId 和 term frequencies
    3. positions:记录 docId 和 term frequencies 和 term postion
    4. offsets:记录 docId 和 term frequencies 和 term postion 和 character offsets
  • null_value:默认情况下 null 是无法被直接搜索的,如果需要对 Null 值进行搜索,可以设置该属性,表示 null 值被当成什么来搜索

  • copy_to:将值拷贝到对应的字段上,多个字段可以同时 copy_to 到同一个字段上,用于对于多个字段同时查询,目标字段不会出现在 mapping 的定义中,但是可以用于搜索

  • fields: 多字段特性,这就相当于实现了 multi-fields。例如,一个 string 类型字段可以被映射成 text 字段作为 full-text 进行搜索,同时也可以作为 keyword 字段用于排序和聚合.

3.5 Index Template

Index template 帮助设定 Mappings 和 Settings,并按照一定的规则,自动匹配到新创建的索引之上,因此只是对于一个新创建的索引才起作用。
创建一个索引时,按照以下顺序配置其 Settings 和 Mappings,后面的覆盖前面的配置

  • ES 默认的 Settings 和 Mappings
  • order 数值低的 Index Template
  • order 数值高的 Index Template
  • 用户对于当前索引指定的 Settings 和 Mappings
PUT _template/test_template
{
  "index_patterns": ["test*"],  // 什么样的 index 会使用这个模板
  "order": 1,                   // 设置模板的优先级
  "settings": { 
    "number_of_shards": 1,      // shard 的数量
    "number_of_replicas": 2     //replication 的数量
  },
  "mappings": {
    "date_detection": false,   //字符串的日期类型是否自动转换
    "numeric_detection": true  //字符串的数字类型是否自动转换
  }
}
3.6 Dynamic Mapping

在写入文档时,如果索引不存在 ES 会自动创建索引,并且会根据用户写入的数据按照上面介绍的逻辑自动推断每个字段的类型和配置,每个索引有一个 dynamic 属性来控制动态推断逻辑,该属性只会对新增数据写入起作用:

  • 当 dynamic 属性 true 时,当有有新字段的文档写入时,Mapping 也会同时被按照推荐自动更新
  • 当 dynamic 属性 false 时,当有有新字段的文档写入时,Mapping 不会被更新,新增字段无法被索引,但是信息会出现在文档的 _source 属性中
  • 当 dynamic 属性是 Strict 时,文档不可以被写入,当然字段也不可以被索引,数据写入时直接报错
  • 对已有字段,一旦有数据写入,就不再支持修改字段定义,因为建立了倒排索引后就不允许修改了,如果希望修改字段类型,必须 Reindex API,重建索引

同时 Elasticsearch 还支持用户定义 Dynamic Template 来通过字段名称来动态设置字段类型:

PUT my_test_index
{
  "mappings":{
    "dynamic_template": {
      "path_match": "name.*",  //字段以 name 开头
      "path_unmatch": "*.middle",  //字段以 middle 结尾
      "mapping": {
        "type": "text",
        "copy_to": "full_name"
      }
    }
  }
}

根据 ES 识别的数据类型,结合字段名称来动态设置字段类型
与 Index Template 不同,Dynamic Template 是定义在某个具体的索引下的

4.常用的 RestAPI
4.1文档的 CURD
  • PUT {index_name}/_create/{id}:增加一条文档记录,必须保证对应 ID 的文档不存在
  • POST {index_name}/_update/{id}:不会删除原先文档,实现真正的数据更新,可以增加字段或者修改某些字段
//对索引 users 中 ID = 1 的文档进行更新操作
POST users/_update/1
{
  "doc": {
    "user": "chenmangmanga",
    "message": "learning elasticSearch",
    "class": "one",
    "age": 30
  }
}
  • PUT {index_name}/_doc/{id}:新增一条文档记录,如果记录已经存在,则删除原先文档,版本信息会加 1

  • POST {index_name}/_doc/:新增一条文档记录,ID 会自动生成

  • GET {index_name}/_doc/{id}:根据 ID 获取某一文档记录

  • DELETE {index_name}/_doc/{id}:根据删除一条文档

  • POST _bulk:文档的批量增删改

    /*
    - 支持在一次 API 调用中对不同的索引进行操作
    - 支持 INDEX/Create/Update/Delete 四种类型的操作
    - 单条记录操作失败,并不会影响其它操作
    - 返回结果中包括了每一条操作执行的结果
    */
    
    POST _bulk
    {"index": {"_index": "users", "_id": "1"}}
    {"user": "yangzhiwei2"}
    {"delete":{"_index": "users", "_id": "2"}}
    {"create":{"_index": "users", "_id": "3"}}
    {"user": "zhuweilin","message": "learning elasticSearch","age": 35}
    {"update":{"_index":"users","_id": "1"}}
    {"doc":{"user": "helloworld"}}
    
  • GET _mget: 批量读取文档数据

    GET _mget
    {
      "docs": [{
        "_index": "users",
        "_id": 1
      },{
        "_index": "users",
        "_id": 3
      },{
        "_index": "users",
        "_id": 2
      }]
    }
    
  • POST /{index_name}/_update_by_query:批量更新检索出来的文档

    POST _update_by_query
    {
      "script": {
        "source": "ctx._source.likes++",
        "lang": "painless"
      },
      "query": {
        "term": {
          "user": "kimchy"
        }
      }
    }
    

POST与PUT的区别:POST不用加具体的id,它是作用在一个集合资源之上的(/uri),而PUT操作是作用在一个具体资源之上的(/uri/xxx)比如 PUT /website/blog/123.

4.2 文档的搜索

文档的搜索分为 URI Search 和 Request Body Search 两种方式,URI Search 主要在 URL 中通过 query string 的方式传参进行查询,方便简单;Request Body Search 是通过 POST 的请求体 Body 的方式传参进行搜索,支持丰富的搜索格式,下面是搜索相关的 rest API 如下:

  • POST|GET /_search: 对集群中所有的索引进行搜索
  • POST|GET /index1,index2/_search:搜索索引 index1 和 index2
  • POST|GET /index*/_search: 搜索以 index 开头命名的索引

搜索结果的是否合理,主要取决于搜索的相关性,搜索相关性由下面三个属性决定:

  1. 查准率:尽可能返回较少的无关文档
  2. 查全率:尽量返回较多的相关文档
  3. 排名:是否可以按照相关度进行排序

URI Search 相关示例

//搜索 users 索引下,user 字段包含 chenmangmanga,并对搜索结果按照 year 降序,获取前 10 个搜索结果,搜索超时时间为 1 秒
GET users/_search?q=user:chenmangmanga&sort=year:desc&from=0&size=10&timeout=1s
{
  "profile": true
}

q: 指定查询语句
df: 指定查询的默认字段
sort:按照什么字段进行排序
from/size:用于分页
profile:可以查看查询是如何被执行的
timeout:查询超时时间

Query String Syntax
对于上面查询查询语句 q 字段,Elasticsearch 支持丰富的搜索格式:

普通查询

1. q=status:active:表示搜索 status 字段中包含 active 的文档

布尔查询

2. q=title:(quick OR brown): 表示搜索 title 字段中包含 quick 或者 brown 的文档,等同于 q=title:(quick brown) 或者 q=title:(quick || brown)
3. q=title:(quick AND brown): 表示搜索 title 字段中包含 quick 并且包含 brown 的文档,等同于 q=title:(quick && brown) 或者 q=title:(quick +brown)
4. q=author:"John Smith": 表示搜索 author 字段中包含 John Smith 的文档
5. q=_exists_:title:表示搜索 title 字段为非 null 的文档
6. q=title:(quick NOT brown): 表示搜索 title 字段中包含 quick 并且不包含 brown 的文档,q=title:(quick ! brown) 或者 q=title:(quick -brown)

范围查询

7. q=date:[2012-01-01 TO 2012-12-31]:查询 date 字段在 2012-01-01 和 2012-12-31 之间的文档
8. q=count:[10 TO *]:count 字段大于 10 的范围查询
9. q=age:(>=10 AND <20):查询 age 字段大于等于 10 且小于 20 的文档

正则查询

10. q=name:/joh?n(ath[oa]n)/

模糊查询和近似查询

1. q=title:befutifl~1 : 表示增加,删除,替换,或者字符位置转换不超过1的模糊匹配
2. q=title:"lord rings"~2:表示lord 和 rings 中间不超过2个字符的模糊匹配情况

Request Body Search 示例
将查询语句通过 HTTP Request Body 的形式发送给 Elasticsearch,支持非常丰富的查询格式,详情请参考官方文档

POST users/_search
{
  "profile": "true",   //输出ES是如何执行查询的
  "query": {
    "match": {         //user字段匹配 chenmangmanga 或者 zhuweil
      "user": {
        "query": "chenmangmanga, zhuweil",
        "operator": "OR"
      }
    }
  },
  "_source": ["user", "age"], //只返回部分字段
  "from": 0,                   //分页
  "size": 2,
  "sort": [{"age": "desc"}],  //排序,支持多个字段排序
  "script_fields": {
    "newField": {             //通过脚本生成新的字段
      "script": {
        "lang": "painless",
        "source": "doc['age'].value + '_hello'"
      }
    }
  }
}

聚合分析
类似关系型数据库,Elasticsearch 同样支持丰富的聚合运算,可以根据某个维度对数据进行求和,平均值,最大值等运算,并可以进行二次聚合运算

GET kibana_sample_data_flights/_search
{
  "size": 0,
  "aggs":{
    "flight_dest":{
      "terms":{
        "field":"DestCountry"   //根据字段 DestCountry 进行聚合
      },
      "aggs":{
        "avg_price":{                // 求 AvgTicketPrice 的平均值
          "avg":{
            "field":"AvgTicketPrice"
          }
        },
        "wather":{
          "terms": {
            "field": "DestWeather",  // 二次聚合,求每个 DestCountry 下每个 DestWeather 的情况
            "size": 5
          }
        }
      }
    }
  }
}

Elasticsearch 中聚合主要分为以下几类:

Bucket Aggregation: 对满足特定条件的文档的聚合,分成不同的桶,类似关系型数据库中的 Group By
Metric Aggregation:一些数学运算,可以对文档字段进行统计分析,比如最大值,最小值,平均值的计算等
Pipeline Aggregation:对其它的聚合结果进行二次聚合
Matrix Aggregation:支持对多个字段的操作并提供一个结果矩阵

其它 API

  • GET _cluster/health: 查看集群的健康状态

  • GET {index_name}: 查看对应索引的状态

  • GET {index_name}/_count:查看索引的文档总数

  • POST /_analyze: 使用某个分词器对文本进行分词

    POST /_analyze
    {
      "analyzer": "standard",
      "text": "hello world, you are right"
      }
    
  • DELETE {index_name}:删除索引

  • PUT /{index_name}:创建索引

    {
        "settings": {
            "analysis": {
                "normalizer": {
                    "string_lowercase": {
                        "type": "custom",
                        "filter": [
                            "lowercase"
                        ]
                    }
                }
            }
        },
        "mappings": {
            "portrait": {
                "_all": {
                    "enabled": false
                },
                "dynamic_templates": [
                    {
                        "string_fields": {
                            "match_mapping_type": "string",
                            "mapping": {
                                "type": "keyword"
                            }
                        }
                    }
                ],
                "properties": {
                    "column": {
                        "properties": {
                            "comment": {
                                "type": "text",
                                "analyzer": "ik_max_word"
                            },
                            "name": {
                                "type": "keyword",
                                "normalizer": "string_lowercase"
                            },
                            "type": {
                                "type": "keyword",
                                "normalizer": "string_lowercase"
                            }
                        }
                    },
                    "comment": {
                        "type": "text",
                        "analyzer": "ik_max_word"
                    },
                    "db": {
                        "type": "keyword",
                        "normalizer": "string_lowercase"
                    },
                    "id": {
                        "type": "text",
                        "analyzer": "ik_max_word"
                    },
                    "identity": {
                        "properties": {
                            "sign": {
                                "type": "keyword"
                            }
                        }
                    },
                    "name": {
                        "type": "text",
                        "analyzer": "ik_max_word"
                    },
                    "owner": {
                        "type": "text",
                        "analyzer": "ik_max_word"
                    },  
                    "table": {
                        "type": "text",
                        "analyzer": "ik_max_word"
                    },
                    "tags": {
                        "type": "nested",
                        "properties": {
                            "id": {
                                "type": "long"
                            },
                            "keywords": {
                                "type": "text",
                                "fields": {
                                    "keyword": {
                                        "type": "keyword",
                                        "ignore_above": 256
                                    }
                                },
                                "analyzer": "ik_max_word"
                            },
                            "level": {
                                "type": "long"
                            },
                            "parentTagId": {
                                "type": "long"
                            },
                            "tag": {
                                "type": "text",
                                "fields": {
                                    "keyword": {
                                        "type": "keyword",
                                        "ignore_above": 256
                                    }
                                },
                                "analyzer": "ik_max_word"
                            },
                            "type": {
                                "type": "keyword"
                            }
                        }
                    }
                }
            }
        }
    }
    
  • GET {index_name}/_mapping:查看 mapping 定义

  • PUT {index_name}/_mapping:修改 mapping 定义

    # 新增一个字段并设置类型为text
    {
        "portrait": {
            "properties": {
                "path": {
                    "type": "text",
                    "analyzer": "ik_max_word"
                }
            }
        }
    }
    
  • PUT /{index_name}/_alias/{alias_name}:给索引新增别名

  • GET {index_name}/_alias:获取索引所有别名

  • PUT /_template/template_test:定义 Index Template

参考文献
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值