跳转至

ElasticSearch

什么是ElasticSearch?

Elasticsearch是基于Apace Lunence构建的开源,分布式,具有高可用性和高拓展性的全文检索引擎。Elasticsearch具有开箱即用的特性,提供RESTful接口,是面向文档的数据库,文档存储格式为JSON,可以水平扩展至数以百计的服务器存储来实现处理PB级别的数据。

ES有哪些节点类型?

一个节点就是一个Elasticsearch的实例,每个节点需要显示指定节点名称,可以通过配置文件配置,或者启动时候-E node.name=node1指定

节点类型

每个节点在集群承担承担不同的角色,也可以称为节点类型。

候选主节点(Master-eligible nodes)和主节点(Master Node)

  • 每个节点启动之后,默认就是一个Master eligible节点,Master-eligible节点可以参加选主流程,成为Master节点
  • 当第一个节点启动时候,它会将自己选举成为Master节点
  • 在每个节点上都保存了集群的状态信息,但**只有Master节点才能修改集群的状态信息**。集群状态(Cluster State)中必要信息包含
    • 所有节点的信息
    • 所有的索引,以及其Mapping与Setting信息
    • 分片的路由信息

数据节点(Data Node)和协调节点(Coordinating Node)和Ingest节点

  • Data Node
    • 用于保存数据的节点。负责保存分片的数据,在数据拓展上起到至关重要的作用
  • Coorination Node
    • 负责接受Client的请求,将请求分发到合适的节点,最终把结果汇集到一起
    • 每个节点默认都起到Cooridinating Node的职责,这就意味着如果一个node,将node.master,node.data,node.ingest全部设置为false,那么它就是一个纯粹的coordinating Node node,仅仅用于接收客户端的请求,同时进行请求的转发和合并
  • Ingest节点
    • 用于预处理,可以运行pipeline脚本,用来对document写入索引文件之前进行预处理的

在生产环境部署上可以部署独立(dedicate)的 Ingest Node 和 Coordinate node,在前端的Load Balance前面增加转发规则把读分发到coording node,写分发到 ingest node。 如果集群负载不高,可以配置一些节点同时具备coording和ingest的能力。然后将读写全部路由到这些节点。不仅配置简单,还节约硬件成本

其他类型节点

  • 冷热节点(Hot & Warm Node)

    • 不同硬件配置的Data Node,用来实现Hot & Warm架构,降低集群部署的成本。通过设置节点属性来实现
  • 机器学习节点(Machine Learning Node)

    • 负责跑机器学习的Job,用来异常检测

配置原则:

  • 开发环境一个节点可以承担多种角色,节省服务器资源
  • 生产环境中,应该设置单一的角色的节点,即dedicated node
节点类型 配置参数 默认值
候选主节点 node.master true
数据节点 node.data true
ingest节点 node.ingest true
协调节点 每个节点默认都是协调节点
机器学习节点 node.ml true

为什么说ES是准实时的?

Document首先写入到Indexing buffer中,当 buffer 中的数据每隔index.refresh_interval秒(或者Indexing buffer满了)缓存 refresh 到filesystem cache 中时,此时文档是可以检索到了

怎么解决ES深度分页问题?

深度分页指的是假设我们请求第 1000 页—​结果从 10001 到 10010 。所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。 然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果中的 50040 个结果。在分布式系统中,对结果排序的成本随分页的深度成指数上升。

解决办法就是业务上面避免。

doc values为了解决什么?

doc_values是为了解决排序和聚合问题。doc_values不适合text类型字段,对于text类型字段需要使用Fielddata

PUT /myindex/_mapping/doc
{
  "properties": {
      "myfield": {
      "type": "text",
      "fields":{
        "keyword":{
          "type":"keyword",
          "ignore_above":256
        }
      }
    }
  }
}

上面myfield是不可以聚合的,但是myfield.keyword是可以聚合的

ES中副本分片的目的是做什么?

  1. 副本分片的主要目的就是为了故障转移,如果持有主分片的节点挂掉了,一个副本分片就会晋升为主分片的角色。

  2. 副本分片可以服务于读请求,可以通过增加副本的数目来提升查询性能

怎样在不停机情况下,进行索引重建?

索引别名

ES数据建模有哪些模式?

加入我们正在根据用户名称,搜索其博客文章

应用层联接

根据user索引搜索到用户id,然后根据id去搜索文章索引blogpost

PUT /my_index/user/1 
{
  "name":     "John Smith",
  "email":    "john@smith.com",
  "dob":      "1970/10/24"
}

PUT /my_index/blogpost/2 
{
  "title":    "Relationships",
  "body":     "It's complicated...",
  "user":     1 
}

反范式设计

用于1对多模式下,将1的信息,存放在N这一边。下面就是把用户信息存放一份在blogpost这里面

PUT /my_index/user/1
{
  "name":     "John Smith",
  "email":    "john@smith.com",
  "dob":      "1970/10/24"
}

PUT /my_index/blogpost/2
{
  "title":    "Relationships",
  "body":     "It's complicated...",
  "user":     {
    "id":       1,
    "name":     "John Smith" 
  }
}

嵌套对象

假定们可以将一篇博客文章的评论以一个 comments 数组的形式和博客文章放在一起:

PUT /my_index/blogpost/1
{
  "title": "Nest eggs",
  "body":  "Making your money work...",
  "tags":  [ "cash", "shares" ],
  "comments": [ 
    {
      "name":    "John Smith",
      "comment": "Great article",
      "age":     28,
      "stars":   4,
      "date":    "2014-09-01"
    },
    {
      "name":    "Alice White",
      "comment": "More like this please",
      "age":     31,
      "stars":   5,
      "date":    "2014-10-22"
    }
  ]
}

我们想要搜索到评论者是Alice,且年龄是28岁的评论,搜索语句如下:

GET /_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "name": "Alice" }},
        { "match": { "age":  28      }} 
      ]
    }
  }
}

搜索结果却能够搜索到记录。这是不符合预期的。

这是因为对象数组中JSON 格式的文档被处理成如下的扁平式键值对的结构,comments对象中字段失去关联:

{
  "title":            [ eggs, nest ],
  "body":             [ making, money, work, your ],
  "tags":             [ cash, shares ],
  "comments.name":    [ alice, john, smith, white ],
  "comments.comment": [ article, great, like, more, please, this ],
  "comments.age":     [ 28, 31 ],
  "comments.stars":   [ 4, 5 ],
  "comments.date":    [ 2014-09-01, 2014-10-22 ]
}

这时候我们可以使用nested object,来保证子对象关系未打散:

{ 
  "comments.name":    [ john, smith ],
  "comments.comment": [ article, great ],
  "comments.age":     [ 28 ],
  "comments.stars":   [ 4 ],
  "comments.date":    [ 2014-09-01 ]
}
{ 
  "comments.name":    [ alice, white ],
  "comments.comment": [ like, more, please, this ],
  "comments.age":     [ 31 ],
  "comments.stars":   [ 5 ],
  "comments.date":    [ 2014-10-22 ]
}
{ 
  "title":            [ eggs, nest ],
  "body":             [ making, money, work, your ],
  "tags":             [ cash, shares ]
}


PUT /my_index
{
  "mappings": {
    "blogpost": {
      "properties": {
        "comments": {
          "type": "nested", 
          "properties": {
            "name":    { "type": "string"  },
            "comment": { "type": "string"  },
            "age":     { "type": "short"   },
            "stars":   { "type": "short"   },
            "date":    { "type": "date"    }
          }
        }
      }
    }
  }
}

父子文档

在 nested objects 文档中,所有对象都是在同一个文档中,而在父-子关系文档中,父对象和子对象都是完全独立的文档。

父-子关系的主要优势有:

  • 更新父文档时,不会重新索引子文档。
  • 创建,修改或删除子文档时,不会影响父文档或其他子文档。这一点在这种场景下尤其有用:子文档数量较多,并且子文档创建和修改的频率高时。
  • 子文档可以作为搜索结果独立返回。

ES索引生命周期管理是怎么回事?

ES索引生命周期管理分为4个阶段:hot、warm、cold、delete,其中hot主要负责对索引进行rollover操作,warm、cold、delete分别对rollover后的数据进一步处理。

phases desc
hot 索引更新和查询很活跃
warm 索引不再更新,但仍然有查询
cold 索引不再更新,只有很少的查询,而且查询速度也很慢
delete 索引不需要了,可以安全的删除

操作

timing

ILM各个阶段的action几乎都需要用到定时器,例如下面这个操作:

curl -X PUT "localhost:9200/_ilm/policy/my_policy" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "warm": {
        "min_age": "1d",
        "actions": {
          "allocate": {
            "number_of_replicas": 1
          }
        }
      },
      "delete": {
        "min_age": "30d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}
'

# 应用到模板上
PUT _index_template/my_template
{
    "index_patterns": ["test-*"],
    "template": {
        "settings": {
            "number_of_shards": 1,
            "number_of_replicas": 1,
            "index.lifecycle.name": "my_policy",
            "index.lifecycle.rollover_alias": "test-alias"
        }
    }
}

上述warm阶段通过min_age配置了1d,意思是索引从创建至少需要经历1天的时间才会被移入到warm阶段,另一个delete阶段min_age配置了30d,意思是索引在创建后的30天会被删除;通常情况下warm、cold、delete的起始时间是从索引创建开始算起的,但是如果配置了hot,那么后面phrase配置的时间应该大于rollover的时间。

Hot Rollover

curl -X PUT "localhost:9200/_ilm/policy/datastream_policy" -H 'Content-Type: application/json' -d'
{
  "policy": {                       
    "phases": {
      "hot": {                      
        "actions": {
          "rollover": {             
            "max_size": "50GB",
            "max_age": "30d"
          }
        }
      } 
   }
}
'

我们定义了一个策略,策略中使用了hot rollover action,当引用该策略的索引满足rollover中任一一个条件时就会触发滚动操作,生成新的索引,新索引的格式是 ^.*-\d+$ (如,index_name-000001)

Warm Allocate

allocate action主要有两个操作,1、转移数据到warm节点;2、修改索引副本数。

PUT _ilm/policy/my_policy
{
  "policy": {
    "phases": {
      "warm": {
        "actions": {
          "allocate" : {
            "number_of_replicas": 0,
            "include" : {
              "box_type": "cold,warm"
            }
          }
        }
      }
    }
  }
}

其中include配置的标签需要和elasticsearch.yml中配置的标签名一致,allocate支持的参数有:

参数 | 描述 number_of_replicas | 分配后索引保持的分片数 include | 至少满足其中一个标签 exclude | 排除包含这些标签的服务器 require | 需要同时满足所有配置的标签

Warm Read-Only

配置索引为只读模式

curl -X PUT "localhost:9200/_ilm/policy/my_policy" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "warm": {
        "actions": {
          "readonly" : { }
        }
      }
    }
  }
}
'

Warm Force-Merge

指定索引合并后保留的segment数,过多的 segment 对查询性能有影响,为了充分合并数据,建议设置为 max_num_segments = 1

curl -X PUT "localhost:9200/_ilm/policy/my_policy" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "warm": {
        "actions": {
          "forcemerge" : {
            "max_num_segments": 1
          }
        }
      }
    }
  }
}
'

需要注意的是,设置forcemerge action后索引会被修改为只读模式

Warm Shrink

通过shrink action可以降低索引的分片数量,同样执行该action操作后,索引会被修改为只读模式,同时索引名也会发生变化,如原来索引名称是“logs”,执行后的名称会多一个shrink-前缀,即“shrink-logs”。

curl -X PUT "localhost:9200/_ilm/policy/my_policy" -H 'Content-Type: application/json' -d'
{
  "policy": {
    "phases": {
      "warm": {
        "actions": {
          "shrink" : {
            "number_of_shards": 1
          }
        }
      }
    }
  }
}
'

Cold Freeze

冻结索引意思就是关闭索引。

PUT _ilm/policy/my_policy
{
  "policy": {
    "phases": {
      "cold": {
        "actions": {
          "freeze" : { }
        }
      }
    }
  }
}