Elasticsearch---读书笔记(1)

371 阅读8分钟

一、前言

  • 什么是Elastisearch :
    • Elasticsearch 是一个分布式、可扩展、实时的搜索与数据分析引擎

二、基础入门

1、 基础知识

  • 什么是 Lucene
    • 一个搜索引擎库
  • Elasticsearch 也是使用 Java 编写的,它的内部使用 Lucene 做索引与搜索,但是它的目的是使全文检索变得简单, 通过隐藏 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API

1.1、 安装并运行

  • 自己捣鼓捣鼓

1.2、 和 ES 的交互

  • 通过RESTful 风格的 API。自己捣鼓捣鼓

1.3、 面向文档

  • 数据通常不是简单的键值对,通常包含更加复杂的结果
  • 如果采用关系型数据库的行和列,相当于把一个对象进行拆分,放入到一个非常巨大的表格,
    • 为了适应表结构,你必须设法将这个对象扁平化-----通常一个字段对应一列------而且每次查询时又需要将其重新构造为对象。
  • 在 Elasticsearch 中将对象转化为 JSON 后构建索引要比在一个扁平的表结构中要简单的多。

1.4、 上手

  • 自己捣鼓捣鼓

1.5、 索引员工文档

  • 存储数据到 Elasticsearch 的行为叫做 索引 ,但在索引一个文档之前,需要确定将文档存储在哪里
  • 一个 Elasticsearch 集群可以 包含多个 索引 ,相应的每个索引可以包含多个 类型

对于员工目录,我们将做如下操作:

  • 每个员工索引一个文档,文档包含该员工的所有信息。
  • 每个文档都将是 employee 类型 。
  • 该类型位于 索引 megacorp 内。
  • 该索引保存在我们的 Elasticsearch 集群中。

实践中这非常简单(尽管看起来有很多步骤),我们可以通过一条命令完成所有这些动作:

PUT /megacorp/employee/1
{
    "first_name" : "John",
    "last_name" :  "Smith",
    "age" :        25,
    "about" :      "I love to go rock climbing",
    "interests": [ "sports", "music" ]
}

注意,路径 /megacorp/employee/1 包含了三部分的信息:

  • megacorp 索引名称

  • employee 类型名称

PUT /megacorp/employee/2
{
    "first_name" :  "Jane",
    "last_name" :   "Smith",
    "age" :         32,
    "about" :       "I like to collect rock albums",
    "interests":  [ "music" ]
}

PUT /megacorp/employee/3
{
    "first_name" :  "Douglas",
    "last_name" :   "Fir",
    "age" :         35,
    "about":        "I like to build cabinets",
    "interests":  [ "forestry" ]
}

1.6、检索文档

GET /megacorp/employee/1
{
  "_index" :   "megacorp",
  "_type" :    "employee",
  "_id" :      "1",
  "_version" : 1,
  "found" :    true,
  "_source" :  {
      "first_name" :  "John",
      "last_name" :   "Smith",
      "age" :         25,
      "about" :       "I love to go rock climbing",
      "interests":  [ "sports", "music" ]
  }
}

1.7、 轻量级检索

  • 请求所有的雇员
GET /megacorp/employee/_search
  • 尝试下搜索姓氏为 Smith 的雇员
GET /megacorp/employee/_search?q=last_name:Smith

1.8、 表达式搜索

GET /megacorp/employee/_search
{
    "query" : {
        "match" : {
            "last_name" : "Smith"
        }
    }
}
  • 返回结果与之前的查询一样,但还是可以看到有一些变化。其中之一是,不再使用 query-string 参数,而是一个请求体替代。这个请求使用 JSON 构造,并使用了一个 match 查询(属于查询类型之一,后面将继续介绍)。

1.9、 更为复杂的操作

  • 过滤器
GET /megacorp/employee/_search
{
    "query" : {
        "bool": {
            "must": {
                "match" : {
                    "last_name" : "smith" (1)
                }
            },
            "filter": {
                "range" : {
                    "age" : { "gt" : 30 } (2)
                }
            }
        }
    }
}

1.10、 全文检索

  • 搜索下所有喜欢攀岩(rock climbing)的员工:
GET /megacorp/employee/_search
{
    "query" : {
        "match" : {
            "about" : "rock climbing"
        }
    }
}

显然我们依旧使用之前的 match 查询在about 属性上搜索 ``rock climbing'' 。得到两个匹配的文档:

{
   ...
   "hits": {
      "total":      2,
      "max_score":  0.16273327,
      "hits": [
         {
            ...
            "_score":         0.16273327, (1)
            "_source": {
               "first_name":  "John",
               "last_name":   "Smith",
               "age":         25,
               "about":       "I love to go rock climbing",
               "interests": [ "sports", "music" ]
            }
         },
         {
            ...
            "_score":         0.016878016, (1)
            "_source": {
               "first_name":  "Jane",
               "last_name":   "Smith",
               "age":         32,
               "about":       "I like to collect rock albums",
               "interests": [ "music" ]
            }
         }
      ]
   }
}
  • 相关性得分
    • Elasticsearch 默认按照相关性得分排序,即每个文档跟查询的匹配程度。第一个最高得分的结果很明显:John Smith 的 about 属性清楚地写着 ``rock climbing'' 。

    • 但为什么 Jane Smith 也作为结果返回了呢?原因是她的 about 属性里提到了 rock'' 。因为只有 rock'' 而没有 ``climbing'' ,所以她的相关性得分低于 John 的。

1.11、 短语搜索

GET /megacorp/employee/_search
{
    "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    }
}

毫无悬念,返回结果仅有 John Smith 的文档。

{
   ...
   "hits": {
      "total":      1,
      "max_score":  0.23013961,
      "hits": [
         {
            ...
            "_score":         0.23013961,
            "_source": {
               "first_name":  "John",
               "last_name":   "Smith",
               "age":         25,
               "about":       "I love to go rock climbing",
               "interests": [ "sports", "music" ]
            }
         }
      ]
   }
}

1.12、 高亮

GET /megacorp/employee/_search
{
    "query" : {
        "match_phrase" : {
            "about" : "rock climbing"
        }
    },
    "highlight": {
        "fields" : {
            "about" : {}
        }
    }
}
  • 当执行该查询时,返回结果与之前一样,与此同时结果中还多了一个叫做 highlight 的部分。这个部分包含了 about 属性匹配的文本片段,并以 HTML 标签 <em></em> 封装:
{
   ...
   "hits": {
      "total":      1,
      "max_score":  0.23013961,
      "hits": [
         {
            ...
            "_score":         0.23013961,
            "_source": {
               "first_name":  "John",
               "last_name":   "Smith",
               "age":         25,
               "about":       "I love to go rock climbing",
               "interests": [ "sports", "music" ]
            },
            "highlight": {
               "about": [
                  "I love to go <em>rock</em> <em>climbing</em>" (1)
               ]
            }
         }
      ]
   }
}

1.13、 分析

  • Elasticsearch 有一个功能叫聚合(aggregations),允许我们基于数据生成一些精细的分析结果。聚合与 SQL 中的 GROUP BY 类似但更强大。

  • 挖掘出员工中最受欢迎的兴趣爱好:

GET /megacorp/employee/_search
{
  "aggs": {
    "all_interests": {
      "terms": { "field": "interests" }
    }
  }
}
  • 暂时忽略掉语法,直接看看结果:
{
   ...
   "hits": { ... },
   "aggregations": {
      "all_interests": {
         "buckets": [
            {
               "key":       "music",
               "doc_count": 2
            },
            {
               "key":       "forestry",
               "doc_count": 1
            },
            {
               "key":       "sports",
               "doc_count": 1
            }
         ]
      }
   }
}
  • 可以看到,两位员工对音乐感兴趣,一位对林业感兴趣,一位对运动感兴趣。这些聚合的结果数据并非预先统计,而是根据匹配当前查询的文档即时生成的

  • 如果想知道叫 Smith 的员工中最受欢迎的兴趣爱好,可以直接构造一个组合查询:

GET /megacorp/employee/_search
{
  "query": {
    "match": {
      "last_name": "smith"
    }
  },
  "aggs": {
    "all_interests": {
      "terms": {
        "field": "interests"
      }
    }
  }
}

all_interests 聚合已经变为只包含匹配查询的文档:

  ...
  "all_interests": {
     "buckets": [
        {
           "key": "music",
           "doc_count": 2
        },
        {
           "key": "sports",
           "doc_count": 1
        }
     ]
  }

聚合还支持分级汇总 。比如,查询特定兴趣爱好员工的平均年龄:

GET /megacorp/employee/_search
{
    "aggs" : {
        "all_interests" : {
            "terms" : { "field" : "interests" },
            "aggs" : {
                "avg_age" : {
                    "avg" : { "field" : "age" }
                }
            }
        }
    }
}
  • 得到的聚合结果有点儿复杂,但理解起来还是很简单的:
  ...
  "all_interests": {
     "buckets": [
        {
           "key": "music",
           "doc_count": 2,
           "avg_age": {
              "value": 28.5
           }
        },
        {
           "key": "forestry",
           "doc_count": 1,
           "avg_age": {
              "value": 35
           }
        },
        {
           "key": "sports",
           "doc_count": 1,
           "avg_age": {
              "value": 25
           }
        }
     ]
  }
  • 输出基本是第一次聚合的加强版。

1.14、 教程结束语

1.15、 分布式特性

  • 分配文档到不同的容器 或 分片 中,文档可以储存在一个或多个节点中

  • 按集群节点来均衡分配这些分片,从而对索引和搜索过程进行负载均衡

  • 复制每个分片以支持数据冗余,从而防止硬件故障导致的数据丢失

  • 将集群中任一节点的请求路由到存有相关数据的节点

  • 集群扩容时无缝整合新节点,重新分配分片以便从离群节点恢复

2、ES 集群的原理

  • 垂直扩容:不添加新的机器,通过改善当前机器的性能,实现扩容
  • 水平扩容:添加新的机器实现扩容

2.1、 空集群

  • 如果我们启动了一个单独的节点,里面不包含任何的数据和索引,那么当前集群就是包含空节点的集群。

image.png

  • 节点

    • 集群是由一个或者多个拥有相同 cluster.name 配置的节点组成,共同承担数据和负载的压力

    • 当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据

  • 主节点

    • 当一个节点被选举成为主节点时, 它将负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等

    • 主节点并不需要涉及到文档级别的变更和搜索等操作,即使流量的增加它也不会成为瓶颈

2.2、 集群健康

  • Elasticsearch 的集群监控信息中包含了许多的统计数据,其中最为重要的一项就是 集群健康 , 它在 status 字段中展示为 green 、 yellow 或者 red 。
GET /_cluster/health

2.3、 索引

  • 索引实际上是指向一个或者多个物理 分片 的 逻辑命名空间 。

  • 分片

    • 一个 分片 是一个底层的 工作单元 ,它仅保存了全部数据中的一部分

    • 一个分片是一个 Lucene 的实例, 本身就是一个完整的搜索引擎

    • 一个分片可以是  分片或者 副本 分片

    • 我们的文档被存储和索引到分片内,但是应用程序是直接与索引而不是与分片进行交互

  • Elasticsearch 是利用分片将数据分发到集群内各处的分片是数据的容器,文档保存在分片内,分片又被分配到集群内的各个节点里。

  • 在索引建立的时候就已经确定了主分片数,但是副本分片数可以随时修改。

  • 创建一个 blogs 的索引,默认会有个主分片,设置成为3个主分片和 1 份副本

  • 我们的集群现在是拥有一个索引的单节点集群,所有3个主分片都被分配在 Node 1 。

image.png

  • 如果我们现在查看集群健康,我们将看到如下内容:
{
  "cluster_name": "elasticsearch",
  "status": "yellow", (1)
  "timed_out": false,
  "number_of_nodes": 1,
  "number_of_data_nodes": 1,
  "active_primary_shards": 3,
  "active_shards": 3,
  "relocating_shards": 0,
  "initializing_shards": 0,
  "unassigned_shards": 3, (2)
  "delayed_unassigned_shards": 0,
  "number_of_pending_tasks": 0,
  "number_of_in_flight_fetch": 0,
  "task_max_waiting_in_queue_millis": 0,
  "active_shards_percent_as_number": 50
}
  1. 集群 status 值为 yellow 。
  2. 没有被分配到任何节点的副本数。

集群的健康状况为 yellow 则表示全部  分片都正常运行(集群可以正常服务所有请求),但是 副本 分片没有全部处在正常状态。

2.4、 集群模式

  • 另启一个节点

image.png

  • 当第二个节点加入到集群后,3个 副本分片 将会分配到这个节点上——每个主分片对应一个副本分片。 这意味着当集群内任何一个节点出现问题时,我们的数据都完好无损。

2.5、 水平扩容

image.png

  • 分片是一个功能完整的搜索引擎,它拥有使用一个节点上的所有资源的能力
  • 我们这个拥有6个分片(3个主分片和3个副本分片)的索引可以最大扩容到6个节点,每个节点上存在一个分片,并且每个分片拥有所在节点的全部资源。

2.6、 应对故障

  • 如果我们关闭第一个节点

image.png

  • 此时集群的状态将会为 yellow

未完待续

参考