Elasticsearch-02-入门:集群、节点、分片、索引及常用API

2. 基础入门

2.1 重要概念

2.1.1 集群和节点

1)cluster

Elasticsearch集群是由一个或多个节点组成,通过其集群名称来进行唯一标识。节点在搜索到集群之后,通过判断自身的 cluster.name 来决定是否加入该集群

2)node

节点就是单个的Elasticsearch实例。在一般情况下,每个节点都在独立的容器中运行。根据节点的作用,node具有如下的角色分配:

  • master-eligible node

    • 主节点负责轻量级群集范围的操作,例如创建或删除索引,跟踪哪些节点是群集的一部分,并决定哪些分片分配给哪些节点。 群集健康是具有稳定主节点的重要性。只有具有主节点资格的、不是只能投票的节点才有可能被选举为主节点

    • 专职的主节点

      • 通常来说,master 是保证集群稳定运行的关键。如果 master 身兼数职的话,就可能导致无法及时的管理集群,从而导致集群无法正常运行。这种情况在集群规模较小的时候极少发生,一旦集群规模扩大,一些专职的 master 就显得十分必要。

      • 这么设置可以创建一个专职的主节点

        node.roles: [ master ]
        
    • 只能投票的主节点

      • 该类型的节点只能投票,且无法被选举成为主节点。(注意,只有具有master角色的节点才能被设置为voting_only

      • 这种角色通常被用于哪些你不想让他成为主节点的节点,这些节点可能承担了很多职责,从而不适合成为主节点。但为了有足够的主节点参与投票,所以不得不出此下策。这是历史遗留的问题,未来能不能修复我们不得而知

        node.roles: [ data, master, voting_only ]
        
    • 一个节点能成功当选master需要的票数为 N/2 + 1,其中 N 是具有主节点资格的节点数

      • 在ES7之前的版本中,这项配置是避免集群脑裂的关键,但这往往难以配置,因为往集群中加入一个新节点这个值可能就会发生变化。
      • 在ES7,我们就不需要这个配置,后面会详细描述
  • data node

    • 数据节点保存着分片。其主要职责是和数据相关的操作,如CRUD、搜索、聚合等。因此数据节点对IO、内存、CPU等硬件资源要求较高。使用专职的数据节点有助于把管理和数据存储分隔开,能更充分的利用系统资源

      node.roles: [ data ]
      
    • 数据节点还可以根据数据的使用频率划分为 hot data nodewarm data nodecold data node三种,数据的使用更新频率依次降低,对硬件资源的需求也逐渐降低

      node.roles: [ data_hot/data_warm/data_cold ]
      
  • ingest node

    • ingest node 是用于数据的预处理操作,是预处理程序的组成部分之一

      node.roles: [ ingest ]
      
  • coordinating node

    • 一些类似于搜索操作、批量索引操作等请求中可能会涉及到多个数据节点。协调节点的作用就是将这些跨节点的请求分发到具体的、正确的数据节点上。数据节点本地执行请求之后,再将操作结果返回给协调节点,由协调节点完成结果的组装输出。

    • 在默认情况下, 每个节点都具有协调这个角色。但在规模较大的集群中,拥有专职的协调节点是非常有必要的,因为分发请求和生成最终的结果需要较多的内存空间和cpu资源。我们可以通过让节点不具有任何角色来充当专职的协调节点

      node.roles: []
      

2.1.2 文档和索引

1)文档

​ 文档是ES搜索的最小数据单元。简单说,文档相当于关系型数据库中的行。但他有具有一些关系型数据库所没有的特点

  • 文档是独立的
  • 文档中还可以包含文档,可以套娃
  • 文档的结构非常灵活。不同于关系型数据库,无需实现定义文档的结构,可以随用随加
2)索引

在ES中,索引是文档的集合,每个索引由一个或多个文档组成,并且这些文档可以分布在不同的分片之中。这类似于关系型数据库中的 database 。它只是一种逻辑命名空间,内部包含的是 1~N 个主分片,0~N 个副本

2.1.3 分片

一个索引可以存储海量的数据。比如一个具有10 亿文档的索引占据1 TB 的磁盘空间,而任一节点都没有这么大的磁盘空间或单个节点处理响应太慢。

为了解决这个问题,ES选择将索引划分为多分,这些划分出来的部分就叫分片(shard)。ES会自动管理这些分片的分布和排列,我们不用操心

有了分片,我们可以随意扩展数据容量,可以在多个节点上进行并行操作,进一步提高了吞吐量

1)Primary shard

​ 索引可以划分为一个或多个主分片。一旦主分片的数量被确定之后,就不能再修改。这是因为数据被索引在主分片上。如果要变更主分片的数量,那么在需要重新索引数据,否则可能导致查询失败

2)Replica shard

副本就是主分片的备份,其数量可以随意更改而不影响数据的保存。使用副本有两个目的:

  • 冗余主分片,在主分片出现故障的时候顶上
  • 提供并发操作。副本分片也可以执行读操作,这样可以大大的提高系统的吞吐量

ES永远不会在主分片所在节点上启动副本

3)分片健康状态

分片健康状态就是分片的分配状态,可以间接指示集群当前工作状态

  • 红色:至少存在一个主分片未分配
  • 黄色:主分片均已分配,但至少一个副本为分配
  • 绿色:everything is good

2.1.4 搜索原理

ES是通过封装Lucene来实现其搜索功能。Lucene采用一种叫倒排索引的技术来实现快速搜索。

1)倒排索引

假设我们有两个文档,每个文档的正文字段如下:

  • The quick brown fox jumped over the lazy dog

  • Quick brown foxes leap over lazy dogs in summer

那么倒排索引之后的结果如下:

image-20210705153127300

现在我们要搜索 Quick brown只需要根据倒排索引之后的结果反向去查找文档即可:

image-20210705153240715

两个文档都匹配,如果我们仅从匹配数量来判断相关性来说,文档1比文档2更符合我们预期的搜索结果

2)分析

目前来说,我们的倒排索引存在着一些问题,如果解决了这些问题的话,我们的搜索结果会更准确

  • Quickquick 以独立的词条出现,然而用户可能认为它们是相同的词。

  • foxfoxes 非常相似, 就像 dogdogs ;他们有相同的词根。

  • jumpedleap, 尽管没有相同的词根,但他们的意思很相近。他们是同义词

如果我们将词条规范为标准模式,那么我们可以找到与用户搜索的词条不完全一致,但具有足够相关性的文档。例如:

  • Quick 可以小写化为 quick
  • foxes 可以 词干提取 --变为词根的格式-- 为 fox 。类似的, dogs 可以为提取为 dog
  • jumpedleap 是同义词,可以索引为相同的单词 jump。

现在,我们的倒排索引像这样

image-20210705155030806

到这一步,已经算是完成了50%,因为我们格式化了源数据,还需要对我们的查询输入做同样的操作,这样,才能完美的查询

2.2 常用API

2.2.1 信息API

请求 作用
GET /_cat/aliases 获取别名信息
GET /_cat/indices 获取索引信息
GET /_cat/shards 获取索引的分片信息
GET /_cat/nodes 获取节点的基本信息
GET /_cluster/settings 获取集群的设置信息,只展示通过API修改过的设置
GET /_cluster/health 获取集群的健康状态
GET /_cluster/state 获取集群的详细信息
GET /_cluster/stats 获取集群的简略状态信息
GET /_nodes 获取节点的信息
GET /_nodes/stats 获取节点的信息

2.2.2 CRUD

1)操作文档
  • GET <index>/_doc/<_id>GET <index>/_source/<_id>

    • 获取制定的文档ID的文档,使用 _doc 获得的是全部文档内容,_source 值获取正文内容
  • POST <index>/_doc/<_id>

    • 创建一个具有给定ID的文档,如果没有给出ID,ES会自动分配一个
  • DELETE /<index>/_doc/<_id>

    • 删除给出id的文档
  • POST /<index>/_delete_by_query

    • 根据条件删除文档,请求体如下:
    {
      "query": {
        "match": {
          
        }
      }
    }
    
  • POST /<index>/_update/<_id>

    • 更新制定文档,请求体中为需要更新的内容
  • POST /_bulkPOST /<index>/_bulk

    • 批量操作,请求体格式如下

      { "index" : { "_index" : "test", "_id" : "1" } }	// 创建索引,下一行是需要创建索引的数据
      { "field1" : "value1" }
      { "delete" : { "_index" : "test", "_id" : "2" } }	// 删除文档
      { "create" : { "_index" : "test", "_id" : "3" } } 	// 创建文档,下一行是文档的数据
      { "field1" : "value3" }
      { "update" : {"_id" : "1", "_index" : "test"} }		// 更新文档,下一行是数据
      { "doc" : {"field2" : "value2"} }
      
  • POST _reindex

    • 重构索引,请求体如下

      {
        "source": {
          "index": "my-index-000001"
        },
        "dest": {
          "index": "my-new-index-000001"
        }
      }
      
2)操作索引
  • 创建索引,如果索引已存在的话则更新

    PUT /<index>
    {
      "settings": {
        "index": {
          "number_of_shards": 3,  
          "number_of_replicas": 2 
        }
      },
      "mappings": {
        "properties": {
          "field1": { "type": "text" }
        }
      }
    }
    
  • 删除索引:DELETE /<index>, 支持通配符

  • 获取索引信息:GET /<index>,支持通配符

  • 判断索引是否存在:HEAD /<index>,支持通配符

  • 获取索引映射:GET /<target>/_mapping

  • 更新索引映射

    PUT /<index>/_mapping
    {
      "properties": {
        "field": {
          "type": "keyword"
        }
      }
    }
    
  • 更新或创建别名:PUT /<index>/_alias/<alias>

  • 查看别名:GET /<index>/_aliasGET /_alias

  • 删除别名:DELETE /<index>/_aliases/<alias>

  • 更新索引设置:PUT /<index>/_settings,请求体为详细索引设置

  • 查看所有设置:GET /<index>/_settings

2.2.3 搜索API

  • 常规搜索:POST /<index>/_search,请求体如下

    // 简单搜索
    {
      "query": {
        "term": {
          "user.id": "kimchy"
        }
      },
      "explain": true, // 在结果中返回有关分数计算的详细信息
      ""
      "scroll" : "1m",	// 滚动搜索数据量,以及上一次滚动搜索的结果
      "scroll_id" : "DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYm9laVYtZndUQlNsdDcwakFMNjU
    1QQ=="
    }
    
    // 复杂查询
    {
      "query": {
        "bool": {
          "must":     { "match": { "title": "quick" }},
          "must_not": { "match": { "title": "lazy"  }},
          "should": [
                      { "match": { "title": "brown" }},
                      { "match": { "title": "dog"   }}
          ]
        }
      }
    }
    

2.3 配置Elasticsearch

2.3.1 ES配置文件

cluster.name: my-application	# 集群名称,

node.name: node-1	# 节点名称
node.roles: [master, data, ingest, data_cold, data_warm, data_hot]	# 节点角色配置

path.data: /path/to/data	# 数据存储路径
path.logs: /path/to/logs	# 日志存储位置

bootstrap.memory_lock: true

network.host: 192.168.0.1	# 节点ip
http.port: 9200	# 节点对外访问端口
transport.port: 9300	# 节点通信端口

discovery.zen.minimum_master_nodes: 2	# 最小主节点数,ES7之前的版本必须合理配置,不然可能导致脑裂问题;ES7之后该配置项失效(无论配多少都不会生效)

# ES7之后的配置,initial_master_nodes的值要和node.name完全一致。在集群启动的时候,要有半数以上的initial_master_nodes存活才可以成功建立集群。
discovery.seed_hosts: ["host1", "ip1", "ip2:port2"]	
cluster.initial_master_nodes: ["node-1", "node-2"]

2.3.2 重要的系统配置

1)禁用内存交换

大多数操作系统会尽可能将未使用的内存空间来作为文件系统的缓存,同时交换应用程序为使用的空间。内存交换会导致ES节点不稳定,因为这会影响GC的工作效率,从而导致节点无法响应

  • 操作系统层面

    sudo swapoff -a
    
    • 这会暂时禁用操作系统的内存交换。如果需要永久禁用,则需要修改/etc/fstab文件
  • 应用层面

    bootstrap.memory_lock: true
    
    • 这个选项会让ES在启动的时候就锁定分片的内存空间,不过要注意不能锁定多于当前可用内存。

    • 这个选项出于某些原因,仍有可能失效,大多数情况下是ES没有权限锁定内存,需要编辑 /etc/security/limits.conf文件

      # allow user 'elasticsearch' mlockall
      elasticsearch soft memlock unlimited
      elasticsearch hard memlock unlimited
      
2)增加文件句柄

​ ES在运行的使用会使用大量的文件句柄,如果没有足够的文件句柄,则可能会造成数据丢失。ES最少需要65535个文件句柄

  • 系统命令

    ulimit -n 65535
    
  • 系统配置文件

    # elasticsearch用户最多可以使用65535个文件
    elasticsearch  -  nofile  65535
    
3)确保能创建足够的线程

​ ES在不同操作的情况下,使用不同大小的线程池。要确保ES在需要的时候能创建足够多的线程,ES最少需要能创建4096个线程

  • 系统命令

    ulimit -n 4096
    
  • 系统配置文件

    # elasticsearch用户最多可以使用4096个线程
    elasticsearch soft nproc 4096
    elasticsearch hard nproc 4096
    

2.3.3 Heap大小设置

默认情况下,ES会自动根据节点的角色和总内存空间来自动设置堆空间(至少需要JDK14),但手动更改JVM堆大小也是十分常见的操作,修改 jvm.options 即可,但最大不要超过32GB

如果服务器内存空间非常大的话,可以考虑运行多个ES实例而不是使用大内存空间

在单独部署的情况下,建议给ES分配的堆空间为总内存空间的50%,剩余的内存空间留给Lucene实例

  • 为什么不建议给ES分配过多的内存空间?

    ES使用的JVM会使用一种叫压缩对象指针的技术。其通过使用偏移量来表示内存空间地址,从而减少了指针的大小,节约了内存空间。

  • 关于压缩指针,可以参考这篇文章:https://blog.csdn.net/liujianyangbj/article/details/108049482

2.4 源码环境构建

编译和加载过程中可能会出现JDK版本不匹配的问题,手动替换JDK版本即可

  • 从GitHub上获取ES源码:https://github.com/elastic/elasticsearch,同时准备一个对应发行版的ES

  • 用IDEA打开ES源码,在 Preferences -> Build -> Build Tools -> Gradle 中设置好grade要使用的JDK,在 File -> Project Structure -> Project Settings -> Project -> Project SDK 中设置JDK版本

  • idea会自动导入gradle项目,期间会联网下载gradle。如果不想等待,可以在 项目文件夹/gradle/wrapper/gradle-wrapper.properties 中手动设置gradle的位置,从而跳过下载

    image-20210702183926159

    • 手动设置的话可能会出现sha256校验失败等问题,注释相关配置即可
  • 等待gradle构建完成

  • 直接运行主启动类:server/src/main/java/org.elasticsearch.bootstrap.Elasticsearch

  • 常见错误

    1. the system property [es.path.conf] must be set
    • 设置jvm,-Des.path.conf,value是发行版es的配置目录,使用默认配置即可
    1. *path.home is not configured*
    • 设置jvm,-Des.path.home,value是发行版es的根目录
    1. ERROR Could not register mbeans java.security.AccessControlException: access denied ("javax.management.MBeanTrustPermission" "register")
    • 设置jvm,-Dlog4j2.disable.jmx=true
    1. Unknown codebases [codebase.elasticsearch-plugin-classloader, codebase.elasticsearch, codebase.elasticsearch-secure-sm]
    • 注释掉相应的检查报错异常,需要相应的jar包,而ide生成的class文件,所以检测不到
    1. Plugin [xxx] was built for Elasticsearch version xxx but version xxx is running
      • 插件版本不对应,注释掉相应检查逻辑即可
  • 启动成功后访问:http://localhost:9200/测试

posted @ 2021-07-14 11:05  PrimaBruceXu  阅读(300)  评论(0编辑  收藏  举报