Elasticsearch笔记4 基础入门

执行分布式检索

一个查询操作,在ES分布式环境中分为两步:查询与合并

查询阶段

ES集群向所有分片传递查询语句。
分片接收到请求后,执行搜索并建立一个长度为top-n的优先队列,存储结果。
top-n 的大小取决于分页参数 top-n=from+size

步骤

  • 客户端发送查询请求到节点A,节点A创建分片数*(from+size)长度的优先队列(空的)。
  • 此处ES对top-n的长度最大是10000(可配置)
  • 节点A将查询请求并行发送到所有主分片和副分片。
  • 各分片本地执行检索,并将结果存到各自本地的top-n优先队列
  • 各分片将优先队列返回给A节点(仅id和排名(_score))
  • 节点A合并各节点返回的内容,产生一个全局排序的结果存到自身优先队列中。

取回阶段

步骤

  • 协调节点判断哪些需要返回,哪些需要丢弃。form的丢弃,size的返回。
  • 协调节点根据个分片返回的id,向个分片发起get请求。
  • 各分片接收到get请求,检索完整文档,返回给协调节点。
  • 协调节点判断所有文档均返回后,向客户端返回结果。

深分页

协调节点创建的优先队列,长度是有限制的。即便长度无限,处理他们也是非常耗时的。所以不建议提供过深的分页功能。

搜索选项

有几个影响性能的参数

  • preference(偏好)
    控制分片是否参与检索
    • _primary
    • _primary_first
    • _local
    • _only_node:xyz
    • _prefer_node:xyz
    • _shards:2,3

Bouncing Results

当使用类似入库时间这样的字段检索时,由于主分片和副本分片本身存在入库时间差异,所以导致多次查询结果集变化的情况。可以使用preference处理。

超时问题

处理过程可能超时

路由

在取回阶段,协调节点根据id获取文档,此时会走获取流程,会根据id计算所属分片,快速提取。

搜索类型

缺省类型是query_then_fetch(包含预查阶段)

游标查询

ES为解决深分页的性能问题,提供了游标查询机制。

游标分解

  • 初始化阶段:按照检索条件进行数据检索,并将结果缓存起来。
  • 遍历阶段:从缓存中提取数据
    特点:初始化阶段后任何操作不会对结果产生影响,因为读的是缓存数据

索引管理

创建一个索引

语法格式

PUT /my_index
{
    "settings": { ... any settings ... },
    "mappings": {
        "type_one": { ... any mappings ... },
        "type_two": { ... any mappings ... },
        ...
    }
}

禁止自动创建索引

config/elasticsearch.yml

action.auto_create_index: false

删除一个索引

语法格式

DELETE /my_index
DELETE /index_one,index_two
DELETE /index_*
DELETE /_all
DELETE /*

避免误删除,必须指定删除,拒绝模糊范围删除,避免误删除

elasticsearch.yml
action.destructive_requires_name: true

索引设置

ES提倡使用默认设置,不要瞎改

  • number_of_shards 主分片数,默认5。创建后不可变更。
  • number_of_replicas 副本分片数,默认1。创建后可变更。

配置分析器

ES支持配置已有的分析器,或者为你的索引设置创建自定义分析器。

默认全文分析器

standard分析器是用于全文检索的默认分析器,适用于大部分西方语言。特点如下:

  • standard分词器: 通过单词边界分割输入文本。
  • standard语汇单元过滤器: 语汇单元过滤器,目的是整理分词器触发的语汇单元(目前什么也没做)
  • lowercase语汇单元过滤器: 转换所有的语汇单元为小写
  • stop 语汇单元过滤器: 删除停用词-对搜索相关性影响不大的常用词,如a,the,and等等

自定义分析器

可以通过在一个适合你的特定数据的设置之中组合字符过滤器、分词器、词汇单元过滤器来创建自定义的分析器。
一个分析器就是组合了三种函数的一个包装器, 三种函数按照顺序被执行:

  • 字符过滤器
    • 数量 0 ~ N 个
    • 用于整理尚未被分词的字符串,调整内容。比如去除HTML语法,转码等
  • 分词器
    • 用于将字符串分解成单个词条或者词汇单元。
    • 数量 1 唯一
  • 词单元过滤器
    • 经过分词,作为结果的 词单元流 会按照指定的顺序通过指定的词单元过滤器 。
    • 词单元过滤器可以修改、添加或者移除词单元。
    • 数量 0 ~ N 个

创建自定义分析器语法

PUT /my_index
{
    "settings": {
        "analysis": {
            "char_filter": { ... custom character filters ... },
            "tokenizer":   { ...    custom tokenizers     ... },
            "filter":      { ...   custom token filters   ... },
            "analyzer":    { ...    custom analyzers      ... }
        }
    }
}

实例

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

类型和映射(大概意思)

在ES中同一索引下的不同类型通过_type进行区分和使用。但是在Lucene中并没有类型的概念。Lucene中将同一索引下全部类型的映射信息进行无差别的同级存储。
如果不同类型下相同映射采用不同的分析器,ES会报错。
所以新版本中的ES不再支持多类型模式,而是单类型模式的原因

根对象

动态映射

当ES遇到文档中以前未遇到过的字段,会动态的进行映射。该功能可通过dynamic进行配置。

  • true 动态添加新的字段映射
  • false 忽略新的字段
  • strict 遇到新的字段报错
PUT /my_index
{
    "mappings": {
        "my_type": {
            "dynamic":      "strict", 
            "properties": {
                "title":  { "type": "string"},
                "stash":  {
                    "type":     "object",
                    "dynamic":  true 
                }
            }
        }
    }
}

自定义动态映射

日期检测

当新字段内容为‘2020-10-10’时,动态映射成为date类型,此时再次传递字段内容为‘1234’时,将报错。可以通过关闭日期检测避免问题。
关闭日期检测

PUT /my_index
{
    "mappings": {
        "my_type": {
            "date_detection": false
        }
    }
}

动态模板

通过指定动态模板,实现有规则的进行动态映射。比如通过字段名后缀等等。

缺省映射

通过缺省映射为所有映射进行统一配置,如有不同可在具体映射内更改配置。

PUT /my_index
{
    "mappings": {
        "_default_": {
            "_all": { "enabled":  false }
        },
        "blog": {
            "_all": { "enabled":  true  }
        }
    }
}

重新索引你的数据

重新索引:用新的设置创建新的索引并把文档从旧的索引复制到新的索引。

GET /old_index/_search?scroll=1m
{
    "query": {
        "range": {
            "date": {
                "gte":  "2014-01-01",
                "lt":   "2014-02-01"
            }
        }
    },
    "sort": ["_doc"],
    "size":  1000
}

索引别名和零停机

别名

别名支持一对多关系
创建别名

PUT /my_index_v1/_alias/my_index

删除别名
查看别名指向索引

GET /*/_alias/my_index

查看索引关联别名

GET /my_index_v1/_alias/*

零停机

在不停机的情况下,删除原始别名,建立新别名

POST /_aliases
{
    "actions": [
        { "remove": { "index": "my_index_v1", "alias": "my_index" }},
        { "add":    { "index": "my_index_v2", "alias": "my_index" }}
    ]
}

该操作属于原子操作

分片内部原理

段和提交点的概念

一个索引关联多个段索引(每个段都是倒排索引),并由提交点文件进行记录关联关系。

文档的存储流程A

  • 新文档被收集到内存索引缓存
  • 定时,缓存到被提交
    • 一个新的段-》一个追加的倒排索-》被写入磁盘
    • 一个新的包含新段名字的 提交点 被写入磁盘
    • 磁盘进行同步:所有在文件系统缓存中等待的写入都刷新到磁盘,以确保它们被写入物理文件。
    • 文档保存结束,可以被检索(可检索延迟高,后续流程B将解决)

不变性

倒排索引被写入磁盘后是 不可改变 的,不变性的价值

  • 不需要锁。因为没有变更,所以不需要锁来预防并发改的问题。
  • 方便缓存。因为没有变更,只要缓存有空间,便可一直缓存。不需要考虑缓存一致性问题。
  • 其他缓存(Filter)。在检索过程中产生的缓存,可以重复使用。
  • 数据压缩。减少磁盘I/O,节省磁盘空间,节省缓存空间。

动态更新索引

既然索引是不可改变的,又如何应对更新、删除操作?答案是用更多的索引。
当删除一个索引时,其实是状态删除,检索过程中进行过滤。
当更新一个索引时,其实是状态删除和新增索引。检索过程中过滤掉删除的,保留新增的,

近实时索引

文档的存储流程B

  • 新文档被收集(组成一个倒排索引段)到内存索引缓存
  • 倒排索引段保存到系统文件缓存(此时已支持被检索,延迟低)
  • 定时,缓存到被提交
    • 一个新的段-》一个追加的倒排索-》被写入磁盘
    • 一个新的包含新段名字的 提交点 被写入磁盘
    • 磁盘进行同步:所有在文件系统缓存中等待的写入都刷新到磁盘,以确保它们被写入物理文件。
    • 文档保存结束,可以被检索(可检索延迟高,后续流程B将解决)

refresh API

ES对一个新段的操作是需要关联的,不能上来直接用。系统采用定时执行的方式实现自动关联。
手动触发

POST /_refresh 
POST /blogs/_refresh 

设置定时频率

PUT /my_logs
{
  "settings": {
    "refresh_interval": "30s" 
  }
}

设置定时开与关
在做大索引迁移过程中,建议先关闭,迁移结束后再开启。

PUT /my_logs/_settings
{ "refresh_interval": -1 }

PUT /my_logs/_settings
{ "refresh_interval": "1s" }

如果refresh_interval=1 表示1毫秒,每毫秒做一次关联,会把ES搞瘫痪

持久化变更

translog 事务日志

ES通过translog机制确保数据安全性

translog记录点

  • 新文档被收集(组成一个倒排索引段)到内存索引缓存
  • 记录本次操作到translog中。
  • 倒排索引段保存到系统文件缓存(此时已支持被检索,延迟低)
  • 定时,缓存到被提交(全量提交)
    • 一个新的段-》一个追加的倒排索-》被写入磁盘
    • 一个新的包含新段名字的 提交点 被写入磁盘
    • 磁盘进行同步:所有在文件系统缓存中等待的写入都刷新到磁盘,以确保它们被写入物理文件。
    • 文档保存结束,可以被检索(可检索延迟高,后续流程B将解决)

translog处理过程-运行中

每隔一段时间或者translog过大。索引将被刷新(flush);一个新的translog被创建,并且一个全量提交被执行。

  • 所有在内存缓冲区的文档都被写入一个新的段。
  • 缓冲区被清空
  • 一个提交点被写入磁盘
  • 文件系统缓存通过fsync被刷新(flush)
  • 老的translog被删除

translog处理过程-启动

当ES关闭时,translog中还有剩余数据。在ES启动过程中需要被刷新(或者说启动时需要一个新的translog,才把旧translog刷新,而不是继续使用)。

  • 使用提交点记录关联索引与段的关联关系
    • 此时不包含未fulsh段。
    • 此时内存缓存区是空的。
    • 此时translog是有旧数据的。
  • 重放translog
    重放数据到内存缓存区
    建立新段
  • 全量提交
    • 内存缓存区清空
    • translog清空

flush API

通常情况下,自动刷新就足够了。
手动提交

POST /blogs/_flush 
POST /_flush?wait_for_ongoing 

启动与关闭

PUT /my_index/_settings
{
    "index.translog.durability": "async",
    "index.translog.sync_interval": "5s"
}

durability=async表示开启
sync_interval=5s表示间隔5秒同步到磁盘

translog有多安全

translog先记录在内存,每间隔5S同步到磁盘。

段合并

由于自动刷新流程每秒会创建一个新的段 ,这样会导致短时间内的段数量暴增。而段数目太多会带来较大的麻烦。 每一个段都会消耗文件句柄、内存和cpu运行周期。更重要的是,每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢。
ES在后台自动进行段合并操作,在后台将多个相似的小段合并到大段。并且不会影响ES操作。

合并流程

  • 新的段被刷新(flush)到了磁盘。 ** 写入一个包含新段且排除旧的和较小的段的新提交点。
  • 新的段被打开用来搜索。
  • 老的段被删除。

optimize API

API大可看做是 强制合并 API。它会将一个分片强制合并到 max_num_segments 参数指定大小的段数目。 这样做的意图是减少段的数量(通常减少到一个),来提升搜索性能。

POST /logstash-2014-10/_optimize?max_num_segments=1

文章中部分内容来源于:
Elasticsearch: The Definitive Guide by Clinton Gormley and Zachary Tong (O’Reilly). Copyright 2015 Elasticsearch BV, 978-1-449-35854-9。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值