原理

基于lucence 集群 –> 节点 –> 索引 –> 分片

集群

节点发现机制:ZenDiscovery

节点

集群中有一个主节点,单个节点可以同时为候选主节点和数据节点,通过配置指定。 主节点:维护集群节点状态; 候选主节点:可参与选举,成为主节点; 数据节点:执行实际的数据查询和存储操作; 协调节点:client直连的节点,可以是集群中的任何节点,负责将请求转发到实际的主分片节点上;

分片

分片数提前规划,不可变更,单分片 20-50G 主从分片不能在同一个实例节点中;

索引

ES默认会把所有字段都建成索引,字符串字段会默认被分词,作为全文检索,分词器使用的默认分词器,中文会一个字一个字的分词,英文会根据空格分成一个一个单词,之后会全部转换成小写存储,分词后就会形成一个一个的token(词条),后续ES的查询基本上就是根据每个字段的tokens查询的,token这个东西就很关键了,不同的分词器分成的token不一样,一个字段会分词成多个token,存放的是一个token数组,所以就会影响查询结果。
索引不可变更。

mapping

对应数据库表结构 properties:具体字段,以及相关类型,不同类型的查询支持情况不尽相同,text类型支持分词,long,keyword类型支持精确匹配。

multi field

对同一个字段构建不同的索引,以应对不同的查询需要,不会改变_source的存储数据,额外创建索引,增加存储开销; 新增一个filed时,会对后续的indice数据有效,已有数据不会重建索引;

倒排索引

建立 关键字 –> 文档的 映射关系,即为倒排索引! 关键字构建词典,词典中存放关键字,关键字指向具体的原始文档列表;

深度分页

索引分页过大时,性能损耗严重,可通过max_result_window控制最大条目数,默认为10000. 可使用scroll游标处理

常用操作

索引信息查看

’‘’ curl -v “http://hostname:7000/_cat/indices?pretty=true” ’‘’

索引删除

curl -v -XDELETE "http://host:9201/index-name"

数据检索

curl -X POST -H "Content-type:application/json" "http://hostname:7000/indexName/_search?pretty=true" -d  '{"size": 5, 
"sort" : [{"dt" : {"order" : "asc"}}], "query": {"range":{"dt":{"gte":1627747200000}}}
      }'

数据删除

curl -X POST -H "Content-type:application/json" "http://hostname:7000/indexName/_delete_by_query" -d  '
{"query": {"range":{"dt":{"lt":1630425600000,"gte":1627747200000}}}}'

分词器

分词器是针对text字段进行文本分析的工具。
官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-analyzers.html

写入分词器

在settings中定义分词器,并在mapping中使用: “analyzer” : “analyzer_name”;

查询分词器

  • 1,在settings中定义分词器,并在mapping中使用: “search_analyzer” : “analyzer_name”;
  • 2,在查询接口中指定使用特定分词器;
    注意:默认分词之后,为or的关系,只要有一个存在也会返回!

更新分词器配置

官方说明:https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html

1,停止索引

POST /*-2021.05/_close?wait_for_active_shards=0

2,更新settings

PUT /*-2021.05/_settings
{
"settings": {
"analysis": {
"analyzer": {
"log_analyzer": {
"type": "custom",
"tokenizer": "ik_max_word"
},
"search_analyzer": {
"type": "custom",
"tokenizer": "ik_max_word",
"filter": [
"stemmer",
"remove_letters"
]}},
"filter": {
"remove_letters": {
"type": "keep_types",
"mode": "exclude",
"types": [
"LETTER"
]}}}}}

3,重启索引

POST /*-2021.05/_open

字段分词器更新

官方说明:https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html

更新mapping

PUT /*-2021.05/_mapping

{
"properties": {
"message": {
"type" : "text",
"analyzer" : "log_analyzer",
"search_analyzer": "search_analyzer"
}}}

分词结果分析

官方说明:https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-analyze.html

数据类型

text类型:

  • 会进行分词,分词后建立索引。
  • 支持模糊查询,支持准确查询。
  • 不支持聚合查询
  • 最大支持的字符长度无限制,适合大字段存储;

keyword类型:

  • 不分词,直接建立索引。【依据此特点,可以使用keyword类型+wildcardQuery(通配查询)实现类似sql的like查询(模糊搜索)】
  • 支持模糊查询,支持准确查询。
  • 支持聚合查询。
  • 最大支持的长度为——32766个UTF-8类型的字符,可以通过设置ignore_above指定自持字符长度,超过给定长度后的数据将不被索引,无法通过term精确匹配检索返回结果。

查询类型

term

不会分词,需要完全匹配才可以。 keyword 字段不分词,可直接查询;text类型分词后也可以被term查询。

match

match 分词,text 也分词,只要 match 的分词结果和 text 的分词结果有相同就匹配,这里可以通过operator(AND,OR)控制分词后各token匹配关系,默认为OR,即分词后,其中部分token匹配即可。

match_phrase

match_phrase 是分词的,text 也是分词的。match_phrase 的分词结果必须再 text 字段分词中都包含,而且顺序必须相同,默认必须都是连续的(可以通过slop控制间隔)。

注意:

实际不要使用*开头做通配,严重影响性能,并可能导致系统崩溃。

检索不到分析

当前实际使用的分词器为:log_analyzer(写入使用),search_analyzer(查询使用)。

通过分析不同分词器对给定文本的分词结果,可以快速定位当前检索不到数据的原因。(通常为数据写入的分析器,跟查询分词器的分词结果不一致)

POST qiyelog.qiye-domainservice-email20-2021.07/_analyze
{
"text" : """openauthcode""",
"analyzer": "search_analyzer"
}
失败场景 失败原因 处理方式
xxxx:yyyy格式的数据,查询”xxxx“无法获取 默认分词器不支持”:“的分词 调整默认分词器,引入ik_max_word;备注:实际通过调整检索类型(match_prefix)也可支持
使用邮箱域名查询日志,无法正常检索 写入分词器包含LETTER类型分词,必须完全匹配,包括域名后缀 查询分词器补充对LETTER类型的分词检索过滤
使用带e,ing等后缀的关键字,无法正常检索 查询分词器包含了stemmer过滤器,将特殊后缀进行了截断 查询分析器删除对的stemmer过滤

索引迁移

对现有索引新增/修改字段时,无法对历史数据生效,需要进行索引迁移,同时在迁移过程中需要保证平滑切换,对业务无感知。

前提

索引需要有别名,业务使用别名操作。

步骤

1,已有索引查看

curl -X GET -H "Content-type:application/json" "http://${host}:7000/archive_log_2021/?pretty=true"

其中这里dynamic字段这是为false,不自动新增字段(避免推断类型不准确),但是可以通过接口显式修改。

2,创建新索引

具体属性以及mapping字段,可直接复制已有索引进行修改。

 curl -X PUT -H "Content-type:application/json" "http://${host}:7000/archive_log_2021_new" -d '{
"aliases" : {
"archive_log_2021" : {
}
},
"mappings" : {
"dynamic" : "false",
"_meta" : {
"init_mark" : true
},
"properties" : {
"content" : {
"type" : "text"
},
"did" : {
"type" : "long"
},
...
}
},
"settings" : {
"index" : {
"number_of_shards" : "3",
"number_of_replicas" : "2"
}
}
}'

3,别名更新

对新建的索引添加别名,并且设置为可读,同时需要删除原有索引的别名。

_aliases该接口可实现多个操作的原子性(同一别名下保证只有一个索引写入)。 对于同一个别名,每次只能有一个索引可写。该操作之后,数据会写入新的索引中。

curl -X POST -H "Content-type:application/json" "http://${host}:7000/_aliases" -d '{
"actions": [
{
"add": {
"index": "archive_log_2021_new",
"alias": "archive_log_2021",
"is_write_index": true
}
},
{
"remove": {
"index": "archive_log_2021_old",
"alias": "archive_log_2021"
}
}
]
}'

4,索引迁移

此时将老的索引中的数据,重做到新索引中。
根据索引数据量不同,这个过程耗时会比较久,可以通过reindex的其他参数进行更精细的控制,此处可采用异步方式,默认冲突不插入。

该操作执行完毕后,所有数据可以在新的索引中正常检索到。

curl -X POST -H "Content-type:application/json" "http://${host}:7000/_reindex?wait_for_completion=false" -d '{
  "source": {
    "index": "archive_log_2021_old"
  },
  "dest": {
    "index": "archive_log_2021_new"
  }
}'

总结

通过上述操作,可以实现平滑的索引更新。 注意:如果字段有更新操作,那么该方案仍然存在漏洞,即更新数据写入新索引后,历史数据再重做过来,实际会出现两条记录。