ElasticSearch基础

ES基本概念

端口

9300:ElasticSearch集群间组件通信端口

9200:浏览器访问的http协议RESTful接口。http://localhost:9200

Windows单机启动之前可能需要修改的部分地方

  1. config/elasticsearch.yml

    xpack.security.enabled: false:改为false,禁用安全访问。

  2. bin/elasticsearch-env.bat

    配置ES_JAVA_HOME=%JAVA_HOME%:使得使用系统的JDK,而不是使用ES自带的。对于8.10.0版本来说,JDK8跑不了,使用JDK17可以跑。

ES数据格式与MySQL的对照

ES MySQL
Index(索引) Database
Type(类型,7.x版本后该概念被删除) Table
Documents(文档) Row
Fields(字段) Column

ES基本操作

索引操作

创建索引(PUT请求):http://localhost:9200/shopping

查看索引(GET请求):

删除索引(DELETE请求):http://localhost:9200/shopping

文档操作

添加文档(POST/PUT请求):http://localhost:9200/shopping/_doc/1001。这里的1001表示让ES使用指定的ID,而不是让它自己生成。

{
    "title": "小米手机",
    "category": "小米",
    "price": 3999.00
}

查询文档(GET请求):

修改文档(POST请求):

删除文档(DELETE请求):http://localhost:9200/shopping/_doc/1001

查询操作

ES默认是全文检索匹配,因此match的方式会被切词去匹配文档。

  • 条件查询:http://localhost:9200/shopping/_search

    {
        "query": {
            "match": {
                "category": "小米"
            }
        }
    }
    
  • 分页查询:http://localhost:9200/shopping/_search

    {
        "query": {
            "match_all": {
                
            }
        },
        "from": 0,
        "size": 2
    }
    

    match_all:全查询。from+size实现分页

  • 筛选字段+排序:http://localhost:9200/shopping/_search

    {
        "query": {
            "match_all": {
                
            }
        },
        "from": 0,
        "size": 2,
        "_source": ["title"],
        "sort": {
            "price": {
                "order": "asc"
            }
        }
    }
    

    _source:筛选字段

  • 多条件查询

    {
        "query": {
            "bool": {
                "must": [
                    {
                        "match": {
                            "category": "小米"
                        }
                    },
                    {
                         "match": {
                            "price": 3999.00
                        }
                    }
                ],
                "filter": {
                    "range": {
                        "price": {
                            "gt": 5000
                        }
                    }
                }
            }
        }
    }
    

    must:相当于多个条件and

    should:相当于多个条件or

  • 完全匹配查询(不允许切词匹配)

    {
        "query": {
            "match_phrase": {
                "category": "小米"
            }
        }
    }
    
  • 聚合查询

    // 分组
    {
        "aggs": { // 聚合操作
            "price_group": { // 起个组名
                "terms": {
                    "field": "price" // 分组字段
                }
            }
        },
        "size": 0 // 可以不展现文档结果,只展现聚合结果
    }
    // 平均值
    {
        "aggs": { // 聚合操作
            "price_avg": { // 起个名
                "avg": {
                    "field": "price" // 分组字段
                }
            }
        }
    }
    // topN
    {
        "aggs": {
            "top3": {
                "top_hits": {
                    "size": 3
                }
            }
        },
        "size": 0
    }
    
  • 建立映射关系:http://localhost:9200/user/_mapping

    {
        "properties": {
            "name": {
                "type": "text", // 全文索引,会分词
                "index": true, // 建立索引
            },
            "sex": {
                "type": "keyword", // 不分词
                "index": true
            },
            "tel": {
                "type": "keyword",
                "index": false // 不建立索引,即无法通过该字段检索
            }
        }
    }
    

ES进阶

补充概念

分片(Shards)

将索引划分为多份,放到不同的节点上,每一份索引称之为分片。

优点:

  • 允许水平分割,从而提高索引容量。
  • 在分片上进行分布式、并行的操作,提高性能/吞吐量。

副本(Replicas)

分片的一份或多份拷贝。

优点:

  • 提高可用性。
  • 提高吞吐量,因为可以在所有副本上并行进行。

分配(Allocation)

将分片分配给某个节点的过程,包括分配主分片或副本。如果是副本,还包含从主分片复制数据的过程,这个过程由主master节点完成。

集群

水平扩容

主分片的数目在创建时就确定下来了(具体指的是几份索引),无法修改;但副本分片的数量可以修改,默认是1(其实指的是副本的个数,当主分片数为3,当副本数从1变为2,则增加了3个分片)。

故障应对

某个节点宕机后,如果还有其他节点在,则主分片仍然存在,健康度为黄色(有未分配的分片)。

路由计算和分片控制

路由计算:hash(id) % 主分片数量

分片控制:查询时,要让查询打到有对应数据分片的节点上。默认是轮询。

数据写流程

  1. 客户端请求集群中的任意节点
  2. 如果当前节点不能处理(即路由计算结果不由当前节点处理),则将请求转发到指定节点
  3. 主分片保存数据
  4. 主分片将数据发送到副本节点
  5. 副本保存数据,返回ack
  6. 主分片(一致性默认是quorum,可以为one或者all)返回ack给客户端

数据读流程

  1. 客户端请求集群中的任意节点
  2. 该节点计算数据所在的分片以及全部的副本信息,选择节点进行查询
  3. 转发请求到指定节点
  4. 节点返回结果给客户端

分片原理

倒排索引

  • 分词器:keyword(关键词,不再分词)、text(文本,可以分词)、ik_max_word(最细粒度分词)、ik_smart(最粗粒度分词)。默认的IK分词器只能分出单个汉字,需要利用IK插件以使用前述的两种分词器。
  • 词条:索引中最小存储和查询单元
  • 词典:词条的集合(实现结构,B+树 或 Hash)
  • 倒排表:每个关键词出现的位置、频率等,每一项被称为倒排项。

文档搜索

早期的全文检索为整个文档建立很大的倒排索引并写入磁盘。一旦要更新,则等新的索引就绪后,替换旧的索引。倒排索引写入磁盘后不可修改,优点是:1)不需要锁;2)索引被读入内存后,大部分请求直接命中内存;3)写入倒排索引允许启用数据压缩。缺点是更新索引需要重建整个倒排索引。

动态更新索引:增加新的补充索引来反映最新的修改,而不是直接重写整个倒排索引,每个倒排索引都会被轮流查询到,从最早的开始查询完后再对结果进行合并。

文档

  • 通过在plugins目录下添加插件,可以增加分词器。

  • 分词器可以配置扩展词典,以解决某些被视为整体的词被分词问题。

  • 自定义分析器。通过设置字符过滤器、分词器、词汇单元过滤器来创建自定义分析器,按顺序执行。

    创建自定义分析器(PUT):http://localhost:9200/my_index

    {
        "settings": {
            "analysis": {
                "char_filter": {
                    "&_to_and": {
                        "type": "mapping",
                        "mappings": ["& => and"]
                    }
                },
                "filter": {
                    "my_stopwords": {
                        "type": "stop",
                        "stopwords": ["the", "a"]
                    }
                },
                "analyzer": {
                    "myanalyzer": {
                        "type": "custom",
                        "char_filter": ["html_strip", "&_to_and"],
                        "tokenizer": "standard",
                        "filter": ["lowercase", "my_stopwords"]
                    }
                }
            }
        }
    }
    

    测试该分析器(GET):http://localhost:9200/my_index/_analyze

    {
        "text": "The quick & brown fox",
        "analyzer": "myanalyzer"
    }
    
  • 并发更新:使用版本号乐观更新(seq_no、primary_term)

ES优化

磁盘

  • 使用SSD。
  • 使用RAID0。因为ES有副本机制,所以使用RAID0没问题。
  • 如果不用RAID0,还可以用多块磁盘,并允许ES通过多个path.data目录配置把数据条带化分配到它们上面。
  • 不要使用远程挂载的存储。

分片策略

硬件优化,可以参考的一些原则:

  1. 控制每个分片占用的磁盘容量不超过ES的最大JVM堆空间设置(一般不超过32G,因为大部分用作服务器的物理机内存为32G,且内存中的数据需要刷到磁盘上,参考下文的JVM设置原则)。因此,如果索引的总量在500G左右,那分片大小在16个左右即可,最好同时考虑原则2。
  2. 考虑node数量,一般一个节点有时候就是一台物理机,如果分片数量太多,大大超过节点数,可能导致一个节点上存在多个分片,一旦该节点故障,即使保持了1个以上的副本,同样有可能导致数据丢失,集群无法恢复。一般设置分片数不大于节点数的3倍。
  3. 主分片、副本和节点最大数之间数量,可参考公式节点数<=主分片数*(副本数+1)

推迟分片分配:对于节点瞬时中断的问题,默认情况集群会等待一分钟看节点是否重新加入,如果该节点在此期间重新加入,重新加入的节点会保持其现有的分片数据,不会触发新的分片重新分配,可以减少ES在自动再平衡可用分片时所带来的极大开销。可以修改参数delayed_timeout,可以延长再均衡时间,可以全局设置也可以在索引级别修改(PUT):

/_all/_settings
{
    "settings": {
        "index.unassigned.node_left.delayed_timeout": "5m"
    }
}

路由选择

shard = hash(routing) % number_of_primary_shards,其中routing默认是文档的id,也可以采用自定义值如用户id。通过带routing查询,可以直接根据routing信息定位到某个分片查询,不需要查询所有的分片,经过协调节点排序。而不带routing查询,首先将请求达到协调节点上,由协调节点将请求分发到每个分片上,然后,协调节点收集每个分片的查询结果,将查询结果进行排序,返回给用户。

写入速度优化

针对搜索性能要求不高,但对写入要求比较高的场景,可以选择适当的写优化策略:

  • 加大Translog Flush,目的是降低Iops、Writeblock。Flush的目的是把文件缓存系统中的段持久化到硬盘,当Translog的数据量达到512M或30分钟时,会触发一次Flush。增大该参数意味着需要为操作系统的文件缓存系统留下足够的空间。
  • 增加Index Refresh间隔,目的是减少Segment Merge次数。默认1s写,如果对数据实时性要求不高,可以将Refresh周期延长,从而减少段刷新次数,但意味着需要消耗更多的堆内存。
  • 调整Bulk线程池和队列(批量处理的大小)。ES提供Bulk API支持批量操作。
  • 优化节点间的任务分布。
  • 优化Lucene层的索引建立,目的是降低CPU和IO。

内存设置

ES堆内存分配需要满足以下两个原则:

  • 不要超过物理内存的50%;Lucene的设计目的是把底层OS里的数据缓存到内存中。Lucene的段是分别存储到单个文件中的,这些文件不会变化,所以利于缓存,同时操作系统会把这些段文件缓存起来,以便更快地访问。如果堆内存设置过大,Lucene可用内存会减少,将严重影响降低Lucene的全文本查询性能。
  • 单个节点的堆内存大小最好不要超过32G(对于64G的机器来说)。

重要配置

参数名 参数值 说明
cluster.name elasticsearch 配置ES的集群名称,默认是ES
node.name node-1 集群中节点名称,同一集群中要唯一
node.master true 指定该节点是否有资格被选举为master,默认true
node.data true 指定该节点是否存储索引数据,默认true,增删改查都是在数据节点完成的
index.number_of_shards 1 主分片数量,默认1
index.number_of_replicas 1 索引副本个数,默认1
transport.tcp.compress true 节点间传输数据时是否压缩,默认false
discovery.zen.minimum_master_nodes 1 设置在选举master时需要参与的最少候选节点数,默认为1。如果使用默认值,在网络不稳定时可能出现脑裂,合理的数值是 候选主节点数 / 2 + 1
discovery.zen.ping.timeout 3s 在集群中自动发现其他节点ping连接的超时时间,默认3s。如果网络环境较差,需要设置大一些,防止因误判节点存活状态而导致分片转移

面试题

为什么用ES

利于全文索引进行加快模糊查询速度。

master选举流程

  1. ES的master选举由ZenDiscovery模块负责,包含Ping(节点间通过该RPC发现彼此)和Unicast(单播模块包含一个主机列表以及控制哪些节点需要Ping通)这两部分。
  2. 对所有可以成为master的节点根据nodeId字典排序,每次选举每个节点都把自己知道的所有节点排一次序,选出第一个节点,暂时认为它是master节点。
  3. 如果对某个节点的投票数超过集群中节点数的一半,且该节点也选择了自己,则该节点成为master,否则重复上述过程。

master节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理。

脑裂问题

脑裂成因:

  • 网络问题。
  • 节点负载。master节点如果还是data节点,访问量较大时会导致ES停止响应造成大面积延迟,使得其他节点得不到响应而认为master挂了,重新进行选举。
  • 内存回收。data节点上的ES进程占用内存较大,触发GC,使ES失去响应。

脑裂解决方案:

  • 减少网络延迟的误判。
  • minimum_master_nodes配置。
  • 角色分离。master和data分离,即主节点配置为node.master:true, node.data:false,data节点配置为node.master:false, node.data:true

ES 8.x

基础功能

索引模板

  • 创建模板

    PUT _template/mytemplate
    {
        "index_patterns": [ // 模板的应用规则
            "my*"
        ],
        "settings": {
            "index": {
                "number_of_shards": "1"
            }
        },
        "mappings": {
            "properties": {
            	"now": {
                    "type": "date",
                    "format": "yyyy/MM/dd"
                }
            }
        }
    }
    
  • 查看模板

    GET _template/mytemplate
    
  • 使用模板

    PUT my_test_template
    
  • 删除模板

    DELETE _template/mytemplate
    

文档评分机制

TF(Term Frequency,词频):搜索文本的各个词条(term)在查询文本中出现了多少次,出现次数越多,就越相关。

IDF(Inverse Document Frequency,逆文本频率):搜索文本中的各个词条(term)在整个索引的所有文档中出现了多少次,出现的次数越多,说明越不重要。

评分公式:boost * tf * idf

查看评分计算公式:GET shopping/_search?explain=true

查询提权:

{
    "query": {
        "bool": {
            "should": [
                {
                    "match": {
                        "title": {
                            "query": "Spark",
                            "boost": 2, // 提权
                        }
                    }
                },
                {
                     "match": {
                        "title": {
                            "query": "Hadoop",
                            "boost": 1,
                        }
                    }
                }
            ]
        }
    }
}

EQL

EQL(Event Query Language,事件查询语言),是一种基于事件的时间序列数据(如日志、指标等)查询语言。

优点:

  • 表达事件之间的关系。使可以匹配不同事件类别和时间跨度的一系列事件。
  • EQL类似于其他查询语言如SQL。
  • 可用于设计安全用例。

要运用EQL搜索,搜索到的数据流或索引必须包含时间戳和事件类别字段。默认情况下,EQL使用ES通用模式(ECS)中的@timestamp(时间戳)和event.category字段(事件分类)。

SQL

从ES6.3开始支持SQL查询,SQL会转换为Query DSL。SQL还可以和DSL混合使用。

1、SQL翻译为DSL

POST _sql/translate
{
	"query": """
		show tables
	"""
}

2、查询所有索引

GET _sql?format=txt
{
	"query": """
		show tables
	"""
}

3、查询指定索引

GET _sql?format=txt
{
	"query": """
		show tables like 'myindex'
	"""
}

4、模糊查询索引

GET _sql?format=txt
{
	"query": """
		show tables like 'my%'
	"""
}

5、查看索引结构

GET _sql?format=txt
{
	"query": """
		describe "myindex"
	"""
}

6、类型转换

GET _sql?format=txt
{
	"query": """
		SELECT '123'::long as num
	"""
}
posted @ 2023-10-28 10:32  sjmuvx  阅读(17)  评论(0编辑  收藏  举报